从零开始学习数据结构--2.2线性表之链表

我们在学习顺序表的时候,发现我们利用顺序表去进行插入删除的时候,需要移动大量的元素才能完成这一操作,而且如果存储空间不够的时候,我们又要重新划分一个存储空间给顺序表。

如果存储空间太够又会造成一定程度的浪费。那我们可不可以创造一个随用随取的模式呢?

我们有5个元素,就给5个存储空间,既不会浪费也不会存储不足

欸,这就是我们下面要讲的链表。

链表的定义

 我们首先想一下我们链表在存储空间是怎么样的

是不是在存储空间里面创造一个个元素,然后再链接起来

那我们就可以把这些一个个的元素称为结点,结点分为数据域指针域

长这样,我们称 n 个结点链结成的一个链表,就是线性表的链式存储结构

有些专业术语我们要知道什么意思

头指针:我们把链表中第一个结点的存储位置叫做头指针,意思就是指向链表中第一个节点的指针

头结点:是在链表的首元结点之见附设的一个结点

首元结点:是指链表中存储第一个数据元素a1的结点

具体来讲就是这样

还有一个概念

空表,空表就是没有结点的表呗,具体长这样

要注意,两个都能表示空表

单链表的结构

下面我们来学习单链表的结构

typedef struct Node                //创建一个结点
{
    ElemType data;                 //存放单个结点的数据
    struct Node *next;             //存放下个结点的地址
}Node, *LinkList;

链表的一系列操作

        链表的初始化

int InitLL(LinkList *L)
{
    (*L) = (LinkList)malloc(sizeof(Lnode));     //申请一块空间用来存放链表
    (*L)->next = NULL;                          //L的next域为NULL
    return OK;
}

单链表的销毁

画图不好看我就直接那王卓老师的网课图示来了(感谢王卓老师🙏)

int DestroyList_L(LinkList *L){
    Node *p;                    //创建一个指针
    while(L == NULL){           
    p = L;                      //根据图示,先让指针指向L
    L = L->next;                //L再根据下一个地址移动
    free(p);                    //把p释放掉(c++的话可以用delete p也是一样的)
    }
    return OK;
}

单链表的清空 

我们要区分销毁和清空的区别,销毁是把整个链表给销毁掉,而清空则是保留单链表,把单链表的结点给去掉,简单来说就是把单链表变成空表

 我们看,他用了两个指针  p,q。一个指向a1,一个指向a2。然后删掉p ,后面让p指向q的位置,q继续下一个,如此循环,最终就只剩下头结点和最后一个空值,所以代码如下

int ClearList(LinkList *l){
    Node *p,*q;                        //创建两个指针p,q
    p = L->next;                       //确定p的位置
    while(p == NULL){                  //进行删除操作
        q = p->next;
        delete p;
        p = q;
    }
    L->next = NULL;                    //最后让头节点的地址指向最后一个
    return OK;
 }

求单链表的表长 

int ListLength_L(LinkList L){
    LinkList p;
    p = L->next;            //从第一个结点开始
    i = 0;
    while(p){
    i++;                    //遍历一遍所有结点
    p = p->next;
    }                       //然后再计数,简简单单
    return i;
  }

查找单链表中第 i 个元素的内容

算法思想:

        ①从第一个结点开始,p = L->next

        ②j用来当作计数器,累计扫过的结点数  j = 1

        ③p扫过下一个结点,j+1

        ④当j == i 时,p所指的结点就是我们要找的结点

代码如下

int GetElem_L(LinkList L, int i, ElemType *e){
    p = L->next;                        
    j = 1;
    while(p == NULL && j<i){
    p = p->next;
    ++j;                      //遍历i遍到指定的位置获取数据
    }
    if(!p || j>i)
    return ERROR;
    e = p->data;
    return OK;
  }

按值查找

其实就是查找的另一个翻版,思想都是一样的

