【王道数据结构综合题】线性表 P17
(注)算法是在我自己思考后又参考了王道的答案写的。
算法思想:遍历整个线性表,找到最小值,并记录其下标,最后用最后一个元素替换最小值。( tips:INFINITY=0xfffffff)
bool DeleteMin(SqList &L,int &x){
if(IsEmpty(L)){
printf("错误信息:顺序表为空\n");
return false;
}
int min=INFINITY,idx=-1;
for(int i=0;i<L.length;i++){
if(L.data[i]<min){
min=L.data[i];
idx=i;
}
}
L.data[idx]=L.data[L.length-1];
L.length--;
}
算法思想:设置i、j两个指针分别指向表头和表尾,依次交换元素值。同时适用元素个数为奇数和偶数的情况。(tips:这个逆置的思想经常使用,需要牢记)
//空间复杂度为1的顺序表逆置
void Reverse(SqList &L){
int tmp;
for(int i=0,j=L.length-1;i<j;i++,j--){
tmp=L.data[i];
L.data[i]=L.data[j];
L.data[j]=tmp;
}
}
算法思想:由于要求空间复杂度为O(1),所以就不能借用一个新表,通过遍历旧表依次把非x的元素插入新表了。
(tips:这里的两个思想还挺有用的,后面多次用到了)
解法1:遍历顺序表,在遍历的过程中用count记录当前的x的数量。对于值为x的情况,count++;对于值为非x的情况,往前移动count个单位。最后顺序表的表长减去count。
//空间复杂度为1的顺序表逆置-解法1
void DeleteAllX1(SqList &L,int x){
int count=0;//count记录当前x的数量
for(int i=0;i<L.length;i++){
if(L.data[i]==x){
count++;
}else{
L.data[i-count]=L.data[i];
}
}
L.length-=count;
}
解法2:遍历顺序表,在遍历的过程中用count记录当前的非x的数量。对于值为x的情况,不操作;对于值为非x的情况,移动至count的位置,count++。最后顺序表的表长等于count。
//空间复杂度为1的顺序表逆置-解法2
void DeleteAllX2(SqList &L,int x){
int count=0;//count记录当前非x的数量
for(int i=0;i<L.length;i++){
if(L.data[i]!=x){
L.data[count]=L.data[i];
count++;
}
}
L.length=count;
}
算法思想:由于是有序顺序表,所以要删除的元素一定物理位置相邻。所以只需要找到要删除的首个元素的地址和最后一个元素的地址,最后将剩余元素往前移动即可。
//删除s到t之间的值
bool DeleteBetweenST(SqList &L,int s,int t){
if(s>=t){
printf("输入错误的s和t值\n");
return false;
}
if(IsEmpty(L)){
printf("顺序表为空\n");
return false;
}
int first=-1,last=-1;//分别记录要删除的第一个值的index和最后一个值的index
//找到first和last
for(int i=0;i<L.length;i++){
if(L.data[i]<s){
first=i+1;
}
if(L.data[i]<=t){
last=i;
}
}
//删除first和last之间的值,将last之后的值往前移动。
for(int i=first,j=last+1;j<L.length;i++,j++){
L.data[i]=L.data[j];
}
//修改length值
L.length-=last-first+1;
return true;
}
算法思想:这里由于不是有序,所以和上题有差别。本题我用了和题3一样的方法,仅仅是把判断元素值是否等于x改成了是否在s,t之间。
//删除s到t之间的值
bool DeleteBetweenST(SqList &L,int s,int t){
if(s>=t){
printf("输入错误的s和t值\n");
return false;
}
if(IsEmpty(L)){
printf("顺序表为空\n");
return false;
}
int count=0;//count记录当前要删除的元素的数量
for(int i=0;i<L.length;i++){
if(L.data[i]<=t&&L.data[i]>=s){
count++;
}else{
L.data[i-count]=L.data[i];//非删除元素往前移动count个单位
}
}
//修改length值
L.length-=count;
return true;
}
算法思想:同题三,遍历顺序表,使用count记录当前要删除的元素个数,如果data[i]=data[i+1],则count++;否则就把元素往前移动count个单位。
//删除重复的值
bool DeleteRepeat(SqList &L){
int count=0;//count记录当前删除的数量
for(int i=0;i<L.length;i++){
if(L.data[i]==L.data[i+1]){
count++;
}else{
L.data[i-count]=L.data[i];
}
}
//修改length值
L.length-=count;
return true;
}
算法思想:顺序比较两个顺序表的元素大小,把小的元素插入新的顺序表的尾部。最后一定只剩下L1或者L2的后半部分,只需要把它们顺序插入队尾即可。
SqList Emerge(SqList L1,SqList L2){
SqList L;
InitList(L);
int i=0,j=0;
//顺序比较L1 L2的元素大小,把小的元素插入到新顺序表的队尾
while(i<L1.length&&j<L2.length){
if(L1.data[i]<=L2.data[j]){
ListInsert(L,i+j+1,L1.data[i]);
i++;
}else{
ListInsert(L,i+j+1,L2.data[j]);
j++;
}
}
//把L1 或 L2剩余的元素插入顺序表尾
while(i<L1.length){
ListInsert(L,i+j+1,L1.data[i]);
i++;
}
while(j<L2.length){
ListInsert(L,i+j+1,L2.data[j]);
j++;
}
//更新新顺序表表长
L.length=L1.length+L2.length;
return L;
}
算法思想:运用逆置的思想。首先对整体逆置,再分别对两个线性表逆置。
void reverseTwice(int array[],int length1,int length2){
int tmp;
//整体逆置
for(int i=0,j=length1+length2-1;i<j;i++,j--){
tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
//两个顺序表分别逆置
for(int i=0,j=length2-1;i<j;i++,j--){
tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
for(int i=length2,j=length1+length2-1;i<j;i++,j--){
tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
算法思想:要求用最少时间,应使用折半查找。
//查找插入交换x
void SearchExchangeInsert(SqList &L,int x){
int low=0,high=L.length-1,mid;
//折半查找x
while(low<=high){
mid=(low+high)/2;
if(L.data[mid]==x) break;
else if(L.data[mid]<x) low=mid+1;
else high=mid-1;
}
//查找成功,且x不是最后一个元素
if(L.data[mid]==x&&mid!=L.length-1){
int tmp=L.data[mid];
L.data[mid]=L.data[mid+1];
L.data[mid+1]=tmp;
}
//查找失败,插入元素x
if(low>high){
int i;
for(i=L.length-1;i>high;i--)
L.data[i+1]=L.data[i];
L.data[i+1]=x;
L.length++;
}
}
算法思想:三次逆置。假设开始时为ab,先整体逆置变成b逆a逆,再分别对两个顺序表逆置,变成ba。
//将顺序表从start到end进行逆置
void reverse(SqList &L,int start,int end){
int tmp;
for(int i=start,j=end;i<j;i++,j--){
tmp=L.data[i];
L.data[i]=L.data[j];
L.data[j]=tmp;
}
}
//左移x位
void LeftShift(SqList &L,int x){
reverse(L,0,L.length-1);
reverse(L,0,L.length-x-1);
reverse(L,L.length-x,L.length-1);
}
- 我的解法
算法思想:由于两个顺序表等长(假设每个顺序表表长为L),所以中位数就是第L个元素,所以只需要从小往大数至第L位即可。
//找两个顺序表的中位数,L1 L2等长
int FindMiddle(SqList L1,SqList L2){
int midIdx=L1.length;//确定中位数是第几位数,从1开始数
int i=0,j=0;
while(i<L1.length&&j<L2.length){
if(L1.data[i]<=L2.data[j]){
i++;
if(i+j==midIdx)
return L1.data[i];
}
else{
j++;
if(i+j==midIdx)
return L2.data[j];
}
}
}
时间复杂度O(n),空间复杂度O(1)
- 王道解法
算法思路:(*感觉不太好想*)分别求两个升序序列A,B的中位数,设为a,b,求A,B的中位数的过程如下:
①若a=b,则a或b即为所求中位数,算法结束。
②若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
③若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中重复①②③,知道两个序列均只含一个元素为止,较小者即为所求中位数。
时间复杂度O(logn),空间复杂度O(1)
算法思想:先筛选一个数量最多的元素,再验证该元素是否是主元素。具体步骤:
①选取第一个元素为候选元素main,依次扫描数组,若遇到一个与main相等的元素,count+1;若遇到一个与main不相等的元素,count-1。假设count减至0,则更换当前元素为主元素,继续上述操作,直至扫描完所有元素。
//找主元素
int FindMain(SqList L){
int count=0;
int main=L.data[0];
//筛选一个候选元素
for(int i=0;i<L.length;i++){
if(L.data[i]==main){//与主元素相等,count+1
count++;
}else{//与主元素不等
if(count>0) count--;//count>0,主元素count-1
else{ //count减至0,更换主元素
main=L.data[i];
count++;
}
}
}
//验证是否是主元素
if(count>0)
count=0;
for(int i=0;i<L.length;i++){
if(L.data[i]==main) count++;
}
//返回结果
if(count>L.length/2) return main;
else return -1;
}
算法思想:对于一个表长为n的数组来说,其未出现的最小正整数范围即为1…n+1。所以分配一个用于标记的数组p[n+1],用来记录是否出现1~n中的正整数。最后从头扫描,遇到的第一个标记为0的就是未出现的最小正整数。
//找未出现的最小正整数
int FindNoShowMin(SqList L){
int p[L.length+1]={0};//标记正整数从1到L.length+1,映射关系:p[i]对应i+1,即p[0]对应整数1
//标记
for(int i=0;i<L.length;i++){
if(L.data[i]>0)//出现正整数,即给它标记为1
p[L.data[i]-1]=1;
}
//确定未出现的最小正整数
for(int i=0;i<L.length+1;i++){
if(p[i]==0)
return i+1;
}
}
算法思想:
假设三元组从小到大为a b c,则易得距离D=2(c-a)。所以决定D大小的关键是a和c之间的距离。
循环执行①②③
①计算A[i] B[j] C[k]的距离D
②若D<DMin,更新DMin
③将A[i] B[j] C[k]中最小的那个下标+1(即,最小值为a,最大值为c,固定c而更新a,试图找到更小的距离D)
//计算绝对值
int Abs(int a){
if(a>0) return a;
else return -a;
}
//a是否是三个中的最小值
int IsMin(int a,int b,int c){
if(a<=b&&a<=c) return true;
else return false;
}
//找最小距离的三元组
int FindMinOfTrip(SqList L1,SqList L2,SqList L3){
int i=0,j=0,k=0,DMin=0xfffffff,D;
while(i<L1.length&&j<L2.length&&k<L3.length&&DMin>0){
D=Abs(L1.data[i]-L2.data[j])+Abs(L1.data[i]-L3.data[k])+Abs(L3.data[k]-L2.data[j]);
if(D<DMin) DMin=D;
if(IsMin(L1.data[i],L2.data[j],L3.data[k])) i++;
else if(IsMin(L2.data[j],L1.data[i],L3.data[k])) j++;
else k++;
}
return DMin;
}