王道考研408 数据结构 2.2线性表的表示练习题

本文详细介绍了关于顺序表的各种操作,包括删除最小值元素、元素逆置、删除特定值、删除范围内的元素、合并有序顺序表、查找并交换元素、找中位数以及寻找主元素和未出现的最小正整数等。这些操作均实现了高效的时间复杂度和空间复杂度解决方案,如二分查找和优化的空间利用。
pre
#define MaxSize 50
typedef struct{
    ElemType data[MaxSize];
    int length;
}SeqList;

2.2

2.2.01

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

bool Del_Min(SeqList &L, Elemtype &Value)//用于返回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 = L.data[i];
            pos = i;
        }
    }
    L.data[pos]=L.data[length-1];
    L.length--;
    return true;
}
2.2.02

设计一个高效算法将顺序表L内所有元素逆置且空间复杂度为O(1)

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

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

void del(SeqList &L,ElemType x)
{
    int k = 0;
    for(int i=0;i<L.length;i++){
        if(L.data[i]!=0){
            L.data[k]=L.data[i];
            k++;
        }
    }
    L.length = k;
    return;
}
2.2.05

从有序顺序表中删除其值在给定值s与t之间的所有元素,若s或t不合理或顺序表为空则显示出错信息并退出运行

bool del_sec(SeqList &L, Elemtype s, Elemtype t)//我这个更好(不是),我没按照有序写^^_
{
    int strt=-1,fnsh=-1;
    if(s>t||L.length == 0) return false;
    for (int i = 0; i<L.length;i++)
    {
     	   if(L.data[i]==s){
               strt = i;
               break;
           }
    }
    if(s==t&&strt!=-1) return true;
    for(int i = L.length-1; i>=0; i--){
        if(L.data[i]==t){
           	fnsh = i;
            break;
        }
    }
    if(strt==-1||fnsh==-1) return false;//不存在的地方
    for(int i = fnsh; i<L.length; i++){
      	L.data[strt+i+1-fnsh] = L.data[i];
    }
    L.length-=fnsh-strt+1;
}
2.2.07

将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结构顺序表

void merge(SeqList & L1, SeqList & L2, SeqList &L3)//将顺序表L1和顺序表L2合并为顺序表L3
{
    int p1=0, p2=0;//the position of the list
    for (int i = 0; i<L1.length+L2.length; i++){
		if(L1.data[p1]>=L2.data[p2]||(p1==L1.length-1&&p2!=L2.length-1))){
            L3.data[i]=L2.data[p2];
            p2++;
        }        
      	else{
            L3.data[i]=L1.data[p1];
            p1++;
        }
    }
    L3.length = L1.length+L2.length;
}
2.2.09

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

/*这里使用折半查找速度会更快*/
void insert(SeqList &L, int ins)//ins为查找的x元素,L为线性表
{
    int mid,temp;
    int low = 0, high = L.length-1;
    while(low<high){
        mid = (low+high)/2;
        if(L.data[mid]>ins) high = mid-1;
        else if(L.data[mid]<ins) low = mid+1;
        else break;
    }
    if(L.data[mid]==ins&&mid!=n-1){
        temp = L.data[mid];
        L.data[mid] = L.data[mid+1];
        L.data[mid+1] = temp;
    }
    if(low>high)//没找到捏
    {
        for(int i = n-1;i>high;i--)		L.data[i+1]=L.data[i];
        A[i+1] = ins;
    }
}
2.2.11

一个长度为L的升序序列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. 给出算法的基本思想

    在看到等长升序的时候可以想到两个数列提供的”权重“是相同的,如果在兼并后取中位数则时间复杂度和空间复杂度都为O(n)。结合折半查找的思路,可以将两个中位数进行比较,两个不一样则舍去小的里小的一半和大的里大的一半,再进行中位数比较直至出结果。这样的时间复杂度是O(log2n),空间复杂度就优化到O(1)了。

  2. 根据设计思想,给出代码

    我在写的时候没有考虑到奇数和偶数的情况,事实上这是相当重要的,在题干中给出的中位数是N/2,事实上我也是这么写的,但是完全可以优化,因为在进行过滤的时候,如果是奇数甚至可以把自己也过滤掉(不行!不要想了)。同时,如果两个在最后都拼剩下了一个,那么应该选最小的那个进行返回。不仅如此,我在写的时候突然想到,如果是用length进行除2操作的话,数组又是从0开始标的,是不是中位数应该是N/2-1?下面是答案,优化?优化个锤子!如果仅仅通过size和中位数的位置进行交互的话,将会变得十分困难。

