手把手学计科--数据结构第二章-顺序表与链表(内含可执行代码)

  1. 线性表的定义和特点
    1.1 引入
  • 线性表、栈、队列、串和数组都是线性结构。
  • 线性结构的特点:除第一个元素无直接前驱、最后一个元素无直接后继之外,其他每个数据元素都有一个前驱和后继。
    1.2 线性表
  • 定义:由n个数据特性相同的元素构成的有限序列。
  • 当线性表中的个数n(线性表的长度)为0时称之为空表。
  • 特点:
    (1) 存在唯一的一个被称作“ 第一个" 的数据元素;
    (2) 存在唯一的一个被称作“ 最后一个" 的数据元素;
    (3) 除第一个之外, 结构中的每个数据元素均只有一个前驱;
    (4) 除最后一个之外,结构中的每个数据元素均只有一个后继。
  • 线性表中的关系是一对一的。
  1. 案例引入
    2.1 一元多项式的运算
    顺序结构线性表
    2.2 稀疏多项式的运算
    链式存储结构
    补充的是:一个指针占8个字节。
    线性表中数据元素的类型可以为简单类型,也可以为复杂类型。
    许多实际应用问题所涉的基本操作有很大相似性,不应为每个具体应用单独编写一个程序。
    从具体应用中抽象出共性的逻辑结构和基本操作(抽象数据类型),然后实现其存储结构和基本操作。
  2. 线性表的类型定义
  • 线性表抽象数据类型List
    = 线性表中元素的逻辑结构 + 基本运算定义
    [图片]
    1)InitList (&L)
    操作结果:构造一个空的线性表L。
    2)DestroyList(&L)
    初始条件:线性表L已存在。
    操作结果:销毁线性表L。
    3)ClearList (&L)
    初始条件:线性表L已存在。
    操作结果:将L重置为空表。
    4)ListEmpty(L)
    初始条件:线性表L已存在。
    操作结果:若L为空表, 则返回true, 否则返回false。
    5)ListLength(L)
    初始条件:线性表L已存在。
    操作结果:返回L中数据元素个数
    6)GetElem(L,i,&e)
    初始条件:线性表L已存在, 且1≤i≤ListLength(L)。
    操作结果:用e返回L中第i个数据元素的值。
    7)LocateElem(L,e)
    初始条件:线性表L已存在。
    操作结果:返回L中第1个值与e相同的元素在L中的位置。若这样的数据元素不存在,则返回值为0。
    8)PriorElem(L,cur_e,&pre_e)
    初始条件:线性表L已存在。
    操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回其前驱,否则操作失败,pre_e无定义。
    9)NextElem(L,cur_e,&next_e)
    初始条件:线性表L已存在。
    操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回其后继,否则操作失败,next_e无定义。
    10)ListInsert(&L,i,e)
    初始条件: 线性表L已存在,且1 ≤i≤ ListLength (L) +1。
    操作结果:在L中第i个位置之前插入新的数据元素e, L的长度加1。
    11)ListDelete(&L,i)
    初始条件:线性表L 已存在且非空,且1≤i≤ ListLength(L)。
    操作结果:删除L的第i个数据元素,L的长度减1。
    12)TraverseList(L)
    初始条件:线性表L已存在。
    操作结果:对线性表L进行遍历,在遍历过程中对L的每个结点访问一次。

