线性表顺序表相关习题及详解 ——数据结构

重新系统的过一遍数据结构


现在先回顾下它的定义

#define MaxSize 50              //定义线性表的最大长度
typedef struct{
    ElemType data[MaxSize];     //顺序表的元素
    int length;                 //顺序表的当前长度
}SqList;                        //顺序表的类型定义
#define InitSize = 100              //表长度的初始定义
typedef struct{
    ElemType *data;                 //只是动态分配数组的指针
    int MaxSize,length;             //数组的最大容量和当前个数
}SqList;                            //动态分配数组顺序表的类型定义

注意,题中如果没有强调有序顺序表,那么默认无序 比如下面的第四题


  一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间实现已经固定,一旦空间占满,再加入新的数据将产生溢出,就会导致程序崩溃。

  而动态分配时,储存数组的空间实在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,可以另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要一次性地划分所有所需空间给线性表。


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

2. 设计一个高效算法,将顺序表的所有元素你只,要求算法的空间复杂度为O(1)。

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

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

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

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

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

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

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

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

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

12.已知一个整数序列A=(a0,a1,…,an-1),其中0≤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或++或Java语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度


解答部分

第一题

思想:搜索整个顺序表,查找最小值元素并记住其位置,搜索结束后用最后一个元素填补空出的原最小值元素位置。

bool Del_Min(sqList &L,int &value) {
    //删除顺序表L中最小值元素结点,并通过引用性参数value返回其值
    //如果删除成功,返回true;否则,返回false

    if(L.length == 0)
        return false;               //表空
    value = L.data[0];
    int pos = 0;                    //假设第一个元素是最小的
    for(int i =1;i<L.length;i++)    //循环,寻找具有最小值的元素
        if(L.data[i]<value){        //让value记忆当前具有最小值的元素
            value = L.data[i];
            pos = i;
        }
    L.data[pos] = L.data[L.length-1];//空出的位置由最后一个元素填补
    L.length--;
    return ture;                    //辞职时,value即为最小值

}

第二题

算法思想:扫描顺序表L的前半部分元素,对于元素L.data[i] (0 ≤ i < L.length/2),将其余后半部分对应元素L.data[L.length-i-1]进行交换。

void Reverse(SqList &L) {
    Elemtype temp;                      //辅助变量
    for(i=0;i<L.length/2;i++) {
        temp = L.data[i];               //交换L.dta[i]与L.data[L.length-i-1]
        L.data[i] = L.data[L.length-i-1];
        L.data[L.length-i-1]=temp;
    }
}

其实经过测试,表内元素是奇数或者偶数时都符合条件,所以可以当做一个典型题来记

第三题

有两种方法:

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

void del_x_1(SqList &L,Elemtype x){
    //本算法实现删除顺序表L中所有值为x的数据元素

    int k = 0 ;                 //记录值不等于x的元素个数
    for(i=0;i<L.length;i++){
        if(L.data[i]! = x){
            L.data[k]=L.data[i];
            k++;                //不等于x的元素增1
        }

    }
    L.length=k;                 //顺序表L的长度等于k
}

第二种:
用k记录顺序表L中等于x的元素个数,边扫描L边统计k,并将不等于x的元素前移k个位置,最后修改L的长度。

void del_x_2(SqList &L,Elemtype x) {
    int k =0,i=0;               //k记录值等于x的元素个数
    while(i<L.length) {
        if(L.data[i]==x)
            k++;
        else
            L.data[i-k]=L.data[i];              //当前元素前移k个位置
        i++;
    }
    L.length = L.length-k;      //顺序表L的长度递减
}

第四题

注意:和上面不同,因为是有序表,所以删除的元素必然是相连的整体

思想:先寻找大于等于s的第一个元素(第一个删除的元素),然后寻找值大于t的第一个元素(最后一个删除的元素的下一个元素),要将这段元素删除,则只需直接将后面的元素前移即可

boo Del_s_t2(sqList &L,ElemTyep 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)
        retrun 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;

}

第五题

