第二篇:线性表的顺序表示典型题

//考研复习(2):线性表的顺序表示典型题

----------------------------------------------------------------------------------------------------------------
//---1.从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删除元素的值,
//空出的位置由最后一个元素填补,若顺序表为空则显示出错信息并推出运行

[算法思想]搜索整个顺序表,查找最小元素并记住其位置,搜索结束后用最后一个元
            素填补空缺的原最小元素的位置
[代    码]
bool  Del_Min(sqList  &L,ElemType &value){
	//删除顺序表L中最小元素结点,将最后一个元素补给删除元素所在位置
	//并通过引用型参数value返回其值
	//若删除成功,就返回true,否则返回false
	if(L.length==0)
		return false;         //若表空,中止操作返回
	value=L.data[0];
	
	int pos=0;                //假设0号元素最小
	for(int i=1;i<L.length;i++)   //循环,寻找最小元素,并记住其值和位置
		if(L.data[i]<value){
			value=L.data[i];
			pos=i;
		}
	L.data[pos]=L.data[L.length-1];	//空出的位置由最后一个元素填补
	L.length--;                     
	
	return true;                    //此时,value为最小值
}

----------------------------------------------------------------------------------------------------------------
//---2.设计一个高效算法,将顺序表L的所有元素逆置,要求算法的空间复杂度为0(1).

[算法思想]扫描顺序表L的前半部分,对于元素L.data[i](0≤i<L.length/2),
          将其与后半部分元素L.data[L.length-i-1]交换。
[代码]
void Reverse(sqList  &L){
	//将元素逆置,用引用型变量返回逆置后的顺序表
	
	ElemType  temp;                    //辅助变量
	for(int i=0;i<L.length/2;i++){
		temp=L.data[i];                //将L.data[i]与L.data[L.length-i-1]元素交换
		L.data[i]=L.data[L.length-i-1];
		L.data[L.length-i-1]=temp;
	}
}

----------------------------------------------------------------------------------------------------------------
//---3. 对长度为n的顺序表L,编写一个时间复杂度为0(n)、 空间复杂度为0(1)的
//算法,该算法删除线性表中所有值为x的数据元素。

方法1:
[算法思想]用K记录顺序表中L不等于x的元素个数,边扫描L边统计k,将不等于x的元素向前移动到k的位置上,最后修改L的长度
[代码]
void Del_X_1(sqList &L,ElemType X){
	//本算法实现将所有等于X的数据元素删除
	//利用引用型参数L来实现删除后的顺序表的返回
	int k=0;                           //记录不等于X的元素个数
	for(int i=0;i<L.length;i++)            
		if(L.data[i]!=X){
			L.data[k]=L.data[i];       //将不等于X的元素移动到k+1的位置
			k++;                       //不等于X的元素加1
		}
	L.length=k;                        //顺序表的长度为k,因为当最后一个不等于X的元素移动后,k还要加1
}

方法2:
[算法思想]用k记录顺序表L中等于X的元素个数,边扫描L边统计k,将不等于X的元素向前移动k个位置,最后修改L的长度
[代码]
void Del_X_2(sqList &L){
	//本算法实现将所有等于X的数据元素删除
	//利用引用型参数L来实现删除后的顺序表的返回
	
	int k=0,i=0;                 //k记录值等于X的元素个数
	while(i<L.length){
		if(L.data[i]==X){
			k++;                 //出现等于X的元素,k加1
		}
		else{
			L.data[i-k]=L.[i];   //出现不等于X的元素,该元素向前移k位
		}
		i++;
	}
	L.length=L.length-k;         //顺序表L的长度少了k
}

方法3:
[算法思想]用头尾两个指针,从两端向中间移动,凡遇到最左端值x的元素时候,将最右边非x的元素前移至x元素的位置处,
          直到两指针相遇,但是这种方法会改变原表中元素的相对位置

----------------------------------------------------------------------------------------------------------------
//---4. 从有序顺序表中删除其值在给定值s与t之间(要求s < t)的所有元素,如
//果s或t不合理或顺序表为空,则显示出错信息并退出运行。