```cpp
#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 100
#define ERROR 0
const int OVERFLOW = -1; // 溢出错误码
const int OK = 0; // 操作成功码
typedef struct { //定义顺序表
    int *elem;
    int length;
}List;
int n, m;
int e;
int pre_e;
char st = 'A';

bool InitList(List &L) //创建顺序表
{
    L.elem = new int[MAXSIZE];
    if(!L.elem) exit(OVERFLOW); //存储分配失败退出
    L.length = 0;
    return OK;
}

void DestroyList(List &L) {//摧毁线性表
    delete[] L.elem;
    L.elem = nullptr;
    L.length = 0;
}
void ClearList(List &L) {//清空线性表
    L.length = 0;
}
bool isEmpty(const List& L) {//判断是否为空
    return L.length == 0;
}

int ListLength(List L) //求顺序表的长度
{
    return L.length;
}

int GetElem(List L, int i, int &e) //求顺序表的第i个元素,并以e返回
{
    e = L.elem[i - 1];
    return e;
}

int LocateElem(List L, int e) //判断List里有没有e这个元素,并返回位置
{
    int i;
    for (i = 0; i < L.length; i++)
        if (e == L.elem[i])
            return i + 1; // 返回位置(从1开始计数)
    return 0; // 若找不到匹配的元素,则返回0
}

void ListInsert(List &L, int e) //将e插入到List的最后
{
    L.elem[L.length] = e;
    L.length++;
}
bool PriorElem(List L, int cur_e, int* pre_e) // 判断cur_e是否是L的数据元素,且不是第一个,返回其前驱pre_e
{
    int i;
    for (i = 1; i < L.length; i++)
        if (cur_e == L.elem[i])
        {
            *pre_e = L.elem[i - 1]; // 将前驱赋值给pre_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是第一个元素,则操作失败
}
bool NextElem(List L, int cur_e, int* next_e) // 判断cur_e是否是L的数据元素,且不是最后一个,返回其后继next_e
{
    int i;
    for (i = 0; i < L.length - 1; i++)
        if (cur_e == L.elem[i])
        {
            *next_e = L.elem[i + 1]; // 将后继赋值给next_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是最后一个元素,则操作失败
}
void ListDelete(List &L, int i) // 删除L的第i个元素,L的长度-1
{
    if (i < 0 || i >= L.length) {
        // 索引越界,无法删除元素
        return;
    }

    for (int j = i; j < L.length - 1; j++) {
        L.elem[j] = L.elem[j + 1];
    }
    L.length--;
}
void ListOutput(List L) //输出List
{
    int i;
    for (i = 0; i < L.length; i++)
        cout << L.elem[i] << " ";
    cout << endl;
}
void ListTraverse(List &L) // 遍历线性表L,访问每个节点一次
{
    for (int i = 0; i < L.length; i++) {
        // 在这里可以对节点进行操作,例如输出节点的值
        cout << L.elem[i] << " ";
    }
    cout << endl;
}
int main() {
    List LA;
    InitList(LA);
    ClearList(LA);
    isEmpty(LA);
    ListInsert(LA, 1);
    ListInsert(LA, 2);
    ListInsert(LA, 2);
    ListInsert(LA, 3);
    cout<<"查看当前线性表:"<<endl;
    ListOutput(LA);
    cout<<"线性表LA的长度是:";
    cout<<ListLength(LA)<<endl;
    cout << "第3个元素的值是:" <<GetElem(LA,3,e)<< endl;
    cout<<"第一个与e相同的元素的位置是:"<<LocateElem(LA,e)<<endl;
    int cur_e = 2; 
    int pre_e; // 用于存储前驱元素的值
    int next_e;
    bool success = PriorElem(LA, cur_e, &pre_e); // 调用PriorElem函数
    if (success) {
        cout << "前驱元素是:" << pre_e << endl;
    } else {
        cout << "操作失败" << endl;
    }
    bool succ = NextElem(LA, cur_e, &next_e);
    if (succ) {
        cout << "后继元素是:" << next_e << endl;
    } else {
        cout << "操作失败" << endl;
    }
    int to_delete;
    cout << "请输入要删除的元素的值:" << endl;
        cin >> to_delete;
    ListDelete(LA,to_delete);
    cout << "此时的线性表为: "<<endl;
    ListOutput(LA);
    cout<<"遍历当前线性表的结果是:"<<endl; 
    ListTraverse(LA);
    DestroyList(LA); //销毁
    return 0;
}

```cpp
#include<iostream>
#include<string.h>
#define MAXSIZE 100
#define OVERFLOW -2
#define OK 1
#define ERROR 0
typedef int Status;
using namespace std;
typedef struct
{
        int *elem;//指向数据元素的基地址 
        int length;  //线性表当前长度 
}SqList;
//初始化
Status InitList(SqList &L)
{
        L.elem=new int[MAXSIZE];
        if(!L.elem) exit(OVERFLOW);
        L.length=0;
        return OK;
 } 
 
