王道第二章线性表习题

P17综合应用题

bool DeleteMin(SqList &L,ElemType &min){
    if(L.length==0){
        printf("顺序表为空\n");
        return false;
    }
    else{
        //先找到最小元素的位置
        min=L.data[0];
        int index=0;//最小元素在数组中的下标
        for(int i=1;i<L.length;i++){
            if(L.data[i]<min){
                min=L.data[i];
                index=i;
            }
        }
        /*
        if(index == L.length-1){//说明这就是最后一个元素
           	L.length--;
            return true;
        }
        else{*/
            L.data[index]=L.data[L.length-1];//将最后一个元素填充过去
            return true;
        //}
    }
}
bool ReverseList(SqList &L){
    if(L.length==0){
        printf("表空\n");
        return false;
    }
    Elemtype t;
    for(int i=0;i<(L.length/2);i++){
        t=L.data[L.length-1-i];
        L.data[L.length-1-i]=L.data[i];
        L.data[i]=t;
    }
 	return true;
}

用k记录顺序表L中不等于x的元素个数(即需要保存的元素的个数),边扫描L边统计k, 并将不等于x的元素向前移动k个位置,最后修改L的长度

bool DeleteX(SqList &L,Elemtype x){
    if(L.length==0){
        printf("表空\n");
        return false;
    }
    int k=0;
    for(int i=0;i<L.length;i++){
        if(L.data[i] != x){
            L.data[k]=L.data[i];
            k++;
        }
    }
    L.length=k;//@@@
}

这种解法可以不考虑有序表的升序降序问题,但是没有利用有序表的优势

bool DeleteStoT(SqList &L, Elemtype s,Elemtype t){
    if(s>=t || L.length==0){
        printf("Error\n");
        return false;
    }
    int k=0;
    for(int i=0;i<L.length;i++){
        if(!(L.data[i] >= s && L.data[i] <= t)){
            //不符合删除条件的元素则前移k个位置,最后修改L的长度
            L.data[k]=L.data[i];
            k++;
        }
    }
    L.length=k;
    return true;
}

解法2:但是下面的答案默认所给有序表是升序排列的…

bool DeleteStoT(SqList &L, Elemtype s,Elemtype t){
    if(s>=t || L.length==0){
        printf("Error\n");
        return false;
    }
    int i,j;
    for(i=0;i<L.length && L.data[i]<s;i++);//找到第一个>=s的元素的位置
    if(i>=L.length){
        return false;//所有元素均<s;
    }
    for(j=i;j<L.length && L.data[j]<=t;j++);//找到第一个>t的元素的位置
    for(;j<L.length;i++,j++){
        L.data[i]=L.data[j];
    }
    L.length=i;
    return true;
}
bool DeleteStoT(SqList &L, Elemtype s,Elemtype t){
    if(s>=t || L.length==0){
        printf("Error\n");
        return false;
    }
    int k=0;
    for(int i=0;i<L.length;i++){
        if(!(L.data[i] >= s && L.data[i] <= t)){
            //不符合删除条件的元素则前移k个位置,最后修改L的长度
            L.data[k]=L.data[i];
            k++;
        }
    }
    L.length=k;
    return true;
}
bool DeleteSame(SqList &L){
    if(L.length==0){
        printf("Error\n");
        return false;
    }
    int k=0;
    for(int i=1;i<L.length;i++){
        if(L.data[i] != L.data[k])){//若后面的元素不等与前面的元素则加入k中
            L.data[++k]=L.data[i];
        }
    }
    L.length=k+1;//因为k表示数组下标
    return true;
}
bool MergeOrderedList(SqList A,SqList B,SqList &C){
    if(A.length+B.length > C.maxsize){
        return false;
    }
    int i,j,k;
    k=0;
    for(i=0,j=0;i<A.length && j<B.length;){
        if(A.data[i]<B.data[j]){
            C.data[k++]=A.data[i++];
        }else{
            C.data[k++]=B.data[j++];
        }
    }
    while(i<A.length){
        C.data[k++]=A[i++];
    }
    while(j<B.length){
        C.data[k++]=B[j++];
    }
    C.length=k;
    return true;
}
bool ExchangeAB(int A[],int m,int n){
    int i,j;
    i=m-1;
    j=m+n-1;
    int t;
    for(;i>=0;i--,j--){
        t=A[i];
        A[i]=A[j];
        A[j]=t;
    }
    return true;
}
//折半查找
void FindOrInsert(List &L,Elemtype x){
    int low=0,high=L.length-1;
    int mid;
    while(low < high){
        mid=(low+high)/2;
        if(L.data[mid] == x)
            break;
        if(L.data[mid] < x){
            low=mid+1;
        }else{
            high=mid-1;
        }
    }
    
    if(L.data[mid]==x && mid != L.length-1){
    //找到且不是最后一个元素,则和其后面一个元素互换位置
        Elemtype t=L.data[mid+1];
        L.data[mid+1]=L.data[mid];
        L.data[mid]=t;
    }
    if(low > high){
        for(int i=L.length-1; i>high;i--){
            A[i+1]=A[i];
        }
        A[i+1]=x;
        A.length++;
    }
    
}
// My solution   和第8题很像,感觉这种解决方案更优秀
#include<stdio.h>
bool ExchangeAB(int A[],int m,int n){
    int i,j;
    i=m-1;
    j=m+n-1;
    int t;
    for(;i>=0;i--,j--){
        t=A[i];
        A[i]=A[j];
        A[j]=t;
    }
    return true;
}
int main(){
    int a[10]={1,2,3,4,5,6,7,8,9,10};
   ExchangeAB(a,4,6);
    for(int i=0;i<10;i++){
        printf("%d ",a[i]);
    }
}


