接上篇线性表 (一)
四、线性表的物理结构--链式存储结构
4.1 定义
顺序存储结构的最大缺点就是插入和删除的时候需要移动大量元素,十分耗时。
链式存储结构中,不考虑所有元素的位置,只是让每个元素知道它下一个元素的位置在哪里,所有元素像一条绳子串起来的珠子,只是这些珠子的位置是散乱的。
这种结构下,我们可以在知道第一个元素时就知道第二个元素的内存地址,知道第二个元素时能找到第三个元素的地址......
以前在顺序存储中,每个数据元素只需要存储本身的元素信息就行了,而在链式存储结构中,除了存储本身的元素外还需要存储下一个元素的地址信息。我们把存储数据元素信息的域称为数据域,把存储后续位置的域称为指针域。这两部分信息组成数据元素a的存储映象,称为结点(Node)。
n个结点链接成的一个链表即为线性表的链式存储结构,简称链表。因为这个链表的每个结点都只有一个指针域,所以称为单链表。
单链表正是通过每个结点的指针域将线性表的数据元素按其逻辑次序链接在一起的
链表中的第一个结点的存储位置称为头指针,整个链表的存取必须从头指针开始进行,最后一个指针指向空,用Null或者^表示,如果图:
为了方便。我们会在单链表的第一个结点前设置一个结点,成为头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表长度等附加信息,头结点的指针域存储指向第一个结点的指针:
4.2 头指针与头结点的异同
头指针:
1、头指针是指向链表第一个节点的指针,若链表有头节点,则是指向头结点的指针。
2、头指针具有标识作用,所以常用头指针冠以链表的名字。
3、无论链表是否为空,头指针均不为空,头指针是链表的必要元素。头结点:
1、头结点是为了方便操作的统一而设立的,放在第一个元素的节点之前,其数据域无意义,也可以存储一些公共信息。
2、有了头结点,对在于第一个元素结点前插入结点和删除结点,其操作就与其他结点的操作统一了。
3、头结点不一定是链表必须要素。
4.3 代码描述
使用结构指针来描述单链表:
typedef stuck Node {
ElemType data;
stuck Node *next;
} Node;
//定义一个链表
typedef struct Node *LinkList;
五、单链表的常见操作
5.1 读取
算法思路:
- 声明一个p指针指向链表第一个结点,初始化j从1开始
- 当j<i时遍历链表,让p向后移动,j累加
- 若链表末尾p为空则说明第i个结点不存在
- 否则查找成功,返回p结点的数据
代码实现:
status GetElem(LinkList L, int i, ElemType *e) {
int j = 1;
LinkList p;
p = L->next;
while (j<i && p) {
p = p -> next;
j++;
}
if (!p || j>i) {
return ERROR;
}
*e = p->data;
return OK;
}
寻找链表中点
算法思想
- 声明一个快指针,一个慢指针。
- 块指针一次走一步,慢指针一次走两步。
- 当快指针到达终点时,慢指针刚好在单链表的中点
- 返回慢指针所指向的结点元素
status FindMidElem (LinkList L) {
LinkList fastP = L->next->next;
LinkList slowP = L->next;
int j=1;
while (j < L->length) {
fastP = fastP->next->next;
slowP = slowP->next;
}
return slowP->data;
}
查找元素的时间复杂度取决于i的位置,所以时间复杂度为O(n)。
5.2 单链表的插入
算法思路:
- 先找到需要插入的位置:声明一个指向链表头的结点,初始化j为1;当j<i时,遍历链表,让p的指针向后移动,j累加1;若最后p指针为空则插入位置不存在,否则p就是所寻找的位置。
- 在系统中生成一个空结点s;
- 将元素e的数据赋值给s
- 将p->next赋值给s->next,p->next为s
代码实现:
status insetElem(LinkList *L, int i, ElemType e) {
LikList *p;
LikList *p = GetElem(L, i, e);
if (!e) { return ERROR; }
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
5.2 单链表的删除
算法思想:
- 先找到需要插入的位置:声明一个指向链表头的结点,初始化j为1;当j<i时,遍历链表,让p的指针向后移动,j累加1;若最后p指针为空则插入位置不存在,否则p就是所寻找的位置。
- 将欲删除的结点p->next赋值给q;
- 将要删除的结点q中的元素赋值给e,作为返回值;
- 释放q结点
- 返回成功
代码实现:
status deleteElem(LinkList *L, int i, ElemType *e) {
LinkList *p = GetElem(L,i,e);
if (!e) { return ERROR; }
LinkList *q = p->next;
p->next = q->next;
e = q->data;
free(q);
return OK;
}