思想:从前向后扫描顺序表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之间(要求s<t)的所有元素
    int i,k=0;
    if(L.length == 0 || s>= t)
        return false;                           //线性表为空或者s、t不合法,返回
    for(i=0;i<L.lenght;i++){
        if(L.data[i]>=s&&L.data[i]<=t)
            k++;
        else
            L.data[i-k]=L.data[i];              //当前元素前移k个位置
    }//for
    L.length-=k;                                //长度减小
    return true;
}

本题也可以从后向前扫描顺序表,每遇到一个值在s到t之间的元素,则删除该元素,其后的所有元素全部迁移。但异动次数远大于前者,效率不够高

第六题

思想:注意是有序顺序表,值相同的元素一定在连续的位置上,用类似于直接插入排序的思想,初始时将第一个元素看做非重复的有序表。之后以此判断后面的元素是否与前面非重复有序表的最后一个元素相同,如果相同则继续向后判断,如果不同则插入到前面的非重复有序表的最后,直至判断到表尾为止。

bool Delete_Same(SeqList& 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;
}

书上说,对于本题算法,可以以序列1,2,2,2,2,3,3,3,4,4,5来手动模拟算法的执行过程,在模拟的过程中要标注i和j所指示的元素

思考:如果将本题的有序表改为无需表,你能想到时间复杂度为O(n)的方法吗?
(提示:使用hash表)

第七题

思想:首先,按照顺序不断取下两个顺序表表头较小的节点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。

bool Merge(SeqList A,SeqList B,SeqList &C){
    if(A.length + B.length > C.maxSize)     //大于顺序表的最大长度
        return false;
    int i = 0,j= 0,k=0;
    while(i<A.length&&j<B.length){          //循环,两两比较,小者存入结果表
        if(A.data[i]<=B.data[i++]);
            C.data[k++]=A.data[i++];
        else
            C.data[k++]=B.data[j++];
    }
    while(i<A.length)                       //还剩一个没有比较完的顺序表
        C.data[k++]=A.data[i++];
    while(i<B.length)
        C.data[k++]=B.data[j++];
    C.length=k;
    return ture;
}

第八题

思想:
首先将数组A[m+n] 中的全部元素(a1,a2,a3,…,am,b1,b2,b3,…,bn)原地逆置为(bn,bn-1,bn-2,…,b1,am,am-1,am-2,…,a1)再对前n个元素和后m个元素分别使用逆置算法,就可以得到(b1,b2,b3,…,bn,a1,a2,a3,…,am)从而实现顺序表的位置互换

typedef int DataTyep;
void Reverse(DataType A[], int left, int right,int arrySize){
    //逆转(aleft,aleft+1,aleft+2,...,aright)为(aright,aright-1,....,aleft)
    if(left >= right || right>=arraySize)
        return;
    int mid=(left+right)/2;
    for(int i=0;i<=mid-left;i++){
        DataType 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,a3,...,am),从m到m+n-1存放顺序表(b1,b2,b3,...,bn)
         * ,算法将这两个表的位置互换*/
        Reverse(A,0,m+n-1,arryaSize);
        Reverse(A,0,n-1,arryaSize); 
        Reverse(A,0,m+n-1,arryaSize);       
    }
}

第九题

思想:顺序存储的线性表递增有序,可以顺序查找,也可以折半查找。题目要求“用最少的时间在表中查找数值为x的元素”,这里鹰使用折半查找法

void SearchExchanageInsert(ElemType A[],ElemType x ){
    int low = 0,high = n-1,mid;             //low和high指向顺序表上界和下界的下标
    while(low <= high){
        mid=(low+high)/2;                   //找中间位置
        if(A[mid]==x) break;                //找到x,退出while循环
        else if (A[mid]<x) low = mid+1;     //到中点mid的右半部去查
        else high =mid-1;                   //到中点mid的左半部去查
    }
    if(A[mid]==x&&mid!=n-1){                //若最后一个元素与x相等,则不存在与其后继交换的操作
        t=A[mid];A[mid]=a[mid+1];A[mid+1]=t;
    }
    if(low > high){                         //查找失败,插入数据元素x
        for(i=n-1; i>high;i--) A[i+1] =A[i];//后移元素
        A[i+1]=x;                           //插入x
    }
}

第十题

