线性表的顺序表示---练习题(算法设计题

代码和解析全手打,难免手误,如有问题,欢迎私信或评论~

存储结构:

静态分配

#define MaxSize 30
typedef struct{
    ElemType data[MaxSize];
    int Lenth;
}SqList;

动态分配 

#define InitSize 50
typedef struct{
    ElemType *data;
    int MaxSize,Lenth;
}SeqList;

题目:

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

bool Del_Min(SqList &L,ElemType &e){
    if(L.Lenth<1) return false;
    int pos=0;
    e=L.data[0];
    for(int i=0;i

2、设计一个高效算法,将顺序表L的所有元素逆置,要求空间复杂度为O(1)
解析:空间复杂度O(1),也就是不使用额外的辅助空间


方法1:(这我自己写的方法 

void reverse(SqList &L){
    for(int i=0,j=L.Lenth-1;i>j;i++,j--){
        ElemType t=L.data[i];
        L.data[i]=L.data[j];
        L.data[j]=t;
    }
}

方法2:(这是书上答案给的方法 

void reverse(SqList &L){
    ElemType t;
    for(int i=0;i

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

解析:符合条件的元素一步一移,注意这个思想,删除线性表我们都可以用这个套路
对比下边第四题 第五题 第六题 看下 

void Del_x(SqList &L,ElemType x){
    int i,j;
    for(i=j=0;i


4、从有序顺序表中删除其值在给定值s和t之间的所有元素(s如果s或t不合理或表空,则显示错误信息并退出 

解析:注意这个题给定的是有序表,也就是说我们要删除的元素都在一起,是一个区间 

所以我们可以考虑一种方法,也是书上给的方法

历记录下要删除的区间起始和结束的地方,计算出区间长度k,将后边的元素直接前移k个单位就好了

但我这里采用的方法是对符合条件的元素一步一移,比刚才说的方法多做了一些移动和比较的操作

没有第一种方法效率高,但从复杂度角度考虑,两者都是O(n)的复杂度

但是后者这个算法的优点是兼容性强

后者可以解决的不仅仅的有序表,无序顺序表删除也可以O(n)内完成

所以在这里我写的是这个算法 

bool Del_s_t (SqList &L,ElemType s,ElemType t){
    if(s>=t|| L.Lenth==0) return false;
    int i,j;
    for(i=j=0;i=t){
            L.data[j++]=L.data[i];
        }
    }
    L.Lenth=j;
    return true;
}

5、从有序顺序表中删除其值在给定值s和t之间的所有元素,包括s和t(s
如果s或t不合理或表空,则显示错误信息并退出


解析:题目给定的依然是有序表,参照我上一题给的解析,这里我用的算法同理 

bool Del_s_t (SqList &L,ElemType s,ElemType t){
    if(s>=t|| L.Lenth==0) return false;
    int i,j;
    for(i=j=0;it){
            L.data[j++]=L.data[i];
        }
    }
    L.Lenth=j;
    return true;
}

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

void unique(SqList &L){
    int i,j;
    for(i=j=1;i


7、两个有序顺序表合并成一个新的有序顺序表,并由函数返回顺序表


关于这个算法的复杂度分析,还考过一个题,可以看这篇 点解跳转--合并两个升序链表复杂度分析(链表顺序表一样

bool merge(SqList L1,SqList L2,SqList &L){
    if(L1.Lenth+L2.Lenth>L.Lenth) return false; 
    int i=0,j=0,k=0;
    while(i


8、已知一维数组A[m+n]中依次存放两个线性表(a1,a2,...,am)(b1,b2,...,bn)
编写一个程序,将两个顺序表位置互换

//先写一个函数,实现将数组中left到right区间逆置 
bool Reverse(DataType A[],int left,int right,int MaxSize){
    if(left>=right||right>=MaxSize) return false;
    int mid=(left+right)/2;
    for(int i=0;i


9、线性表(a1,a2,...an)递增有序,顺序存储。 要求设计一个算法:
最少的时间找到表中值为x的元素,将其与其后继元素交换位置,若找不到,则插入x,并使线性表仍然有序

void funtion(ElemType A[],ElemType x){
    int l=0,r=n-1,mid; //折半查找 
    while(l=l;i--) A[i+1]=A[i];
        A[l]=x;
    }
} 

10、(2010真题)n个整数存放在一维数组R中,设计一个在时间和空间两方面都尽可能高效的算法
将R中的序列循环左移P个位置(0(Xp,Xp+1,...,Xn-1,X0,X1,...,Xp-1) 
1)给出算法的设计思路 
2)用c/c++/java描述算法 
3)说明空间复杂度和时间复杂度

解析:
仔细观察可以发现,所谓的循环左移,其实也就是交换数组中两子列的位置
前边我们做过这样的题(第8题),利用逆置


设计思路:

循环左移操作可以看作将数组分为两部分,ab,将其变为ba
我们设Reverse函数实现逆置操作,则完成循环左移需要
Reverse(R,0,p-1);
Reverse(R,p,n-1);
Reverse(R,0,n-1); 


描述算法:

bool Reverse(int A[],int l,int r,int maxsize){
    if(l>=r||r>maxize) return false;
    for(int i=0;i<(r-l+1)/2;i++){
        int temp=A[i+l];
        A[l+i]=A[r-i];
        A[r-i]=temp;
    }
    return true;
} //逆置 
void Converse(int R[],int p,int n){
    Reverse(R,0,p-1,n);
    Reverse(R,p,n-1,n);
    Reverse(R,0,n-1,n); 
}

复杂度:

逆置的复杂度是n/2  
故0到p-1的逆置复杂度为O(p/2) p到n-1的为O((n-p)/2) 再整体逆置是O(n/2)
加起来 时间复杂度是O(n) 空间复杂度为O(1) 

11、(2011年真题)长度为L的升序序列S,在第(L/2)个位置的数称为S的中位数,(注意:向上取整,也就是L=5,则第三个是中位数),两个序列的中位数是指两个序列合并后的的中位数
现给出等长升序序列A和B,试着设计算法,找出A和B的中位数
1)给出算法的设计思路 
2)用c/c++/java描述算法 
3)说明空间复杂度和时间复杂度

这个算法如果不太懂,可以看这一篇博客,关于这道题的详细解析-->戳一戳~~


设计思路:

分别求出两个升序序列A、B的中位数a、b
   1、若a=b 则a,b即为所求的中位数 
   2、若ab 则舍弃B中较小的一半,同时舍去A中较大的一半,要求两次舍弃的长度相等
在保留下来的两个升序序列中,重复上述三个步骤,直到两个序列中都只上一个元素为止,
较小的就是所求的中位数


算法描述:

int M_search(int A[],int B[],int n){
    int s1=0,d1=n-1,s2=0,d2=n-1,m1,m2;   //起点 终点 中间点 
    while(s1!=d1||s2!=d2){
        m1=(s1+d2)/2;
        m2=(s2+d2)/2;
        if(A[m1]==B[m2]) return A[m1];  //若相等 
        if(A[m1]b 与上边同理 互换操作即可 
            if((s2+d2)%2==0){
                d1=m1;
                s2=m2;
            }
            else{
                d1=m1;
                s2=m2+1;
            }
        }
    }
    return A[s1]


复杂度分析:

时间复杂度O(logn)
空间复杂度O(1)

12、(2013真题) 已知整数序列A=(a0,a1,...,an-1),其中0<=ai 若存在....哎呀 题目太长了 不想打了

大概就是说,A里面如果有值相同的元素个数超过n/2,则这个值就是主元素
设计算法找主元素。其实就是找众数,且次数大于n/2

如图

线性表的顺序表示---练习题(算法设计题_第1张图片

 
设计思路:

因为题目要求,这个数必须出现次数多与总数一半,所以我们可以把所有不一样的 
数字互相抵消,最终一定会剩下这个数字
当然还存在无解的情况,所以我们抵消到最后,剩下的,不一定是要求的,需要再扫描验证下

算法描述:

void Sreach(int A[],int n){
    int k,cnt=0;           //k存数 cnt计数 
    for(int i=0;i0){
        cnt=0;
        for(int i=0;in/2) return k;  //返回结果 
        else return -1;       //无解 
    }
    else return -1;          //无解 
} 


复杂度分析:

时间复杂度O(2n)=O(n)
空间复杂度O(1)

注意:先排序,再扫描。时间复杂度是O(nlogn)
但是我看答案上居然说写出O(n^2)的复杂度都可以拿到10分!!(那还学啥,别学了~
看来算法不需要太好,关键是要写正确!

13、(2018真题)给定n个整数的数组,设计一个时间上尽可能高效的算法
找出数组中未出现的最小正整数 
注意:数组中可能有负数 例如A{-5,3,2,3} 答案是1 
1)给出算法的设计思路 
2)用c/c++/java描述算法 
3)说明空间复杂度和时间复杂度


设计思路:

题上说了时间上尽可能高效,大概就是暗示你空间不需要太省
那么我们再开一个数组B用来打标记,扫描A数组,遇到值在(0,n+1)之间的,把b中对应位置置为1


算法描述:

 

int findmin(int A[],int n){
    int i,*B;
    B=(int *)malloc(sizeof(int)*n);
    memset(B,0,sizeof(int)*n);
    for(int i=0;i0&&A[i]

复杂度:

时间复杂度:O(2n)=O(n)
空间复杂度:O(n) 

这个题虽然简单,但是有一些需注意的点:
利用另一个数组计数,这叫桶计数,或叫桶排序 
需要注意的是数组的长度是有限的,你可以对比第十二题,
第十二题让我们找众数,原理上来说我们也可以采用这种方法,但是注意题目要求,第十二题给你的是一个序列,长度为n,又没说n的大小,所以你不能用这种方法,因为可能数字太大,数组存不下
而这道题,注意,我们用的方法是只针对值在(0,n+1)之间的数字处理,不在范围的的不用管 
而且,题目上说了,给的是A数组,n是数组长度,所以说明n是可以开数组的
基于此,我们才可以开n长度的数组,并且用桶计数
然后再注意,我数组长度开的是n,那下标就是0到(n-1)
极端情况下A中n个数,恰好是1-n,所以存的时候,要错开存
且极端情况下扫描到第n-1个数还没找到,则答案是n+1,所以注意 i 要声明在外边,因为需要用到它的值! 

你可能感兴趣的:(数据结构(严蔚敏版))