数据结构-线性链表

Part3_线性链表

1 概念

用一组任意的存储单元存储线性表的数据元素。

(存储单元可以是连续的,也可以是不连续的)

1.1 特点

随机存储,但需要知道后继元素的地址,即线性表的链式存储结构

1.2 数据域

存储数据元素信息的域称为数据域

1.3 指针域

存储直接后继位置的域称为指针域

1.4 结点

这两部分的信息组成数据元素​的存储镜像,称为结点(node)

1.5 链

指针域中存储的信息称做指针或链

1.6 链表

n个结点链结成一个链表,即为线性表的链式存储结构。

1.7 单链表

因为此链表的每个结点中只包含一个指针域,所以叫做单链表。

1.8 其他链表

根据链表结点所含指针个数、指针指向和指针连接方式,可以将链表分为单链表、循环链表;双向链表,二叉链表等。

 

其中:

单链表、循环链表、和双向链表用于实现线性表的链式存储结构

其他形式多用于实现树、和图等非线性结构。

单链表 图示:

 

2 单链表的实现

2.1 属性

2.1.1 单链表 示例:

1614305228390

2.1.2 头指针

链表中的第一个结点的存储位置叫做头指针。

分析:整个链表的存取必须从头指针开始进行。

同时:由于最后一个数据元素没有直接后继,则单链表中最后一个结点的指针为空;

头指针的逻辑示意图:

1614307693963

单链表的结点

1614308152716

 typedef struct *l_node link_list 
 //强调其为指向某个单链表的头指针
 link_list p;
 逻辑含义:
 p:指向某结点的指针变量,该结点的地址
 *p:某结点本身

 

2.1.3 头结点

一般情况下。为了处理方便,在 单链表的第一个结点之前附设一个结点,称之为头结点。

 

(1)头结点的逻辑示意图:

1614308413387

 

(2)其数据域:

可以不存储任何信息。

也可以存储如线性表的长度等附加信息。

(3)其指针域:

存储指向第一个结点的指针

(4)优势:

1614307693963

 消除首元结点(L->data 其他结点 L->next->data)与其他数据元素的操作差异,无需进行特殊处理
 消除空表与非空表的操作差异

头指针:空表:L==NULL

非空表:指向首元结点首地址

 

头结点:空表:头指针指向头结点首地址(L->next ==NULL)

非空表:头指针指向头结点首地址

 

1614309394276

 

2.1.4 对比

分类首元结点头指针头结点
概念指链表中存储第一个数据元素的结点(ZHAO)头指针是指链表指向第一个结点的指针。 若链表有头结点,则是指向头结点的指针首元结点前的附设结点
逻辑示意图1614308869881常用头指针作为链表的名字1614308900384有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作统一1614308936998
必要性空链表中不存在1614309725985头指针必不为空,链表的必要元素链表的非必须要素

 

 

2.2 建表---带头结点的单向链表

创建单链表的过程就是一个动态生成链表的过程。(从空表的初始状态起,依次建立各元素的结点,并逐个插入链表)

2.2.1 空表

算法步骤:

 (1)生成新的结点作为头结点,用头指针指向头结点
 (2)头结点的指针域置空

示例:

1614320631696

 代码解析:
 status  init_list(link_list *pl)
 函数执行状态   函数名   函数参数:单链表的头指针的地址  
 参数:
       pl:头指针的指针
       *pl:头指针的值
       malloc(sizeof(Node));//申请新结点作为头结点
       *pl=(link_list)malloc(sizeof(Node));//头指针=头结点的首地址
       *pl:头结点的首地址
       *pl->next:头结点的指针域
       *pl->data:头结点的数据域      

# 2.2.2 头插法

将新结点逐个插入链表的头部来创建链表,每次申请一个新结点,读入相应的数据元素值,然后将新结点插入到头结点之后。

算法步骤

 (1)创建一个只有头结点的空链表
 (2)根据待创建链表包括的元素个数n,循环n次执行一下操作
       ·生成一个新的结点p;
       ·输入元素值赋给新结点p的数据域
       ·将新结点p插入到头结点之后

1614579597376

参考代码:

1614581400259

 

练习:上述代码

2.2.3 尾插法

将新结点逐个插入到链表的尾部来创建链表。

需要一个尾指针指向链表的尾节点。

算法步骤:

 (1)创建一个只有头结点的空链表
 (2)尾指针r初始化,指向头结点。
 (3)根据创建链表包括的元素个数n,循环n次执行一下操作
         ·生成一个新节点p;
         ·输入元素值赋给新节点p的数据域;
         ·将新节点p插入到尾结点r之后;
         ·尾指针r指向新的结点p;

