顺序表抽象数据结构+综合运用题(历年考研题+核心运用题)

今天作者学习了顺序表的抽象数据结构,在考研资料上发现了好多关于顺序表的综合运用题,基本上都是历年的考研题,所以今天不妨来着重探讨一下。

顺序表的抽象数据结构

抽象数据结构包括了顺序表存储结构的定义,数据元素之间的关系,还有定义在数据元素长的基本操作。由于基本操作都比较简单容易实现,所以就不再单个重述。比较值得注意的是顺序表的插入和删除操作,并非严格意义上的插入删除,只是数组值的覆盖而已。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define ElemType int
#define OK 1
#define FALSE -1
#define ERROR  -2
#define  Status int
#define MAXSIZE 100

//顺序表存储结构定义
typedef struct
{
    ElemType *elem;//定义一个数组基地址
    int length;//length表示顺序表的长度
}SqList;
//初始化顺序表
Status InitList(SqList &L)
{
    L.elem = new ElemType[MAXSIZE];//为elem申请一个长度为MAXSIZE的空间
    if(L.elem==NULL)//如果申请空间失败
        return ERROR;
    L.length=0;//初始化顺序表的长度为0
    return OK;
}
//求顺序表的表长
int GetLength(SqList L)
{
    return L.length;
}
//判断顺序表是否为空
int ListEmpty(SqList L)
{
    if(L.length==0)
        return 1;
    return 0;
}
//取出顺序第i个元素的值并传给e
Status GetValue(SqList L,int i,ElemType &e)
{
    if(i<=0||i>=L.length)//如果位置i不合理
        return ERROR;
    e=L.elem[i-1];//将下标为i-1的数组元素赋值给e
    return OK;
}
//判断元素e第一次出现的位置i,这里并不是e所对应的下标,位置i值=下标值+1
int LacatElem(SqList L,ElemType e)
{
    if(L.length==0)//如果为空
        return ERROR;
    int i,n;
    n=L.length;
    for(i=0;i<n;i++)
        if(L.elem[i]==e)
        return i+1;//位置i值=下标值+1
    return 0;
}
在第i个位置插入元素e,对应的下标为i-1
Status InsertList(SqList &L,int i,ElemType e)
{
    int n;
    n=L.length;
    if(i<1&&i>n+1)//如果插入位置不合理
        return ERROR;
    if(n==MAXSIZE)//如果表已满
        return ERROR;
    //插入实际上就是依次从后向前将数组元素往后移动一个单位,
    //再将插入位置的元素值赋值为e,最后不要忘记表长+1
    int j;
    for(j=n-1;j>=i-1;j--)
    {
        L.elem[j+1]=L.elem[j];//向后移动
    }
    L.elem[i-1]=e;//将插入位置的元素值赋值为e
    L.length++;//表长+1
    return OK;
}
//删除第i个数组元素
Status DeleteList(SqList &L,int i)
{
    int n;
    n=L.length;
    if(i<1||i>n)//如果删除位置不合理
        return ERROR;
   //顺序表的删除不是真正的删除,只是将该删除的元素进行覆盖而已
   //从前往后将数组元素向前移动一个单位,最后不要忘记表长减1
    int j;
    for(j=i;j<n;j++)
        L.elem[j-1]=L.elem[j];
    L.length--;//从前往后将数组元素向前移动一个单位
    return OK;//表长减1
}
//删除所有值为e的数据元素
Status DeleteElem(SqList &L,ElemType e)
{
    int n,i;
    n=L.length;
    if(n==0)//如果是空表
        return ERROR;
    for(i=0;i<n;i++)//进行一次从前往后遍历
    {
        if(L.elem[i]==e)
            DeleteList(L,i+1);//函数DeleteList传入的i+1为位置值而非下标值
    }
    return 1;
}
//从键盘输入一组数据,以0结尾,创建一个顺序表
Status CreatSqList(SqList &L)
{
    ElemType x;
    int i=1;
    cin>>x;
    while(x!=0)
    {
        InsertList(L,i,x);
        i++;
        cin>>x;
    }
    return OK;
}
//顺序表的遍历输出
void DisplayList(SqList L)
{
    int i,n;
    n=L.length;
    printf("输出此顺序表为:");
    for(i=0;i<n;i++)
        printf("%d ",L.elem[i]);
    printf("\n");
}

行了,话不多说,来看看顺序表的综合运用题吧。

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

思路分析

