王道数据结构-线性表2.2.4课后习题

今天编写后续习题的代码并且对比答案。

第二题:设计一个高效的算法,将顺序表的所有元素逆置,要求算法的空间复杂度为O(1)

思路:因为要将元素逆置,并且空间复杂度为O(1),所以利用头尾交换是最快速便捷的方法,将L.data[i]和L.data[L.length-1-i]进行交换即可完成对顺序表逆置的操作

代码如下:

void nizhi(SqList &L){
    int i=0, temp;
    for(;i<L.length/2;i++){
        temp=L.data[i];
        L.data[i]=L.data[n-1-i];
        L.data[n-1-i]=temp;
    }
}

课后答案:

void Reverse(SqList& L) {
	Elemtype temp;
	for (i = 0; i < L.length / 2; i++) {
		temp = L.data[i];
		L.data[i] = L.data[L.length - i - 1];
		L.data[L.length - i - 1] = temp;
	}
}

思考:关于辅助变量temp的类型设置又出入,我将temp定为int型,但是因为temp存储的是线性表中的元素,而线性表中的元素不一定是int型,所以使用int不具有普遍性,而ElemType是指元素类型,并没有对元素类型进行特指,是所有元素类型的总概,虽然在没有特殊指定的情况下,在算法中ElemType默认为是int型,但是还是有区别的,因此在书写数据结构的代码的时候,对存储线性表中元素的辅助变量,或者获取线性表中的元素,统一写为ElemType

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

思想:因为需要删除线性表中的元素,还要求时间复杂度为O(n),空间复杂度为O(1),于是可以使用快慢指针,先规定一个指针在前面走的快一点,然后规定一个指针存储元素,将所有不是x的元素存进慢指针,最后让新链表的长度等于慢指针的下标,这样就可以在从头到尾一次扫描完链表的情况下删除所有的元素x,而不需要每一次遇到x都将后面的元素前移

代码如下:

void Del_x(SqList &L,ElemType x){
    int i=0,j=0;/*设置快慢指针*/
    for(;i<L.length;i++){
        if(L.data[i]!=x)
            L.data[j++]=L.data[i];/*用慢指针存放非x的元素,并记录非x元素的个数*/
    }
    L.length=j;/*令线性表的长度为j*/
}

课后答案:

void Del_x(SqList& L,ElemType x) {
	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;
}

补充:书后还给了两个方法,一个是头尾指针,一个是一边扫描一边统计x元素的个数,并前移x后的元素。但是只给了后面一种方法的代码,代码如下:

void del_x(SqList &L,ElemType x){
    int k=0,i=0;
    while(i<L.length){
        if(L.data[i]==x)
            k++;/*记录顺序表中元素为x的个数*/
        else
            L.data[i-k]=L.data[i];/*当前元素前移k个位置*/
        i++;
    }
    L.length=L.length-x;
}

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

思想:首先判断键入的s和t是否满足s<t这个条件,如果不满足,则返回false,同时判断顺序表是否为空,如果为空,同样返回false,如果s<t且顺序表不为空,则找到顺序表中位于s位置的元素和位于t位置的元素,将t后面的元素全部挪到s后面

代码如下:

void Del_st(SqList &L,ElemType s,ElemType t){
    int i,j;
    if(s>=t||L.length==0)
        return false;
/*如果s>=t或者有序顺序表为空,则返回false*/
    for(i=0;L.data[i]<=s;i++)
/*找到大于s的第一个元素*/
    for(j=L.length-1;L.data[j]>=t;j--)
/*找到大于t的第一个元素*/
    if(i>=L.length||j<=0)
        return false;
/*如果s比有序顺序表中最大的元素还大或者t比有序顺序表中最小的元素还小,则返回false*/
    while(j<L.length)
        L.data[i++]=L.data[j++];
/*将大于t的元素依次移动到s的后面*/
    L.length=i+1;
/*修改表长*/
    return true;
}

课后答案:

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

思考:1.对比两个代码,我发现我写的代码有一个严重的问题就是在寻找大于s和大于t的元素的时候,没有增加<L.length这个条件,这会导致进入死循环,一直运行。

2.因为这个代码需要返回,所以给函数定义的类型不应该是void,void是指不带有返回值的类型,void和return有冲突。

第六题:从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同

思想:因为是有序顺序表,所以后面的元素一定大于等于前面元素的值,所以只需要对比一个元素和其后一个元素大小相比较,如果相等,就继续往后找,如果不想等,就将那个不同的元素插入到前面同样元素的位置。

bool Del(SqList &L){
    int i,j;
    if(L.length==0)
        return false;
/*如果有序顺序表为空,则返回false*/
    for(i=1,j=0;i<L.length;i++){
        if(L.data[i]!=L.data[j])
            L.data[++j]=L.data[i];
/*如果在i位置上的元素和在j位置上的元素不相等,就将这个元素放在j的后面*/
    }
    L.length=j;
/*重新赋值有序顺序表的长度*/
    return ture;
}

课后答案:

bool Delete_Same(SqList &L){
    if(L.length==0)
        return false;
    int 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;
    return ture;
}

第七题:将两个有序顺序表合并成一个新的有序顺序表,并由函数返回结果顺序表

思想:首先判断其中一个表是否为空,如果为空,则返回另一个表,如果两个都不为空,则判断两个表表长,然后需要判断两个表中结点位置元素的大小,将小的一个元素通过尾插法插入空表,在遍历完短的链表之后,将较长链表尚未遍历到的元素直接插入新表

代码如下:

bool link(SqList &L1,SqList &L2,SqList &L3){
/*L1和L2是两个已经给定的有序顺序表*/
    int i,j,k;
    for(i=0,j=0,k=0;i<L1.length&&j<L2.length;){
/*设置变量同时遍历L1和L2*/
        if(L1.data[i]<L2.data[j])
            L3.data[k++]=L1.data[i++];
        else
            L3.data[k++]=L2.data[j++];
/*将L1和L2中较小的元素返回顺序表L3中,并在遍历完长度较短的顺序表之后结束*/
    }
    while(i<L1.length)/*表示L1没有遍历完*/{
         L3.data[k++]=L1.data[i++];
    }
     while(i<L2.length)/*表示L2没有遍历完*/{
         L3.data[k++]=L2.data[i++];
    }
    L3.length=k+1;
    return ture;
}

课后答案:

bool link(SqList A,SqList B,SqList 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[j])
            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[i++];
    }
    C.length=k+1;
    return ture;
}

思考:在写代码的时候考虑到了新的顺序表是接收的,但是没有考虑到新的表因为是接受过来的,所以是有固定大小的,如果两个有序顺序表的长度总和大于新链表,则不能进行合并,这是需要返回false。

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

思想:先将一维数组中所有的元素都进行逆置,这样后n个元素就在前m个元素之前了,这个时候只需要对前n个元素和后m个元素分别逆置就可以完成将顺序表的位置互换了

代码如下:

void nizhi(int A[],int left,int right,int asize){
/*逆置子函数,用于逆置数组中规定起止位置中的元素*/
    if(left>=right||right>=asize)
        return false;
/*如果起始值大于终止值或者终止值大于数组的大小,则错误,不用逆置*/
    int i,temp;
/*设置中间变量*/
    for(i=0;i<(left+right)/2;i++){
        temp=A[left+i];
        A[left+i]=A[right+i];
        A[right+i]=temp;
    }
}

void huhuan(int A[],int m,int n,int asize){
    nizhi(A,0,m+n-1,asize);
/*将数组中m+n个元素整个逆置*/
    nizhi(A,0,n-1,asize);
/*将数组前n个元素逆置*/
    nizhi(A,0,n,n+m-1,asize);
/*将数组后m个元素逆置*/
}

课后答案:

typedef int DataType;
void Reverse(DataType A[],int left,int right,int arraySize){
    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){
    Reverse(A,0,m+n-1,arraySize);
    Reverse(A,0,n-1,arraySize);
    Reverse(A,n,m+n-1,arraySize);
}

思考:在for循环中的终止条件写错了,因为是子函数,所以交换的元素不是从头开始的,所以终止条件不应该是左右值的中间值,这样会过多的逆置元素。

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

思想:顺序存储的线性表递增有序,用折半法找值为x的元素用时最少。

示意图:

代码如下:

void Search_x(ElemType A[],ElemType x){
    int small=0,big=n-1,mid=(small+big)/2;
    while(small<=big){
/*找到元素x所在位置*/
        if(A[mid]==x) break;
/*找到x的位置*/
        else if(A[mid]<x) small=mid+1;
/*找到x在后半段,继续循环,对后半段进行折半查找*/
        else if(A[mid]>x) big=mid-1;
/*找到x在前半段,继续循环,对前半段进行查找*/
    }
    if(small>big){
/*如果没有找到x,则将所有大于x的元素往后移,将x插入big后一位*/
        for(int i=n-1;i>big;i--){
            A[i+1]=A[i];
        }
        A[i+1]=x;
    }
    else if(A[mid]==x&&mid!=n-1){
/*如果找到了元素x并且不位于最后一个元素的位置,则将其与之后继元素交换*/
        int temp=A[mid];
        A[mid]=A[mid+1];
        A[mid+1]=temp;
    }
/*如果找到元素x,但是x在最后一个位置,则不进行任何操作*/
}

课后答案:

void SearchExchangeInsert(ElemType A[],ElemType x){
    int low=0,high=n-1,mid;
    while(low<=high){
        mid=(low+high)/2;
        if(A[mid]==x) break;
        else if(A[mid]<x) low=mid+1;
        else high=mid-1;
    }
    if(A[mid]==x&&mid!=n-1){
        t=A[mid];
        A[mid]=A[mid+1];
        A[mid+1]=t;
    }
    if(low>high){
        for(i=n-i;i>high;i--) A[i+1]=A[i];
        A[i+1]=x;
    }
}

第十题:【2010年计算机联考真题】设将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或C++或Java语言描述算法,关键之处给出注释

(3)说明你所设计算法的时间复杂度和空间复杂度

(1)算法的基本设计思想:

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

Reverse(0,p-1)得到cbadefgh;

Reverse(p,n-1)得到cbaghfed;

Reverse(0,n-1)得到defghabc;

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

void Reverse(int R[],int start,int end){
    int i,temp;
    for(i=0;i<(end-start)/2;i++){
        temp=R[start+i];
        R[start+i]=R[end-i];
        R[end-i]=temp;
    }
}

void xunhuan(int R[],int n,int p){
    Reverse(0,p-1);/*得到cbadefgh*/
    Reverse(p,n-1);/*得到cbaghfed*/
    Reverse(0,n-1);/*得到defghabc*/
}

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

void Converse(int R[],int n,int p){
    Reverse(0,p-1);/*得到cbadefgh*/
    Reverse(p,n-1);/*得到cbaghfed*/
    Reverse(0,n-1);/*得到defghabc*/
}

(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)

参考数目:《2019年数据结构考研复习指导王道系列》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

箬渊凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值