int LocateElem_L(LinkList L, ElemType *e){
    p = L->next;                       
    j = 1;
    while(p == NULL && p->data!=e){
    p = p->next;
    j++;                      //遍历i遍到指定的位置获取数据
    }
    if(p == NULL)
    return j;
    else
    return OK;
  }

单链表的插入操作

首先我们要想一下该怎么插入(我们以a3,a4之间插入a7为例子)

是不是我们先a3指向a4的指针域去指向a7,然后让a7的指针域指向a4

那我们怎么操作呢,我们具体看a3,a4,a7

算法思想:

        1.首先找到 a3 的存储位置 p

        2.生成一个数据域为 a7 的新结点 s

        3:①(图示) 新结点的指针域指向a4-------【s->next = p->next;】

              ②(图示)结点a3的指针域指向新结点-----------【p->next = s】

这里我们不能把①和②搞反了,因为如果搞反了我们就会丢失a4的地址,这个一定要注意

具体代码如下

int ListInsert_L(LinkList &L,int i,ElemType e){
    p = L;                                //先找到要插入的位置
    j = 0;
    while(p && j<i-1){
    p = p->next;
    ++j;
    }
    if(p == NULL || j>i-1)              //插入不合法就error
    return ERROR;
                                        //如果找到了要插入的位置
    S = new Node;                       //我们创造一个结点
    s->data = e;                        //结点内容为e
    s->next = p->next;                  //①
    p->next = s;                        //②
    return OK;

删除第 i 个链表的结点

和插入差不多,都是指针域的替换

算法思想:

                1.找到 a3 的存储位置 p,保存要删除的a7的值。

                2.令p->next 指向 a4。【p->next = p->next->next或者 p->next = s->next】

                3.释放 a7 空间。

具体代码如下

int ListDelete_L(LinkList &L,int i, ElemType &e){
    p = L;                                    
    j = 0;
    while(p->next && j<i-1){                //首先通过遍历找到我们要删除的元素
    p = p->next;                    
    ++j;                            
    }
    if(p->next == NULL || j>i-1)            //判断删除的元素是否合法
    return ERROR;
    s = p->next;                            //合法的话
    p->next = s->next;                      //我们就先用s标记要删除的位置
    e = s->next;                            //存储要删除的值
    delete s;                               //释放 s 的空间
    return OK;
 }

创建链表(头插法)※重点

算法思想:最先插入最后一个

        ①从空表开始,重复读入数据

        ②生成新结点

        ③从最后一个结点开始,依次插入到链表前端

直接看代码

void CreatLL_H(LinkList L, int n)
{
   L = new LNode;
   L->next = NULL;               //先创建一个带头节点的单链表
    for(i = n; i>0; --i){        //进行循环
    p = new LNode;     //生成我们要插入的新节点,也可以用c语言表示p=(LNode*)malloc(sizeof(LNode));
    cin>>p->next  //输入我们的值,也可以用c语言表达scanf(&p->data);
    p->next = L->next;    //让我们新创建的结点的next域等于L的next域也就是NULL
    L->next = p;          //让L的next域指向p结点
  }

其实还是很好懂得,对吧

创建链表(尾插法)※重点

头插法我们输入的是edcba才能得到(看上面的图片)abcde

尾插法我们输入的是abcde就能得到abcde了

具体怎么做呢?

算法思想:①创建一个空表,和一个尾指针r

                  ②创建一个结点p

                  ③使得尾指针r指向新节点

                  ④移动尾指针r到尾部

我们看看具体代码和详解

void CreateList_R(LinkList &L,int n){
    L = new LNode;
    L->next = NULL;       //生成新链表
    r = L;                //尾指针r指向头节点
    for(i = 0; i<n; ++i){    
    p = new LNode;        //创建一个新节点
    cin>>p->data;         //输入数据
    p->next = NULL;       //让新创建的结点的next域等于空值
    r->next = p;
    r = p;

好了,单项链表讲完了,自己理解一下,消化一下

通过这节。我们学到了链表是什么,他的一系列的操作是什么

后面我们要学循环链表双向链表什么的,那就后面再讲吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值