1)思想:可以将这个问题看作是把数组ab转换成数组ba(a代表数组的前p个元素,b代表数组中余下的n-p个元素),先将a逆置得到 a1b ,再将b逆置得到a1b1 ,最后将整个a1b1 逆置得到 (a1b1)1=ba ,设Reverse函数执行将数组元素逆置的操作,对abcdefgh向左循环移动3(p=3)个位置的过程如下:
Reverse(0,p-1)得到cbadefgh;
Reverse(p,n-1)得到cbahgfed;
Reverse(0,n-1)得到defghabc;

注:Reverse中,两个参数分别表示数组中待转换元素的始末位置。

2)使用C语言描述算法如下

void Reverse(int R[],int from,int to){
    int i,temp;
    for(i=0;i<(to-from+1)/2;i++)
    {
        temp=R[from+i];R[from+i]=R[to-i];R[to-i]=temp;
    }//Reverse
    void Converse(int R[],int n ,int p){
        Reverse(R,0,p-1);
        Reverse(R,p,n-1);
        Reverse(R,0,n-1);

    }
}

3)上述算法中三个Reverse函数的时间复杂度分别为O(p/2)、O((n-p)/2)和O(n/2),故所涉及的算法的时间复杂度为O(n),空间复杂度为O(1).

另解,借助辅助数组来实现:

思想:创建大小为p的辅助数组S,将R中前p个整数一次暂存在S中,同事将R中后n-p个整数左移,然后将S中暂存的p个数依次放回到R中的后续单元。
时间复杂度为O(n),空间复杂度为O(p)

第十一题

1)算法的基本涉及思想如下:
分别求两个升序序列A、B的中位数,设为a和b,求序列A、B的中位数过程如下:
①若a=b,则a或b即为所求中位数,算法结束。
②若a

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];           //满足条件1
        if(A[m1]<B[m2]){            //满足条件2
            if((s1+d1)%2==0){       //若元素个数为奇数
                s1=m1;              //舍弃A中间点以前的部分且保留中间点
                d2=m2;              //舍弃B中间点以后的部分且保留中间点
            }
            else{                   //元素个数为偶数
                s1=m1+1;            //舍弃A中间点及中间点以前部分
                d2=m2;              //舍弃B中间点以后部分且保留中间点
            }
        }
        else {                      //满足条件3
            if((s2+d2)%2==0){       //若元素个数为奇数
                d1=m1;              //舍弃A中间点以后的部分且保留中间点
                s2=m2;              //舍弃B中间点以前的部分且保留中间点
            }
            else{                   //元素个数为偶数
                d1=m1;              //舍弃A中间点以后部分且保留中间点
                s2=m2+1;            //舍弃B中间点及中间点以前部分
            }
        }
    }
    return A[s1]<B[s2]?A[s1]:B[s2];
}

3)算法的时间复杂度为O(log2n),空间复杂度为O(1)

第十二题

1)思想:
算法的策略是从前向后扫描数组元素,标记处一个可能成为主元素的元素Num,然后重新计数,确认Num是否是主元素。

算法可分为以下两部
①选取候选的主元素:一次扫描所给数组中的每个整数,将第一个遇到的整数Num保存到c中,记录Num的出现次数为1;若遇到的下一个整数扔等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数,即从当前位置开始重复上述过程,知道扫描完全部数组元素
②判断c中元素是否是真正的主元素:在此扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素;否则,序列中不存在主元素

2)

int Majority(int A[],int n){
    int i,c,count=1;            //c用来保存候选主元素,count用来计数
    c = A[0];                   //设置A[0]位候选主元素
    for(i=1;i<n;i++)            //查找候选主元素
        if(A[i]==c)
            count++;            //对A中的候选主元素计数
        else
            if(count>0)         //处理不是候选主元素的情况
                count--;
            else{               //更换候选主元素,重新计数
                c=A[i];
                count=1;
            }
    if(count>0)
        for(i=count=0;i<n;i++)  //统计候选主元素的实际出险次数
            if(A[i]==c)
                count++;
    if(count>n/2) return c;     //确认候选主元素
    else return -1;             //不存在主元素
}

3)算法的时间复杂度为O(n),空间复杂度为O(1)

发布了83 篇原创文章 · 获赞 103 · 访问量 40万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章