线性表·线性表的链式表示
一、单链表相关概念
1.定义
-
单链表是线性表的链式存储
-
通过一组任意的存储单元来存储线性表中的数据元素
-
对每个链表结点,除存放元素自身的信息外,还需要存放一个指向其后继的指针
-
节点类型和指针定义
-
typedef struct LNode{ //定义单链表节点类型 ELemType data; //数据域 struct LNode *next; //指针域 }LNode,*LinkList;
-
2.特点
-
通过“链”建立起数据元素之间的逻辑关系
-
指针的设置是任意的,可以很方便的表示各种逻辑结构
-
插入和删除操作不需要移动元素,只需要修改指针,但也会失去顺序表可随机存取的优点
3.优缺点
优点:
- 解决顺序表需要大量连续空间的缺点
- 对于插入或删除数据越频繁的操作,单链表的效率就越明显
缺点:
- 需要开辟指针域,需要的空间更大。
- 查找某一序号的值时,需要从表头开始遍历。
4.头结点&头指针
4.1定义
- **头结点:**在链表表头之前插入一个指向链表表头的结点,该结点称为头结点,头结点数据域为空,指针域直线表头节点。
- **头指针:**一般把链表中的第一个结点称为头指针,其存储链表的第一个数据元素
4.2 头结点与头指针的区别
- 不管带不带头结点,头指针都始终指向链表的第一个节点
- 头结点是带头结点的链表中的第一个节点,节点内通常不存储信息
4.3 引入头结点的优点
- 单链表设置头结点的目的是方便运算的实现
- 好处一:有头节点后,插入和删除数据元素的算法就统一了,不再需要判断是否在第一个元素之前插入或删除第一个元素
- 好处二:不论链表是否为空,其头指针是指向头节点的非空指针,链表的头指针不变,因此空表和非空表的处理也就统一了
二、单链表上的操作
1、采用头插法建立单链表
1.1定义
从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。后面再有新生成的节点,都插在头结点与链表表头之间。
1.2图解头插法
1.3代码实现
LinkList List_HeadInsert(LinkList &L)
{ //逆向建立单链表
LNode *s;
int x;
L = (LinkList)malloc(sizeof(LNode)); //创建头结点
L->next = NULL; //初始为空链表
scanf("%d", &x); //输入节点的值
while (x != 9999)
{ //输入9999表示结束
s = (LNode *)malloc(sizeof(LNode)); //创建新节点
s->data = x;
s->next = L->next;
L->next = s; //将新节点插入表中,L为头指针
scanf("%d", &x);
}
return L;
}
每个节点的插入的时间复杂度为O(1),建表的时间复杂度为O(n)
1.4头插法优缺点
优点:算法实现简单
缺点:生成的链表中结点的次序和输入数据的顺序不一致(可以利用这个特点将链表节点倒置)
2、采用尾插法建立单链表
2.1定义
从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后新结点插入到当前链表的表尾,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点,不然每次插入都需要从表头遍历到表尾。
2.2图解尾插法
2.3代码实现
LinkList List_TailInsert(LinkList &L)
{ //正向建立单链表
int x;
L = (LinkList)malloc(sizeof(LNode));
LNode *s, *r = L; // r为表尾指针
scanf("%d", &x); //输入节点的值
while (x != 9999)
{ //输入9999表示结束
s = (LNode *)malloc(sizeof(LNode)); //创建新节点
s->data = x;
r->next = s;
r = s; // r指向新的表尾节点
scanf("%d", &x);
}
r->next = NULL; //尾节点指针置空
return L;
}
每个节点的插入的时间复杂度为O(1),建表的时间复杂度为O(n)
3、按序号查找节点值
3.1算法思路
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。
3.2代码实现
LNode *GetElem(LinkList L, int i)
{
int j = 1; //计数,初始为1
LNode *p = L->next; //第1个节点指针赋给p
if (i == 0)
return L; //若i=0,则返回头结点
if (i < 1)
return NULL; //若i无效,则返回NULL
while (p && j < i)
{ //从第i个节点开始找,查找第i个节点
p = p->next;
j++;
}
return p; //返回第i个节点的指针,若i大于表长,则返回NULL
}
时间复杂度为O(n)
4、按值查找表节点
4.1算法思路
从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于给定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。
4.2代码实现
LNode *LocateElem(LinkList L, ElemType e)
{
LNode *p = L->next;
while (p != NULL && p->data != e)
//从第i个节点开始查找data域为e的节点
p = p->next;
return p;
5、插入节点操作
5.1算法思路
插入操作是将值为x的新结点插入到单链表的第i个位置上。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i−1个结点,再在其后插入新结点。
5.2示意图
5.3代码实现
p = GetElem(L, i - 1); //查找插入位置的前驱结点
s->next = p->next;
p->next = s;
6、删除节点操作
6.1算法思路
删除操作是将单链表的第i个结点删除。先检查删除位置的合法性,然后查找表中第i−1个结点,即被删结点的前驱结点,再将其删除。
6.2示意图
6.3代码实现
p = GetElem(L, i - 1); //查找删除位置的前驱结点
q = p->next; //令q指向被删除节点
p->next = q->next; //将*q节点从链中断开
free(q) //释放节点的存储空间
7、求表长操作(不含头结点)
设置一个计数器,每访问一个节点,计数器加1,直到访问到空节点为止
三、双链表
1.概念
双链表结点中有两个指针prior和next,分别指向其前驱节点和后继节点
1.为什么引入双链表
- 单链表只能从头结点依次顺序地向后遍历
- 单链表不能快速的前驱结点进行操作
2.双链表结点类型和指针定义
typedef struct DNode{ //定义双链表节点类型
ELemType data; //数据域
struct DNode *prior,*next; //前驱和后继指针
}DNode,*DLinkList;
3.双链表的插入操作
3.1示意图
3.2代码实现
s->next = p->next; //将节点*s插入到*p之后
p->next->prior = s;
s->prior = p;
p->next = s
4.双链表的删除操作
4.1示意图
4.2代码实现
p->next = q->next; //删除节点*q
q->next->prior = p;
free(q)
**注意:**插入删除操作时一定要注意操作顺序,不要丢失指向后继结点的指针。
5.单/双链表常考结论
- 带头结点的单链表,判断表为空的条件: head->next==NULL
- 不带头结点的单链表,判断表为空的条件:head==NULL
四、循环链表
1.循环单链表
1.1定义
将单链表表尾节点的指针域设置为头结点地址,使单链表形成一个环形,即为循环单链表。
1.2示意图
1.3特点
- 最后一个节点指向头结点
- 可以从任意一个节点开始遍历整个链表
- 仅设置尾指针
2.循环双链表
2.1定义
将双链表表尾节点的后继指针指向头结点,头结点前驱指针指向表尾节点,使双链表形成一个环形,即为循环双链表。
2.2示意图
2.3特点
- 最后一个节点的next指针指向头结点
- 头结点的prior指针指向最后一个节点
3.常考结论
- 判断带头结点循环单链表为空的条件: head->next==head
- 注意在计算线性表长度的时候, 头结点不计算在内
- 带头结点的双循环链表L为空的条件是: L->prior==L && L->next==L(即头结点的prior和next都指向自己)
五、静态链表
1.定义
- 借助数组来描述线性表的链式存储结构,因此静态链表需要提前申请一片连续的空间。
- 节点也有数据域data和指针域next,这里的指针是节点的相对位置(数组下标),又称游标。
2.结构描述
#define Maxsize 50
typedef struct
{ //定义单链表节点类型
ElemType data; //数据域
int next; //下一个元素的数组下标
} SLinkList[Maxsize];
3.示意图
注意:静态链表中,指针域为-1时,即表示此节点为表尾节点。
4.常考结论
- 静态链表需要分配较大空间,容量固定不可变
- 插入和删除不需要移动元素,只需要修改指针
- 静态链表不能随机存取,只能从头结点开始依次往后查找
欢迎加入我们的计算机考研交流群,群号: 629154108。
欢迎关注我们的微信公众号:Lander计算机。
欢迎关注我们的知乎账号:Lander计算机考研。
欢迎关注我们的小红书账号:Lander计算机考研。