【王道数据结构综合题】线性表 P17

【王道数据结构综合题】线性表 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);
}

在这里插入图片描述

  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)

  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;
}

猜你喜欢

转载自blog.csdn.net/toro180/article/details/122440594
今日推荐