//创建顺序表
 Status ListCreat(SqList &L,int j,int e)
 {
         //合法性判断j<0或j>MAXSIZE
        if(j<0||j>MAXSIZE) return ERROR;
        if(L.length==MAXSIZE) return OVERFLOW;
        L.elem[j]=e;
        //更新长度
        L.length++; 
        return OK;
  }  
  
//按值查找
Status LocateElem(SqList L,int f)
{
        int i;
        for(i=0;i<L.length;i++)
        {
                if(L.elem[i]==f)
                {
                        cout<<L.elem[i]<<"的位置是:"<<i+1<<endl;
                        return OK;
                }
        }
        return ERROR;
 }   
 //按位查找
 Status GetElem(SqList L,int i,int &e)
 {
         if(i<1||i>L.length) return ERROR;
        e=L.elem[i-1]; 
         return OK;
  } 
//插入
Status ListInsert_Sq(SqList &L,int i,int e)
{
        int j;
        if((i<1)||(i>L.length+1)) return ERROR;
        if(L.length==MAXSIZE) return ERROR;
        for(j=L.length-1;j>=i-1;j--)
        {
                L.elem[j+1]=L.elem[j];
        }
        L.elem[i-1]=e;
        ++L.length;
        return OK;
 } 
 
//按位删除操作
Status ListDelete_Sq(SqList &L,int i)
{
        int j;
        if((i<1)||(i>L.length)) return ERROR;
        for(j=i;j<=L.length-1;j++)
        {
                L.elem[j-1]=L.elem[j];        
        }
        --L.length;
        return OK;
 } 
//求表长
Status ListLength(SqList &L)
{
        if(L.length==0) return 0;
        return L.length;
 } 
 //清空
void ClearList(SqList &L) 
 {
         L.length=0;
 }
//判空表
Status ListEmpty(SqList &L)
{
        if(L.length==0) return 1;
        return 0;
}
//重复元素位置的判断
int LocateElem_s(SqList &L, int e) //判断List里有没有e这个元素,并返回位置
{
    int i;
    for (i = 0; i < L.length; i++)
        if (e == L.elem[i])
            return i + 1; // 返回位置(从1开始计数)
    return 0; // 若找不到匹配的元素,则返回0
} 
//销毁顺序表
void DestroyList(SqList &L)
{
        int a;
        cout<<"是否销毁顺序表(1/0):"<<endl;
        cin>>a;
        if(a==1)
        {
                L.length=0;
                delete[] L.elem;
                cout<<"顺序表已销毁"<<endl; 
         } 
 } 