[算法思想]先寻找值大于等于s的第一个元素(要删除的第一个元素),然后寻找大于t的第一个元素(要删除的元素的最后
          一个元素的下一个),将这段元素后面的元素前移来删除这段元素
[代码]
bool Del_s_t2(sqList &L,ElemType s,ElemType t){
	//删除有序表L中值在给定s与t之间的所有元素
	int i ,j;
	if(s>=t||L.length==0)
		return false;
	for(i=0;i<L.length&&L.data[i]<s;i++);  //寻找值大于等于s的第一个元素所在位置
	if(i>=L.length)                        //若一直扫描完都没找到,那么就中止程序
		return false;
	for(j=i;j<L.length&&L.data[j]<=t;j++); //寻找值大于t的第一个元素所在的位置,扫描完还没找到那就停在表尾部
	for(;j<L.length;i++,j++)               //将大于t的第一个元素以及后面的元素前移,填补被删除元素
		L.data[i]=L.data[j];
	
	L.length=i;        //表L的长度为i(因为当填补完成后,j在L.length-1,但是还会加1,所以对于i也为新表长度)
	return true;
	
}


----------------------------------------------------------------------------------------------------------------
//5.从顺序表中删除其值在给定值s与t之间(包含s和t,要求s<t)的所有元
//素,如果s或t不合理或顺序表为空,则显示出错信息并退出运行。

[算法思想]从前往后扫描顺序表L,用K记录下元素在s和t之间的元素个数,k初始为0,对于当前扫描的元素,若其值不在s和t
          之间则,往前移动k个位置,否则执行k++,由于这样每个在s到t之间的元素只需要移动一次,所以算法效率高
[代码]
bool Del_s_t(sqList &L,ElemType s,ElemType t){
	//删除顺序表L中s到t中的所有元素
	int i,k=0;
	if(s>=t||L.length==0)
		return false;                 //若s,t不合法或表为空则返回,程序终止
	for(i=0;i<L.length;i++){
		if(L.data[i]<s||L.data[i]>t)  
			L.data[i-k]=L.data[i];    //若元素不在s,t之间就往前移动k个元素
		else
			k++;                      //若元素在s到t之间就执行k++
	L.length=L.length-k;              //表的长度比删除前减少k
	return true;
	}
}

//备注:第5题虽然和第4题相比只差了1个词语,但是它的思想却和第3题完全一样,有三种解法


----------------------------------------------------------------------------------------------------------------
//---6.从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

[算法思想]类似直接插入排序的思想,初始时候将第一个元素看作一个非重复表,扫描其后的元素,与非重复表最后一个元素比,
          若相同,则往后扫描,若不同,则将此元素移动到非重复表的结尾,直至表尾
[代码]
bool Del_same(sqList &L){
	if(L.length==0)
		return false;
	int i,j;                       //i存储第一个不相同的元素,j扫描后面的元素
	for(i=0,j=1;j<L.length;j++)
		if(L.data[i]!=L.data[j])   //查找下一个与不相同表最后一个元素不相同的元素
			L.data[++i]=L.data[j]; //找到后前移到不重复表的最后
	L.length=i+1;                  //因为前面前移用的是L.data[++i],则长度为i+1
	return true;
}

----------------------------------------------------------------------------------------------------------------
//---7.将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。

[算法思想]首先,按照顺序不断取两个顺序表的表头较小的系欸DNA存入新的顺序表中,然后看哪个表还有剩余,将剩下的部分加
          到新表的后面
[代码]
bool Merge(sqList A,sqList B,sqList &C){
	//将两个有序表A与B合并为一个新的有序表C
	if(A.length+B.length>C.maxSize)  //若原两表长度大于要合并的表的最大长度,则返回
		return false;
	int i=0,j=0,k=0;
	while(i<A.length&&j<B.length)     //扫描两表,并比较,将最小的放入C中
		if(A.data[i]<B.length[j])
			C.data[k++]=A.data[i++];
		else
			C.data[k++]=B.data[j++];
	
    while(i<A.length)                 //若A还有剩下,则A剩下的元素放入C中,若B还有剩的,将B剩下的元素放入C中
       	C.data[k++]=A.data[i++]
    while(j<B.length)
 		C.data[k++]=B.data[j++]
	C.length=k;                      //设置C表的长度
	
	return true;
}