官方答案

  1. 算法的基本思想,可将这个问题视为把数组ab转换为数组ba(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先将a逆置,再将b逆置,然后将上面得到的结果整体逆置
  2. 代码
  3. 时间复杂度O(n),空间复杂度O(1)
void Reverse(int R[], int from, int to){
    int i,temp;
    for(i=0;i <(to-from+1)/2;i++){
        temp=R[i+from];
        R[i+from]=R[to-i];
        R[to-i]=temp;
    }

}
void Converse(int R[], int n, int p){
    if(p>=n){
        p=p%n;
    }
    Reverse(R,0,p-1);
    Reverse(R,p,n-2);
    Reverse(R,0,n-1);
}

#include<stdio.h>
int M_Search(int A[],int B[],int n){
	int s1=0,d1=n-1,s2=0,d2=n-1;
	int m1,m2;
	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+d1)%2==0){//长度为奇数
                    s1=m1;
                    d2=m2;
                }else{
                    s1=m1+1;
                    d2=m2;
                }
		}
		else{//A[m1]>B[m2]
            if((s2+d2)%2==0){
                s2=m2;
                d1=m1;
            }else{
                d1=m1;
                s2=m2+1;
            }
		}
	}
	return A[s1]<B[s2]?A[s1]:B[s2];
}

int main(){
    int a[]={11,13,15,17,19};
    int b[]={2,4,5,8,20};
    int len = sizeof(a)/sizeof(int);
    printf("len = %d\n\n",len);
    int mid = M_Search(a,b,len);
    printf("%d",mid);
    return 0;
}

记主元素的为p
因为候选主元素的个数要大于总数的一半,即 >= n/2+1. 所以如果存在主元素的话它的count一定是>1。分析如下:

  1. 假设总数为偶数个,则候选主元素的count在最坏的情况下是候选主元素均匀分布在数组中,则又因为是总数是偶数个且主元素个数要>= n/2+1,所以最坏情况下必定p在数组中间隔一个分布一个且总有一个位置是有两个连续的p在一起的,所以最坏情况下count也必定大于1 。
  2. 假设总数是奇数个,则最坏情况下m是间隔一个分布一个的,且数组的头尾都是m,所以这种最坏情况下count也必定大于1 。
  3. 综上,可以用count这个巧妙的方法来判断是否有主元素。主要是要注意到要求主元素的个数要大于总数的一半,然后利用这个条件想出来的特征。
    4.当然,主元素必定有count>=1; 但是count>=1却不一定有主元素,所以最后还要统计一下这个count>=1的元素到底有多少个,是否>=n/2+1;
#include<stdio.h>

int MainElement(int A[],int n){
    int e,count=1;
    e=A[0];
    for(int i=0;i<n;i++){
        if(A[i]==e){
            count++;
        }
        else{
            if(count > 0){
                count--;
            }
            else{
                e=A[i];
                count=1;
            }
        }
    }
    if(count > 0){
        count =0;
        for(int i=0;i<n;i++){
            if(A[i]==e){
                count++;
            }
        }
    }
    if(count > n/2 ){
        return e;
    }
    return -1;
}