bool PriorElem(SqList &L, int cur_e, int* pre_e) // 判断cur_e是否是L的数据元素,且不是第一个,返回其前驱pre_e
{
    int i;
    for (i = 1; i < L.length; i++)
        if (cur_e == L.elem[i])
        {
            *pre_e = L.elem[i - 1]; // 将前驱赋值给pre_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是第一个元素,则操作失败
}
bool NextElem(SqList &L, int cur_e, int* next_e) // 判断cur_e是否是L的数据元素,且不是最后一个,返回其后继next_e
{
    int i;
    for (i = 0; i < L.length - 1; i++)
        if (cur_e == L.elem[i])
        {
            *next_e = L.elem[i + 1]; // 将后继赋值给next_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是最后一个元素,则操作失败
}

  
  
int main()
{
        int i,f,j,k,m,n;
        int e,g,h,s;
        SqList L;
        //初始化 
        InitList(L);
        cout<<"请输入数列总数:";
        cin>>n;
        cout<<"请依次输入序列的值:"<<endl; 
        //根据用户输入人数及信息创建顺序表 
        for(i=0;i<n;i++)
        {
                cin>>e;
                ListCreat(L,i,e);
        }
        cout<<"目前表中有"<<L.length<<"个元素如下:"<<endl; 
        for(i=0;i<n;i++)
        {
                cout<<L.elem[i]<<endl;
        }
        //按值查找 
        cout<<"请输入元素的值:";
        cin>>f;
        LocateElem(L,f);
        //按位置查找
        cout<<"请输入待查元素的序号:";
        cin>>j;
        GetElem(L,j,g);
        cout<<"查找到的元素是:"<<g<<endl; 
        //插入
        cout<<"插入操作——请输入元素的位置和元素:"<<endl;
        cin>>k>>h;
        ListInsert_Sq(L,k,h);
        cout<<"插入操作后的所有元素如下:"<<endl;
        for(i=0;i<L.length;i++)
        {
                cout<<L.elem[i]<<endl;
        }
        //求第一个与某元素相同的元素
        cout<<"输入重复元素的位置:"<<endl;
        cin>>s;
                cout<<"第一个与"<<s<<"相同的元素的位置是:"<<LocateElem_s(L,s)<<endl; 
        //求前驱后继 
        int cur_e;
                cout<<"请输入要求前驱和后继的元素:"<<endl;
                cin>>cur_e; 
            int pre_e; // 用于存储前驱元素的值
            int next_e;
            bool success = PriorElem(L, cur_e, &pre_e); // 调用PriorElem函数
            if (success) {
                cout << "ta的前驱元素是:" << pre_e << endl;
            } else {
                cout << "求前驱操作失败" << endl;
            }
            bool succ = NextElem(L, cur_e, &next_e);
            if (succ) {
                cout << "ta的后继元素是:" << next_e << endl;
            } else {
                cout << "求后继操作失败" << endl;
            }
                 
        //按位删除操作        
        cout<<"请输入删除的位序:";
        cin>>m; 
        ListDelete_Sq(L,m);
        cout<<"按位删除操作后的所有元素的信息如下:"<<endl;
        for(i=0;i<L.length;i++)
        {
                cout<<L.elem[i]<<endl;
        }
        //求表长
        int len=ListLength(L);
        cout<<"顺序表的表长"<<len;        
        //清空
        ClearList(L);        
        //判空表
        if(ListEmpty(L)) 
                cout<<"这是一个空表。";
        else
                cout<<"这不是空表。";
        //销毁顺序表
        DestroyList(L);        
        return OK;
}
  • 求前驱的单独函数:
#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 100
const int OVERFLOW = -1; // 溢出错误码
const int OK = 0; // 操作成功码
typedef struct { //定义顺序表
    int *elem;
    int length;
}List;
char st = 'A';
bool InitList(List &L) //创建顺序表
{
    L.elem = new int[MAXSIZE];
    if(!L.elem) exit(OVERFLOW); //存储分配失败退出
    L.length = 0;
    return OK;
}
void ListInsert(List &L, int e) //将e插入到List的最后
{
    L.elem[L.length] = e;
    L.length++;
}
bool PriorElem(List L, int cur_e, int* pre_e) // 判断cur_e是否是L的数据元素,且不是第一个,返回其前驱pre_e
{
    int i;
    for (i = 1; i < L.length; i++)
        if (cur_e == L.elem[i])
        {
            *pre_e = L.elem[i - 1]; // 将前驱赋值给pre_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是第一个元素,则操作失败
}
bool NextElem(List L, int cur_e, int* next_e) // 判断cur_e是否是L的数据元素,且不是最后一个,返回其后继next_e
{
    int i;
    for (i = 0; i < L.length - 1; i++)
        if (cur_e == L.elem[i])
        {
            *next_e = L.elem[i + 1]; // 将后继赋值给next_e
            return true; // 操作成功
        }
    return false; // 若找不到匹配的元素或cur_e是最后一个元素,则操作失败
}
void ListOutput(List L) //输出List
{
    int i;
    for (i = 0; i < L.length; i++)
        cout << L.elem[i] << " ";
    cout << endl;
}
int main() {
    List LA;
    InitList(LA);
    ListInsert(LA, 1);
    ListInsert(LA, 2);
    ListInsert(LA, 3);
    ListInsert(LA, 4);
    cout<<"线性表为:"<<endl; 
    ListOutput(LA);
    int cur_e; 
    cout<<"请输入在线性表中的一个元素:"<<endl;
    cin>>cur_e; 
    int pre_e; // 用于存储前驱元素的值
    int next_e;
    bool success = PriorElem(LA, cur_e, &pre_e); // 调用PriorElem函数
    if (success) {
        cout << cur_e<<"的前驱元素是:" << pre_e << endl;
    } else {
        cout << "求前驱操作失败" << endl;
    }
    bool succ = NextElem(LA, cur_e, &next_e);
    if (succ) {
        cout<<cur_e << "的后继元素是:" << next_e << endl;
    } else {
        cout << "求后继操作失败" << endl;
    }
    return 0;
}
  1. 单链表的定义和表示
  • 单链表中指针的常用操作:
    • 结点的赋值:
      LNode *p;
      p=new LNode;
      p->data=20;//数据域
      p->next=null;//指针域
    • 指针的指向:(把等号读作指向更方便理解,特别注意在同一个链时不要把中间链弄丢)
      q=p;//q和p指向同一地址
      q=p->next;//q指向p的下一个地址
      q->next=p;q的下一个元素和p指向同一个元素
    • 关于指针类型的定义:
      char *p;//指针的类型是一致的,只是指针指向元素的类型不一致
      int *q;
      LNode *l;//结点类型的指针
  • 防止链丢失的重要顺序:
    [图片]
  • 关于单链表中的循环:
    • 初始化:
      Linklist L;
      p=L->next;//p指向第一个元素
      p=l;//p指向第0个元素
    • 循环体的执行条件:
      while(p!=null) while§//处理到第n号元素
      while(p->next)处理到第n-1号元素
  • 单链表的相关操作:
#include<bits/stdc++.h> 
#define OVERFLOW -2
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
using namespace std;
//描述结构指针 
typedef struct LNode 
{
        ElemType data;//结点的数据域 
        struct LNode *next;//指针域 
}LNode,*LinkList;
//初始化
Status InitList(LinkList &L)
{
        L=new LNode;
        L->next=nullptr;
        return OK;
}
//输出封装
 void outputList(LinkList L)
{
        LNode *p=L->next;
        while (p)
        {
                cout<<p->data<<" ";
                p=p->next;
        }
        cout << endl;
} 
//按值查找 
Status GetElem(LinkList L,int i,ElemType &e)
{
        LNode *p=L->next;
        int j=1;//初始化p指向首元结点,计数器j的初值赋值为1
        while(p&&j<i)
        {
                p=p->next;
                ++j;
         } 
        if(!p||i<1||i>j) return ERROR;//i的值太大或者太小 
        e=p->data;
         return OK; 
}
//按地址查找
int LocateElem(LinkList L,ElemType e)
{
          LNode *p=L->next;//p指向首元结点
          int i=1;
         while(p&&p->data!=e)
         {
                 p=p->next;
                 i++;
          } 
         return i;
   } 
   //插入
   Status ListInsert(LinkList &L,int i,ElemType e)
   {
           LNode *p = L;
    int j = 0;
           while(p&&j<i-1)
        {
                p=p->next;
                ++j;
         } 
         if(!p||i<1) return ERROR;
         LNode *s=new LNode;
         s->data=e;
         s->next=p->next;
         p->next=s;
         return OK;
        } 
        //单链表的后插操作
        bool InsertNode(LNode *p,ElemType e)
        {
                if(p==NULL) return false;
//                LNode *s=(LNode *)malloc(sizeof(LNode));
                LNode *s=new LNode;
                if(s==NULL) return false;
                s->data=e;
                s->next=p->next;
                p->next=s;
                return true;                
         } 
         //单链表的前插操作
         bool InsertPriortNode(LNode *p,ElemType e)
        {
                if(p==NULL) return false;
//                LNode *s=(LNode *)malloc(sizeof(LNode));
                LNode *s=new LNode;
                if(s==NULL) return false;
                s->data=e;
                s->next=p->next;
                p->next=s;
                s->data=p->data;//将p中的元素复制到s中
                p->data=e;//p中的元素覆盖为e 
                return true;                
         } 
        //求表长
        int ListLength(LNode *L)
        {
                LNode *p=L->next;
                int sum=0;
                while(p)
                {
                        sum++;
                        p=p->next;
                }
                return sum;
         } 
         //删除单链表中的元素
         Status ListDelete(LinkList &L,int i)
         {
                 LNode *p=L;
                 int j=0;
                 while((p->next)&&(j<i-1))
                 {
                         p=p->next;
                         ++j;
                 }
                 if(!(p->next)||(i<1)) return ERROR;
                 LNode *q=p->next;
                 p->next=q->next;
                 delete q;
                 return OK; 
          } 
         //销毁单链表
        Status DestroyList(LinkList &L)
        {
            LinkList p = L;
            while (p != nullptr)
            {
                L = p->next;
                delete p;
                p = L;
            }
            return OK;
        }

 
int main()
{
        LinkList L;
        //初始化
        InitList(L); 
        //输入单链表 
        int n;
        cout<<"请输入单链表的长度:"<<endl;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
                int e;
                 cout << "请输入第" << i << "个元素:";
        cin >> e;
        if (ListInsert(L, i, e) == ERROR)
        {
            cout << "插入失败!" << endl;
            break;
        }        
        }
        //取值
        int a,b;
        cout<<"请输入要查找的元素的序号:"; 
        cin>>a;
        GetElem(L,a,b);
        cout<<"该序号的元素是:"<<b<<endl; // 修改这里,将n改为b
        //按值查找
        int j;
        cout<<"请输入要查找的元素的值:";
        cin>>j;
//         LNode *result=LocateElem(L,j);
         cout<<"元素"<<j<<"的序号为:"<< LocateElem(L,j)<<endl;
//         if(result)
//        cout<<"找到元素值为"<<j<<"的节点"<<endl;
//        else
//         cout<<"未找到元素值为"<<j<<"的节点"<<endl;
         //插入
         int g,h;
        cout<<"请输入要插入的位置和值:"<<endl;
        cin>>g>>h;
        ListInsert(L,g,h);
        outputList(L); 
        //前插
        int r,k;
        cout<<"请输入要前插的元素的位置和值;"<<endl;
        cin>>r>>k;
        LNode *p =L;
    for (int i = 0; i < r-1  && p != NULL; i++) {
        p = p->next;
    }
    if (p == NULL) {
        cout << "插入位置不合法" << endl;
        return 0;
    }
    if (InsertPriortNode(p, k)) {
        cout << "前插插入成功" << endl;
        outputList(L); 
    } else {
        cout << "前插插入失败" << endl;
    }
        //后插
        int r_h,k_h;
         cout<<"请输入要后插的元素的位置和值;"<<endl;
         cin>>r_h>>k_h;
        LNode *o = L;
    for (int q = 0; q < r-1  && o != NULL; q++) {
        o = o->next;
    }
    if (o == NULL) {
        cout << "插入位置不合法" << endl;
        return 0;
    }
    if (InsertNode(o->next, k_h)) {
        cout << "后插插入成功" << endl;
        outputList(L); 
    } else {
        cout << "后插插入失败" << endl;
    } 
        //输出查看
        outputList(L); 
        //求表长
         cout<<"表长为:"<<ListLength(L)<<endl;
        //删除
        int d;
        cout<<"输入要删除的元素位置:"<<endl;
        cin>>d;
        ListDelete(L,d);
        cout<<"此时的线性表为:"<<endl; 
        outputList(L);
        //销毁单链表
        DestroyList(L); 
     if (DestroyList(L)) { 
        cout << "链表已经被摧毁" << endl;
     } else {
        cout << "链表尚未被摧毁" << endl;
     }
}

注:前插的操作存在一点问题。
4.1 求前驱求后继

//求前驱
    Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e)
    {
            LinkList p=L->next;
            while((p->next)&&(p->next->data!=cur_e))
            p=p->next;
            if(!(p->next)) return ERROR;
            pre_e=p->data;
            return OK;
     } 
     //求后继
     Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e)
    {
            LinkList p=L->next;
            while((p->next)&&(p->data!=cur_e))
            p=p->next;
            if(!(p->next)) return ERROR;
            next_e=p->next->data;
            return OK;
     }  