----------------------------------------------------------------------------------------------------------------
//---8.已知在一维数组A[m+n]中依次存放两个线性(a1,a2,a3,...,am)和(b1,b2,b3...bn).
//试编写一个函数,将数组中两个顺序表的位置互换,即将(b1, b2,...,bn)放在(a1,a2, a3,..,an)的前面。

[算法思想]首先将数组A[m+n]中的元素(a1,a2,a3,...,am,b1,b2,b3,...,bn)原地逆置,变成(bn,...,b2,b1,am,...,a2,a1),
然后分别将(bn,...,b2,b1)和(am,...,a2,a1)逆置,即可得到(b1,b2,...,bn,a1,a2,...,am)
[代码]
typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize){
	//将数组A中从left到right的元素逆置
	if(left>=right||right>=arraySize)
		return;
	int mid;
	mid=(left+right)/2;
	for(int i=0;i<mid-left;i++){
		DataType temp;
		temp=A[left+i];
		A[left+i]=A[right-i];
		A[right-i]=temp;
	}
}

void Exchange(DataType A[],int m,int n,int arraySize){
	//A[m+n]中0到m-1存放(a1,a2,...,am)从m到m+n-1存放(b1,b2,...,bn)
	Reverse(A,0,m+n-1,arraySize);
	Reverse(A,0,n-1,arraySize);
	Reverse(A,n,m+n-1,arraySize);
}


-----------------------------------------------------------------------------------------------------------------
//---9.线性表(a1,a2,a3,...,an)中的元素递增有序且按顺序存储于计算机内。要求设计一算法,
//完成用最少时间在表中查找数值为x的元素,若找到则将其与后继元素位置交换,若找不到则将其插入表中
//并使表中元素仍递增有序。

[算法思想]根据题目要求,本体采用折半查找
[代码]
void SearchExchangeInsert(ElemType A[],int n,ElemType x){
	int low=0,high=n-1,mid;     //开始的时候,low和high分别指向数组的最小下标和最大下标
	while(low<=high){          //这一步是查找x元素,若没找到,那么x就应该放到high+1的位置
		mid=(low+high)/2;
		if(A[mid]==x)
			break;
		else if(A[mid]<x)
			low=mid+1;
		else
			high=mid-1;
	}
	if(A[mid]==x&&mid!=n-1){ //若找到x并且x不在数组最后的位置,这时候才可以与后继元素交换
		ElemType temp;
		temp=A[mid];
		A[mid]=A[mid+1];
		A[mid+1]=temp;
		
	}
	if(low>high){               //若没找到x,就将x插进去
		int i;
		for(i=n-1;i>high;i--){
			A[i+1]=A[i];
		}
		A[i+1]=x;   //x要放在high+1的位置,因为上一步循环当i=high+1循环完成后i又减1了
	}
}

-----------------------------------------------------------------------------------------------------------------
//---10[2010统考真题]设将n (n>1)个整数存放到一维数组R中。设计一个在时间和空间两方面都尽可能高效的算法。
//将R中保存的序列循环左移p(0<p<n)个位置,即将R中的数据由(X0, X1,..., Xn-1 )变换为(Xp, Xp+1,..., Xn-1, X0,...,Xp-1).
//(1)给出算法的基本设计思想。
//(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
//(3)说明你所设计算法的时间复杂度和空间复杂度。