扫描顺序表L的前半部分,对于数组元素L.elem[i(0<=i<L.length/2),将其与后半部分对应元素L.elem(length-i-1)进行交换

代码实现
void Reverse(SqList &L)
{
    int i=0;
    ElemType temp;//做为打擂交换中间值
    while(i<L.length/2)
    {
        temp = L.elem[i];
        L.elem[i] = L.elem[L.length-i-1];
        L.elem[L.length-i-1] = temp;
        i++;//i自增遍历
    }
}
相应主函数实现
int main()
{
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    Reverse(L);//顺序表逆置函数
    DisplayList(L);//遍历输出
}
算法分析

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

Top2:设计一个算法将L中的所有小于0的数组元素放在前半部分,大于等于0的数组元素放在后半部分

思路分析

从L的两端开始查找,前端找大于等于0的元素(位置为i),后端找小于0的元素(位置为j),然后将这两个元素交换。

代码实现
void MoveZero(SqList &L)
{
    ElemType temp;
    int i=0,j=L.length-1;
    //i从前往后遍历,j从后往前遍历
    while(i<j)
    {
        while(i<j&&L.elem[i]<0)//一定要确保i<j
            i++;//i自增
        while(i<j&&L.elem[j]>=0)//一定要确保i<j
            j--;//j自减
        if(i<j)//一定要确保i<j
        {
            temp = L.elem[i];
            L.elem[i] = L.elem[j];
            L.elem[j] = temp;
        }
    }
}
相应主函数实现
int main()
{
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    MoveZero(L);
    DisplayList(L);//遍历输出
}
算法分析

这是作者第一次想到的算法,不知道是不是最优的算法,不过本问题不是很难,首尾同时遍历应该是最优化的算法了。

Top3:将顺序表L以第一个元素为分界线,将所有小于等于它的元素移到该元素的前面,将所有大于它的元素移到该元素的后面

思路分析

把第一个元素所在的位置标记为flag,i从1开始遍历,到L.length-1为止,如果出现L.elem[i]<=L.elem[flag],交换这两个位置的值,并且flag更新为i值,i++。

代码实现
void Move_FirstElem(SqList &L)
{
    ElemType temp;
    int flag=0,i=1;
    while(i<L.length)
    {
        if(L.elem[i]<=L.elem[flag])//如果出现L.elem[i]<=L.elem[flag]
        {
            temp = L.elem[i];
            L.elem[i] = L.elem[flag];//交换这两个位置的值
            L.elem[flag] = temp;
        }
        flag = i ;//flag更新为i值
        i++;//i++
    }
}
相应主函数实现
int main()
{
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    Move_FirstElem(L);
    DisplayList(L);//遍历输出
}
算法分析

上述算法的时间复杂度为O(n),空间复杂度为O(1)。作者参考资料题解发现还有两种算法都和上述算法非常相似,但是思路上比这个稍微麻烦,所以就不做介绍了,如果有新思路欢迎下方评论哟。

Top4:已知一个长度为n的线性表L采用顺序存储结构,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,删除线性表中所有值为x(ElemType型)的数据元素

思路分析

前文中关于顺序表的基本操作中也有删除顺序表中所有值为x的函数,但是那个DeleteElem(SqList &L,ElemType e)函数时间复杂度为O(n*n),不符合题意。我的分析如下:不妨用k来记录顺序表L中不等于x的数据元素的个数,这将是新的顺序表的长度。i从表头开始往后遍历,当L.elem[i]!=x时,L.elem[k]赋值为L.elem[i],同时i++,k++;如果L.elem[i]==x,则i++,k不变。原理是把要删除的数据元素的值进行覆盖。最后还要将顺序表的长度变为k,即L.length=k。

代码实现
void DeleListElem(SqList &L,ElemType x)
{
    int k=0,i=0;
    while(i<L.length)//i从表头开始往后遍历
    {
        if(L.elem[i]!=x)//当L.elem[i]!=x时
            L.elem[k++] = L.elem[i];//L.elem[k]赋值为L.elem[i],同时i++,k++
        i++;//否则i++,k不变
    }
    L.length = k;//将顺序表的长度变为k
}
相应主函数实现
int main()
{
    ElemType x;
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    printf("请输入要删除的元素值为:");
    cin>>x;
    DeleListElem(L,x);
    DisplayList(L);//遍历输出
}
算法分析

上述算法的时间复杂度为O(n),空间复杂度为O(1)。最富有特色的地方就是采用向前覆盖的方法进行遍历,使得时间复杂度降为O(n)。

Top5:一线性表L长度为n,试设计一个尽可能高效的算法,删除线性表中所有元素值处于x与y之间的(包括x和y)的数据元素

思路分析

和top4的算法一样,只是判断条件改了而已,这里不再重述。

代码实现
void DeleListElem1(SqList &L,ElemType x,ElemType y)
{
    int k=0,i=0;
    while(i<L.length)//i从表头开始往后遍历
    {
        if(!(L.elem[i]>=x&&L.elem[i]<=y))//当L.elem[i]!=x时
            L.elem[k++] = L.elem[i];//L.elem[k]赋值为L.elem[i],同时i++,k++
        i++;//否则i++,k不变
    }
    L.length = k;//将顺序表的长度变为k
}
相应主函数实现
int main()
{
    ElemType x,y;
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    printf("请输入要删除的元素区间为:");
    cin>>x>>y;
    DeleListElem1(L,x,y);
    DisplayList(L);//遍历输出
}
算法分析

采用top4的算法,算法分析这里不再重述。

Top6:设有线性表L=(a1,a2,a3,,,,an)其中n>=3,设计一个时间复杂度为O(n),空间复杂度为O(1)的算法,将L中所有奇数序号的元素移到所有偶数序号元素的前面

思路分析

首尾同时遍历,i从a第一个偶数序号的元素开始遍历(即i=1起),j从最后一个奇数序号的元素开始遍历(这要分情况讨论),交换两者的值,同时向中间移动,移动步长为2,当i>=j时跳出循环

代码实现
void SwapSqList(SqList &L)
{
    ElemType temp;
    int i=1,j;//i是第一个偶数序号元素
    //给j赋初值
    if(L.length%2==0)
        j = L.length-2;//j是最后一个奇数序号元素
    else
        j = L.length-1;//j是最后一个奇数序号元素
    while(i<j)
    {
        temp = L.elem[i];
        L.elem[i] = L.elem[j];
        L.elem[j] = temp;
        i = i+2;//i自增两个单位
        j = j-2;//j自减两个单位
    }
}
相应主函数实现
int main()
{
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    SwapSqList(L);
    DisplayList(L);//遍历输出
}
算法分析

本算法扫描L中接近一半的元素,所以时间复杂度为O(n),仅仅定义了三个临时变量,所以空间复杂度为O(1)。

Top7:给定一个有n个元素的顺序表L,其中连续的相等元素构成的子序列称为平台,设计算法求顺序表L的最长平台的长度

思路分析

用maxlen保存最长平台的长度(初值为1),len保存当前平台的长度(初值也为1),用i扫描顺序表L并累计当前平台的长度。当maxlen<len时,将maxlen赋值为len,最后返回maxlen。

代码实现
int GetMaxLen(SqList L)
{
    int maxlen=1,len=1,i;
    for(i=1;i<L.length;i++)
    {
        if(L.elem[i]==L.elem[i-1])
            //如果当前元素与前一元素相等,当前平台长度加一
            len++;
        else//如果不相等
        {
            if(maxlen<len)//考虑是否要更新最大平台的长度
                maxlen=len;
            len=1;//不管是否更新maxlen,len都要归为初始化值
        }
    }
    return maxlen;
}
相应主函数实现
int main()
{
    SqList L;//定义一个顺序表L
    InitList(L);//初始化一个顺序表L
    CreatSqList(L);//创建一个顺序表L
    printf("最长平台的长度为:%d\n",GetMaxLen(L));
}
算法分析

哈哈,这个题和作者之前做的浙江大学的数据结构题中的求最大子列的算法神似,还可以应用的求字符串的最长子串,好多好多相似的题目中去。

Top8:设将n个整数存放到顺序表中,设计一个时间和空间上尽可能高效的算法,将L的整数序列循环左移p个单位

思路分析

emmm。。。作者有些困了,改日再补上。欢迎读者在线评论哟,虽然机会很渺茫。

代码实现
在这里插入代码片
相应主函数实现
在这里插入代码片
算法分析

顺序表总结

1.顺序表逻辑上相邻的元素再物理结构上也是相邻的,具有随机存取的特性;
2.顺序表的存储密度为1,存储密度比链式表要高;
3.顺序表插入和删除元素要大量移动元素;
4.顺序表的基本操作比较简单,算法设计也相对容易,作者是数据结构初学者,对代码的设计还不是很应用自如,难免会有不尽人意的地方,还望包涵。

作者practical_sharp
作者也是数据结构的初学者,代码和理论有不正确不规范的地方还望见谅,不喜勿喷,欢迎评论交流

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值