int main(){
    int a[8]={0,5,5,3,5,7,5,5};
    int b[8]={0,5,5,3,5,1,5,7};
    int maina=MainElement(a,8);
    int mainb=MainElement(b,8);
    printf("maina=%d\n\nmainb=%d\n\n",maina,mainb);
    return 0;
}

数组元素的个数为n, 从1开始排查到n, 如果一旦有没查到的就说明找到了,但是时间复杂度为O(n^2)

#include<stdio.h>

int MinInteger(int A[],int n){
    int i,j;
    for(i=1;i<=n;i++){
        int flag=0;
        for(j=0;j<n;j++){
            if(i == A[j]){
                flag=1;//说明找到与i相同的了
                break;
            }
        }
        if(j==n && flag==0){//如果找到最后一个还没找到和i相同的A[j],退出循环
            if(i <= n)
                return i;
            else
                return i+1;
        }
    }
}

int main(){
    int a[8]={1,2,3,4,5,6,8,7};
    int b[8]={0,5,5,3,5,1,5,7};
    int x=MinInteger(a,8);
    int y=MinInteger(b,8);
    printf("x=%d\n\ny=%d\n\n",x,y);
    return 0;
}

标准答案
memset函数按字节对内存块进行初始化,所以不能用它将int数组初始化为0和-1之外的其他值(除非该值高字节和低字节相同)
以空间换时间

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int MinInteger(int A[],int n){
    int i,*B;
    B=(int*)malloc(sizeof(int) * n);
    memset(B,0,sizeof(int)*n);
    for(i=0;i<n;i++){
        if(A[i]>0 && A[i]<=n)
            B[A[i]-1]=1;
    }
    for(i=0;i<n;i++){
        if(B[i]==0)
            break;
    }
    return i+1;
}

int main(){
    int a[8]={1,2,3,4,5,6,8,7};
    int b[8]={0,5,5,3,5,1,5,7};
    int x=MinInteger(a,8);
    int y=MinInteger(b,8);
    printf("x=%d\n\ny=%d\n\n",x,y);
    return 0;
}

暴力求解

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int MinDistance(int A[],int a,int B[],int b,int C[],int c){
    int mins=abs(A[0]-B[0])+abs(A[0]-C[0])+abs(B[0]-C[0]);
    int group[3]={A[0],B[0],C[0]};
    int sum;
    for(int i=0;i<a;i++)
        for(int j=0;j<b;j++)
            for(int k=0;k<c;k++){
                sum=abs(A[i]-B[j])+abs(A[i]-C[k])+abs(C[k]-B[j]);
                if(sum<mins){
                    mins=sum;
                    group[0]=A[i];
                    group[1]=B[j];
                    group[2]=C[k];

                }
            }
    printf("tuple is: ");
    for(int i=0;i<3;i++)
        printf("%d ",group[i]);
    return mins;

}

int main(){
    int S1[3]={-1,0,9};
    int S2[4]={-25,-10,10,11};
    int S3[5]={2,9,17,30,41};
    int d=MinDistance(S1,3,S2,4,S3,5);
    printf("\n");
    printf("%d\n",d);
    return 0;
}

标准答案:
为啥要把最小值的下标+1, 因为如果把非最小值的下标+1,则得到的Distance只会比原来的更大。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
bool isMin(int a,int b,int c){//a是否为最小的那个
    if(a<=b && a<=c)
        return true;
    else
        return false;
}
int MinDistance(int A[],int a,int B[],int b,int C[],int c){

    int group[3]={A[0],B[0],C[0]};
    int i,j,k,d;
    int mind=abs(A[0]-B[0])+abs(A[0]-C[0])+abs(B[0]-C[0]);
    i=j=k=0;
    for(;i<a && j<b && k<c;){
        d=abs(A[i]-B[j])+abs(A[i]-C[k])+abs(B[j]-C[k]);
        if(d < mind){
            mind = d;
            group[0]=A[i];group[1]=B[j];group[2]=C[k];
        }
        if(isMin(A[i],B[j],C[k])){//如果A[i]为三个中最小的
            i++;
        }
        else if(isMin(B[j],C[k],A[i])){
            j++;
        }
        else{
            k++;
        }
    }
    printf("tuple is: ");
    for(int p=0;p<3;p++){
        printf("%d ",group[p]);
    }
    printf("\n");
    return mind;
}

int main(){
    int S1[3]={-1,0,9};
    int S2[4]={-25,-10,10,11};
    int S3[5]={2,9,17,30,41};
    int d=MinDistance(S1,3,S2,4,S3,5);
    printf("\n");
    printf("%d\n",d);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值