线性表学习之顺序表示
原创博文,转载请注明出处
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构,两者属于不同层面的概念,因此不要将其混淆。接下来我们学习:
第一部分:线性表的顺序表示
顺序表最主要的特点是可以进行随机存取,即通过首地址和元素序号可以在O(1)的时间内找到指定的元素。顺序表的存储密度高,每个节点只存储数据元素,但是插入和删除需要移动大量元素。
插入、删除、查找的时间复杂度为O(n)
综合应用 :
1、设计一个高效算法,将顺序表的所有元素逆置,要求算法的空间复杂度为O(1).
算法思想:扫描顺序表L的前半部分元素,对于元素L.data[i](0<=i<=L.length/2),将其余后半部分对应元素L.data[L.length-i-1]进行交换。
1 void Reverse(Sqlist &L){ 2 Elemtype temp; 3 for(i=0;i<L.length/2;i++){ 4 temp=L.data[i]; 5 L.data[i]=L.data[L.length-i-1]; 6 L.data[L.length-i-1]=temp; 7 } 8 }
2、长度为n的顺序表L,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中所有值为x的数据元素。
解法一:用K记录顺序表L中不等于x的元素个数(即需要保存的元素个数),边扫描L边统计k,并将不等于x的元素向前放置k位置上,最后修改L的长度。
1 void del_x(Sqlist &L,Elemtype x){ 2 int k=0; 3 for(i=0;i<L.length;i++) 4 if(L.data[i]!=x){ 5 L.data[k]=L.data[i]; 6 k++; 7 } 8 L.length=k; 9 }
解法二: 用K记录顺序表中等于x的元素个数,边扫描L边统计k,并将不等于x的元素前移k个位置,最后修改L的长度。
3、从有序 顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素,如果s或t不合理或者顺序表为空则显示出错信息并推出运行.
算法思想:先寻找值大于等于s的第一个元素(第一个删除的元素),然后寻找值>t的第一个元素(最后一个删除的元素的下一个元素),要将这段元素删除,则只需要直接将后面的元素前移即可。实现如下:
1 bool Del_s_t(sqList &L,ElemType s,ElemType t){ 2 int i,j; 3 if(s<=t)||L.length==0 4 return false; 5 for(i=0;i<L.length&&L.data[i]<s;i++); 6 if(i>L.length) 7 return false; 8 for(j=0;j<L.length&&L.data[j]<=t;j++); 9 for(;j<L.length;i++,j++) 10 L.data[i]=L.data[j]; 11 L.length=i; 12 return true; 13 }
那如果不是有序的怎么办呢?很简单,我们从前向后扫描顺序表L,用k记录下元素值在s到t之间元素的个数。对于当前扫描的元素,若其值不在s到t之间,则前移k个位置,替代原值,否则执行k++。由于这样每个不在s到t之间的元素仅移动一次,所以算法效率高。
4、从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。
算法思想:由于是有序表,所以值相同的元素一定在连续的位置上,判断后面的元素是否与前面的非重复有序表最后一个元素相同,如果相同则继续向后判断,如果不同则插入到前面的非重复有序表的最后 ,直到判断整个表尾为止。实现如下:
1 bool Delete_Same(SeqList L){ 2 if (L.length==0) 3 return false; 4 int i,j; 5 for(i=0,j=1;j<L.length;j++) 6 if(L.data[i]<L.data[j]) 7 L.data[++i]=L,data[j] 8 L.length=i+1; 9 }
5、将两个有序顺序表合并成一个新的有序顺序表,并有函数返回结果顺序表
算法思想:首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。
1 bool Merge(SeqList A, SeqList B ,SeqList &C){ 2 if(A.length+B.length>C.maxSize) 3 return false; 4 int i=0,j=0,k=0; 5 while(i<A.length&&j<B.length){ 6 if(A.data[i]<=B.data[j]) 7 C.data[k++]=A.data[i]; 8 else 9 C.data[k++]=B.data[j]; 10 } 11 while(i<A.length) 12 C.data[k++]=A.data[i++]; 13 while(j<B.length) 14 C.data[k++]=B.data[j++]; 15 C.length=k; 16 return true; 17 }
6、线性表(a1,a2,a3,...,an)中元素递增有序且按顺序存储在计算机内。要求设计一算法完成用最少时间在表中查找数值为x的元素,若找到将其与后继元素位置交换,若找不到将其插入表中并使表中元素仍递增有序。
算法思想:顺序存储的线性表递增有序,可以顺序查找,也可以折半查找。题目要求用最少时间,应用折半查找法。
1 void Search(ElemType A[],ElemType x){ 2 int low=0,high=n-1; 3 while(low<high){ 4 mid=(low+high)/2; 5 if(A[mid]==x)break; 6 else if (A[mid]<x)low=mid+1; 7 else high=mid-1; 8 } 9 if(A[mid]==x&&mid!=n-1){ 10 t=A[mid];A[mid]=A[mid+1];A[mid+1]=t; 11 } 12 if(low>high){ 13 for(i=n-1;i>high;i--)A[i+1]=A[i]; 14 A[i+1]=x; 15 } 16 }
7、设将n(n>1)个整数存放到一堆数组R中。试设计一个在时间和空间两方面都尽可能高校的算法。将R中保存的序列循环左移p(0<p<n)个位置,即将R中的数据由(X0,X1,...,Xn-1)变换为(Xp,Xp+1,...,Xn-1,X0,X1,...,Xp-1)。
算法思想:可以将这个问题看作是吧数组ab转换成ba(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先将a逆置得到a-1b,再将b逆置得到a-1b-1,最后将整个逆置得到
(a-1b-1)-1=ba。设Reverse函数执行将数组元素逆置的操作。两个参数分别表示数组中待转换元素的始末位置。实现如下:
1 void Reverse(int R[ ], int from, int to){ 2 int i,temp; 3 for(i=0;i<(to-from+1)/2;i++){ 4 temp=R[from+i];R[from+i]=R[to-i];R[to-i]=temp; 5 } 6 } 7 8 void main(int R[ ], int n,int p){ 9 Reverse(R,0,p); 10 Reverse(R,p,n-1); 11 Reverse(0,n-1); 12 }
上述算法中,三个Reverse函数的时间复杂度分别为O(p/2),O((n-p)/2),O(n/2),故所设计的算法时间复杂度为O(n),空间复杂度为O(1)。
8、一个长度为L的升序序列S,处在第[L/2]个位置的数称为S的中位数。例如,若序列S1=(11,13,15,17,19),则S1 的中位数是15,两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若S2=(2,4,6,8,20),则S1和S2的中位数是11.现在有两个等长的升序序列A和B,设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列A和B的中位数。
算法思想:一开始我用了快速排序将两个序列进行合并排序(与第5题类似)。后来看了答案,才知道另一种更高效的算法。
分别求两个升序序列A、B的中位数,设为a和b,求序列A、B的中位数过程如下:
1. 若a=b,则a或b即为所求中位数,算法结束
2. 若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
2. 若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中,重复过程1,2,3,直到两个序列中均只含一个元素时为止,较小者即为所求的中位数。
实现如下: 算法的时间复杂度为O(log2n),空间复杂度为O(1).
1 int M_Search(int A[ ],int B[ ],int n){ 2 int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2; 3 while(s1!=d1&&s2!=d2){ 4 m1=(s1+d1)/2; 5 m2=(s2+d2)/2; 6 if(A[m1]=B[m2]) 7 return A[m1] 8 if(A[m1]<B[m2]){ 9 if ((s1+d1)/2==0){ 10 s1=m1; 11 d2=m2; 12 } 13 else{ 14 s1=m1+1; 15 d2=m2; 16 } 17 } 18 else{ 19 if((s1+d1)/2==0){ 20 d1=m1; 21 s2=m2; 22 } 23 else{ 24 d1=m1; 25 s2=m2+1; 26 } 27 } 28 } 29 return A[s1]<B[s2]?A[s1]:B[s2]; 30 }