ElemType find_mid(SeqList &L1, SeqList &L2)
{
    int s1=0,e1=L1.length-1,s2=0,e2=L2.length-1;//用于记录两个数列的起始和终结
    int m1,m2;//中位数位置
	while(s1!=e1||s2!=e2){//每次都减一样的,只留前面半部分都行
        m1 = (s1+e1)/2;
        m2 = (s2+e2)/2;
        if(L1.data[m1]==L2.data[m2]) return L1.data[m1];
        else if(L1.data[m1]>L2.data[m2]){
            if((e1-s1)%2==0)//当其中元素为奇数时
            {
                e1 = m1;
                s2 = m2;
			}
            else{//偶数的时候贪掉带中位数前面的一个
                e1 = m1;
                s2 = m2+1;
            }
        }
        else{
            if((e1-s1)%2==0){
                s1 = m1;
                e2 = m2;
            }
            else{
                s1 = m1+1;
              	e2 = m2;
            }
        }
    }
    return (L1.data[s1]<L2.data[s2])?L1.data[s1]:L2.data[s2];
}
2.2.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. 给出算法的基本设计思想

    因为超过一半的出现频次,所以可以用count的方法进行查找,如果重复则+1,如果很久没有相似的则换一个元素进行计算。此时时间复杂度为O(n),空间复杂度为O(1)

  2. 代码

int major(int A[],int n)//我发现之前都是,因为他是线性表,所以好像不用那么表达了,当然,用SeqList方法是完全一样的
{
    int  c, count=1;
    c = A[0];
    for(int i=0;i<n;i++){
        if(A[i]==c) count++;
        else if(count>0) count--;
        else{
            c = A[i];
            count = 1;
        }
    }
    if(count>0){
        count = 0;
         for(i = 0;i<n;i++) if(A[i]==c) count++;
    }
    if(count>n/2) return c;
    else return -1;
}
2.2.13

给定一个含n(n≥1)个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的最小正整数。

  1. 基本思想

    考虑到要尽可能高效,但是不遍历肯定是无法生存的,所以需要O(n)的时间复杂度,同时就空间复杂度考虑,此时好像只能O(n),建立一个正整数数组用于存储正整数,如果本来那个数组的大小为n,则建立一个n+1的数组即可(事实上,如果是n的数组好像也可以完成任务,因为遍历完都有后直接取n+1好像就行了,我的)。

  2. 代码

int smallint(int a[],int n)
{
    int *b;
    b = (int *)malloc(sizeof(int)*(n+1));//这个malloc分配的我之前从未用过,可以背一背
    memset(b,0,sizeof((int)*(n+1)));//memset的时间复杂度也是O(n)
    for(i=0;i<n;i++) if(a[i]>0&&a[i]<n) b[a[i]-1]=1;//这里只要标1就行了,要统计频次的话改为++
    for(i=0;i<n;i++) if(b[i]==0) break;
    return i+1;
}
2.2.14

定义三元组(a, b, c)的距离D=|a-b| + |b-c| + |c-a|,计算给定3个非空整数集合S1, S2和S3中所有可能的最小距离

  1. 基本设计思想

    由于是计算a,b,c之间的距离,并且是升序排列,因此在每次前进的时候将三个数中的最小值删去即可。同时考虑到当有一个数据不仅是这个数组的最大值并且是目前的三元组的最小值时,则没必要继续了。因此时间复杂度为O(n),空间复杂度为O(1)

  2. 代码

int calc_abs(int a)//返回绝对值
{
    if(a>=0) return a;
    else return (-a);
}
int short_dis(int a[],n1,int b[],n2,int c[],n3)
{
    int p=0,q=0,r=0;
    int dis,min = 0x7fffffff;
    while(p<n1&&q<n2&&r<n3&&min>0)//最小值为0也不用继续了
    {
        dis = calc_abs(a[p]-b[q])+calc_abc(a[p]-c[r])+calc_abc(b[q]-c[r]);
        if(dis<min) min = dis;
        if(a[p]<=b[q]&&a[p]<=c[r]) p++;
        else if(b[q]<=a[p]&&b[q]<=c[r]) q++;
        else r++;
    }
    return min;
}
后记

图书馆敲得笔记本都冒烟了,下个练习还有25道,难受

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值