4.2 顺序表与链表的比较

  1. 存储方式:顺序表将数据元素按序存储在连续的内存空间中,而链表通过指针将各个数据元素链接起来,这些元素可以分布在内存的任意位置。
  2. 内存分配:顺序表在初始化时通常需要一次性申请一块连续的内存空间,大小往往是固定的或需要预估;相比之下,链表则按需为每个数据元素动态分配内存,因此对内存的使用更加灵活。
  3. 数据插入和删除:在顺序表中进行插入和删除操作时,可能需要移动大量元素以维护数据的连续性,这会导致较高的时间复杂度;而在链表中,由于数据元素不需要连续存放,因此插入和删除操作通常只需要改变相关节点的指针,具有较低的时间复杂度。
  4. 随机访问:顺序表支持快速的随机访问,可以直接通过索引快速定位到任何元素;链表由于是非线性存放的,要访问特定元素通常需要从头开始逐个遍历,因此在随机访问方面效率较低。
  5. 空间利用率:顺序表的空间利用率通常较高,因为它没有额外的空间开销;而链表则需要为每个节点存储额外的指针信息,这增加了空间开销。
  6. 内存碎片:顺序表可能会因为频繁的插入和删除操作产生内存碎片问题,而链表由于动态分配内存,不太会产生内存碎片。
  7. 循环链表与双向链表
    第一个表的尾指针指向第二个表的头指针。
  • 双向链表:节点中既有后继又有前驱。
  • 双向链表的插入和删除:
    [图片]