[算法思想]可以将这个问题看作把数组ab(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先把a逆置得到a^(-1)b,
          再将b逆置得到a^(-1)b^(-1),最后将a^(-1)b^(-1)整体逆置,得到(a^(-1)b^(-1))^(-1)=ba,设Reverse函数执行逆置操作
		  例如将abcdefgh向左循环p(p=3 )个单位的过程如下:
		  Reverse(0,p-1)得到cbadefgh;
		  Reverse(p,n-1)得到cbahgfed;
		  Reverse(0,n-1)得到defghabc;
		  注:Reverse中两个参数分别表示待逆置的元素始末位置。
[用c语言描述]
          void Reverse(int R,int from,int to){   //逆置函数
			  int  i,temp;
			  for(i=0;i<(to-from)/2;i++){
				  temp=R[from+i];
				  R[from+i]=R[to-i];
				  R[to-i]=temp;
			  }
		  }
		  void  Converse(int R[],int n,int p){
			  Reverse(R,0,p-1);
		      Reverse(R,p,n-1);
		      Reverse(R,0,n-1);
		  }
[时间与空间复杂度]
时间复杂度:三个逆置函数的时间复杂度分别为O(p/2),O((n-p)/2),O(n/2),故时间复杂度为O(n)
空间复杂度:申请了temp,所以为O(1)

-----------------------------------------------------------------------------------------------------------------
//---11.[2011统考真题]一个长度为L (L>=1)的升序序列S,处在第[L/2](向上取整)个位置的数称为S的中位数。
//例如,若序列S1=(11, 13, 15,17, 19),则S的中位数是15,两个序列的中位数是含它们所有元素的升序序列的中位数。
//例如,若S2= (2,4,6,8,20),则S1和S2的中位数是11。现在有两个等长升序序列A和B,试设计一个在时间和空间两方面都
//尽可能高效的算法,找出两个序列A和B的中位数,要求:
//(1)给出算法的基本设计思想。
//(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
//(3)说明你所设计算法的时间复杂度和空间复杂度。

[算法思想]算法的基本思想如下:
          求两个升序序列A、B的中位数设为a和b,求序列A、B的中位数过程如下
		  (1)、若a=b,则a或b即为所求中位数,算法结束。
		  (2)、若a<b,则舍弃序列A中较小的一半,同时舍弃B中较大一半,要求两次舍弃的大小一样
		  (3)、若a>b,则舍弃序列A中较大的一半,同时舍弃B中较小一半,要求两次舍弃的大小一样
		  在保留的两个升序当中重复以上三个过程,直到两个序列中只含有一个元素时为止,较小者即为所求
[用c语言描述]
           int M_Search(int A[],int B[],int n){
			   int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2;
			   //以上分别代表序列A和B的首位数、末位数和中位数
			   while(s1!=d1||s2!=d2){
				   m1=(s1+d1)/2;
				   m2=(s2+d2)/2;
				   if(A[m1]=B[m2])       //满足第一种情况
					   return A[m1];
				   if(A[m1]<B[m2]){      //满足第二种情况
					   if((s1+d2)%2==0){ //元素个数为奇数
						   s1=m1;        //舍弃A序列中点以前的部分,保留中点
						   d2=m2;        //舍弃B序列中点以后的部分,保留中点
					   }
					   else{             //元素个数为偶数
						   s1=m1+1;      //舍弃A序列中点以及中点以前的部分
						   d2=m2;        //舍弃B序列中点以后的部分,保留中点
					   }
				   }
				   else{                  //满足第三种情况
					   if((s2+d2)%2==0){  //元素个数为奇数
						   s2=m2;         //舍弃B序列中点以前的部分,保留中点
						   d1=m1;         //舍弃A序列中点以后的部分,保留中点
					   }
					   else{              //元素个数为偶数
						   s2=m2+1;       //舍弃B序列中点以及中点以前的部分
						   d1=m1;         //舍弃A序列中点以后的部分,保留中点
					   }
				   }
			   }
			   
			   return A[s1]<B[s2]?A[s1]:B[s2];  //组后各剩一个元素中较小的是中位数
			   
		   }
[时间与空间复杂度]
时间复杂度:O(log2n)
空间复杂度:O(1)

-----------------------------------------------------------------------------------------------------------------
//---12. [2013统考真题]已知一个整数序列A = (a0,a1,...,an-1), 其中θ≤ai<n(0≤i<n)。若存在ap1=ap2= ... =apm=x 且m>n/2
//(0≤pk<n,1≤k≤m),则称x为A的主元素。例如A -(0,5,5,3,5,7,5,5),则5为主元素;又如A=(0,5,5,3.5,1,5,7),则A中没有主元素。
//假设A中的n个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;
//否则输出-1.要求:
//(1)给出算法的基本设计思想。
//(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。
//(3)说明你所设计算法的时间复杂度和空间复杂度。

[算法思想]算法的策略是从前往后扫描数组元素,标记出一个可能成为住院时的元素Num。然后重新计数,确认Num是否是主元素。
          算法可以分为两步:
		  ①、选取候选主元素。依次扫描所给数组的中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数是1,
		  若遇到下一个整数仍等于Num,则计数加1,否则计数减1,当计数减到0的时候,将遇到的下一个整数保存到c中,将计
		  数重新记为1;开始新一轮计数,即充当前位置重复上述过程,直到扫描完全部数组元素。
		  ②、判断c中元素是否是真正的主元素。再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则,序
		  列中不存在主元素。
[用c语言描述]
         int Majority(int A[],int n){
			 int i,c ,count=1;     //c用来保存候选主元素,count用来计数
			 c=A[0];               //开始,设置A[0]为主元素
			 for(i=0;i<n;i++){     //查找主元素
				 if(A[i]==c)
					 count++;      //对当前c保存的候选主元素计数
				 else              //处理不是主元素时候的情况
					 if(count>0)   
						 count--;
					 else{
						 c=A[i];   //更换主元素,重新计数
						 count=0;
					 }
			 }
			 for(i=count=0;i<n;i++) //统计主元素实际出现的次数
				 if(A[i]==c)
					 count++;
			 if(count>n/2)       //符合定义,返回主元素
				 return c;
			 else                //不符合定义,返回-1
				 return -1; 
			 
		 }

[时间与空间复杂度]
时间复杂度:O(n)
空间复杂度:O(1)

注意:本体如果是用先排序再统计的办法(时间复杂度为O(nlog2n)),只要回答正确,最高可拿11分,即便是写
出O(n^2)的算法最高也能拿10分,因此对于算法题,花费大量时间为一个想出来可能性还不大的最优算法是得不偿失的。


----------------------------------------------------------------------------------------------------------------
//---13. [2018统考真题]给定一个含n(n≥1)个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的
//最小正整数。例如,数组{-5,3,2,3}中未出现的最小正整数是1;数组{1,2,3}中未出现的最小正整数是4.要求:
//(1)给出算法的基本设计思想。
//(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
//(3)说明你所设计算法的时间复杂度和空间复杂度。

[算法思想]我们用B[n]来标注A中是否出现了1~n中的数,其中B[i]代表i+1,B[n-1]代表n。B初始化全部为0,由于A中含n个数,返
          回可能是1~n中的任何一个数,也可能是n+1,当A中n个数恰好为1~n时返回n+1,当A中出现了小于1或者大于n的数时,其
		  中1~n中必然至少有一个数是A没有包含的,这时候返回值应该是这些没包含的数中最小的那个。
		  流程为:
		  从A[0]开始遍历A,若0<A[i]<=n,则令B[A[i]-1]=1,否则不做操作。遍历A结束后,开始遍历B数组,第一个满是B[i]==
		  0 的时候,返回i+1,若B全部为1,返回i+1 (这时候i循环结束后由于又执行了一次加1操作为n,i+1 为n+1 ),此时
		  A中未出现的最小整数为n+1
[用c语言描述]
        int findMissMin(int A[],int n){
			int i,*B;                         //标记数组
			B=(int*)malloc(sizeof(int )*n);  //分配空间
			memset(B,0,sizof(int)*n);        //赋初值为0,memset参数分别是起始地址、填充的数、填充长度
			for(i=0;i<n;i++)
				if(A[i]>0&&A[i]<=n)          //若A[i]的值介于1~n,则标记数组B
					B[A[i]-1]=1;
			for(i=0;i<n;i++)                 //扫描数组B,找到结果
				if(B[i]==0)
					break;
			return i+1;	           //①若找到为0的元素,此时i为为0的元素下标,返回结果是i+1,
			                       //②若没找到为0的元素,循环结束,i还会加1,从n-1变成n,返回结果还是i+1

		}
[时间复杂度]
时间复杂度:O(n)
空间复杂度:O(n)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值