算法示意图:

1614581819486

参考代码:

1614583462915

练习:上述代码

2.3 按位取值

只能从链表的首元结点出发,顺着指针域逐个结点向下访问

(无间道的单线联系)

1614323611260

算法步骤:

1614323605368

 (1)用指针p指向首元结点,用j做计数器,初值为1
         link_list  p=l;
         int j=1;
 (2)首元结点开始,依次顺着指针域next向下访问,
         只要指向当前结点的指针p不为空,并且j != i(位置序号),便循环执行
        p指向下一个结点;
        计数器j+1;
   (3)退出循环时,如果指针p为空,或者计数器j大于i;说明位置序号i不合法。
             取值失败,返回error;
             否则取值成功,此时j=i,p所指向的结点就是第i个结点
                         参数e保存p->data;返回OK;                  

示例代码:

 #include "02_my.h"
 Status get_elem(LinkList L,int i,ElemType *e)
 {
     int j;//计算数器变量
     LinkList p;
     p=L->next;//第一个结点的首地址
     j=1;
     while(p && j<i)
     {
         p=p->next;
         j++;
     }
     if(!p || j>i) 
         //p为空,空表,超过链表长度||j>i ,i小于1
         return ERROR;//读取失败
     *e=p->data;
     return OK;
 }

算法分析:O(n)

练习:整表查询

 

2.5 插入

逻辑示意图

1

图解:假设存储元素e的结点为s,要实现结点p、p->next,和s之间的逻辑关系的变化,只需要将结点s插入到结点p和p->next 之间即可

 

2.5.1 算法步骤

 (1)查找结点a(i-1),(第ai个数据元素前一个)。
 (2)生成新的结点s(局部变量?全局变量,动态内存分配!)
 (3)将新的结点数据域设置为e;
 (4)将新结点的指针域指向结点ai;(小明在终点线超过了第二名,成为了第几名?)
 (5)将结点p的指针域指向新结点s

算法示意图:

1614568778252

详细解析:

插入解析图

###

2.5.2 代码

1614568658247

 

2.5.3 算法分析O(n)

找到第ai-1个结点

2.6 删除

逻辑示意图:

1614569401155

 

图解:

要实现将结点b删除,将它的前继结点a的指针绕过b,指向它的后继结点c即可。

 p->next =p-next->next;
 //把p的后继结点改为p的后继的后继结点

2.6.1 算法步骤

 (1)查找结点a(i-1),并使指针p指向该节点
 (2)临时保存待删除结点ai的地址在q中。以备释放
 (3)将结点指针p的指针域指向ai的直接后继结点
 (4)释放结点ai的空间

算法示意图:

1614569973708

2.6.2 参考代码:

1614570762385

 

2.6.3 时间复杂度O(n)

算法:遍历查找第i个结点(线性)O(n)

2.6.4 单链表整表删除 练习

算法思路

 声明一结点p和q;
 将头指针赋值给p;
 循环:
     (1)备份p的后继结点地址 给q;
     (2)释放p;
     (3)将q赋值给p

参考代码

 #include"02_ my.h"
 Status clear_list(LinkList *L)
 {
     LinkList p,q;
     p=(*L)->next;//头结点
     while(p)//表尾
     {
         q=p->next;
         free(p);
         p=q;
     }
     (*L)->next=NULL;//头结点的指针域为空
     return OK;
 ​
 }

 

2.7 表长

算法步骤:遍历找到空指针

1614583581902

3 循环链表

单向链表:表中最后一个结点的指针域指向空

循环链表:表中最后一个结点的指针域指向头结点

1614585669224

1614584142249

相同: 其他数据元素的操作基本一致

区别: 判断链表是否结束的条件不同

分类判别条件判别条件
 无头结点有头结点
单向链表p !=NULLp->next !=NULL
循环链表p !=Lp->next !=L
   

示例:计算循环链表的长度

1614586342763

 

优势:合并线性表

4 合并线性表

逻辑示意图:

左头右尾

1614584549529

算法步骤

 (1)找到B的首元结点给p,备份B的头结点以备释放
 (2)表B的尾指针指向表A的头结点
 (3)将表A的尾指针指向p
 (3)释放表B的头结点
 //主要语句(A,B是尾指针)
 //B:尾指针
 //B->next:头结点
 //B->next->next:首元结点
 ​
 tmp=B->next;//备份B的头结点
 p=B->next->next;//备份首元结点的地址
 B->next=A->NEXT;//B的尾指针指向A的头结点
 A->next=p;//表A尾指针指向B的首元结点
 free(tmp);

参考代码:

1614586294657

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值