Status ListInsert_DuL(DuLinkList &L,int i,ElemType e)
{
        //在头结点的第i个位置插入元素 
        if(!(p=GetElem_Dul(L,i))) 
        return ERROR;
        s=new DuiNode;//生成新节点s
        s->date=e;
        //关键的四句话
        s->prior=p->prior;
        s->prior->next=s;
        s->next=p;
        p->prior=s;
        return OK; 
  }  

[图片]

Status ListInsert_DuL(DuLinkList &L,int i)
{
        //删除第i个元素
        if(!(p=GetElem_Dul(L,i))) 
        return ERROR;//p为null时,第i个元素不存在 
        //关键的两句话
        p->prior->next=p->next;
        p->next->prior=p->prior;
        delete p;//释放被删节点的空间
        return OK; 
  }  
  1. 线性表的应用
    一位大佬的总结
  • 线性表的合并
    [图片]

总结
在这里插入图片描述

  • 例题:
    1. 已知长度为n的线性表采用顺序存储结构。写
      算法,删除线性表中所有值为x的元素。请先
      对算法思想作简要文字说明,再写出算法具体代
void del_x_1(SqList &L, ElemType x) {
    int k = 0; // 用于记录要删除的元素个数
    int i = 0; // 用于遍历顺序表的索引
    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; // 更新顺序表的长度
}
  1. 设计一个高效算法,将顺序表L的所有元素逆置。
void Reverse(SqList &L) {
    ElemType temp; // 用于交换元素的临时变量
    for (int 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; // 将临时变量中保存的元素赋值给对称位置
    }
}

//封装的实现数组的逆置

#include <iostream>
using namespace std;
void reverseArray(int arr[], int n) {
    for (int i = 0; i < n / 2; i++) {
        swap(arr[i], arr[n - i - 1]);
    }
}
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr) / sizeof(arr[0]);

    reverseArray(arr, n);

    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    return 0;
}
  • 线性表的每个结点只能是一个简单类型,而链表的每个结点可以是一个复杂类型。()
    答案:错误
  • 将长度为n的单链表A链接在长度为m的单链表B之后的算法时间复杂度为答案:O(m)
    链接在B之后,需要对B遍历,长度为m,因此遍历了m次找到尾结点。
  • 链表的删除算法很简单,因为当删除链中某个结点后,计算机会自动地将后续的各个单元向前移动。答案:错误
    链表的删除不会发生结点的移动,顺序表的删除才会发生结点的移动。
  • 从头指针为la的带表头结点的有序顺序表中删除所有值相同的多余元素,并释放被删除结点的空间。
void purge(ListLink &la){
    ListNode *p,*q,*t;
    ElemType temp;
    p=la->link;
    while(p!=NULL){
        q=p;
        temp=p->data;
        p=p->link;
        if(p!=NULL&&temp!=p->data){
            while(p!=NULL&&temp==p->data){
                t=p;
                p=p->link;
                free(t);
            }
            q->link=p;
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值