链式存储结构的线性表
前言
线性表用链式存储结构来实现,借助的是链表。链表有单链表,循环链表,双向列表,本文主要记录的是带表头结点的单链表,具体原因之后再说,但我还是会给出与不带表头结点的单链表的比较,关于链表的理解我想通过数组来参照。
- 对于链表来说,最小单位是结点
struct Node {
int element;
struct Node *link;
};
typedef struct Node Node;
- 相比于数组的元素,对于单个结点来说,不仅有存储数据元素信息的数据域(这里我直接设定为int,实际上类型和数量都不一致),而且有存储后继结点存储位置的指针域。(链表相邻结点的位置地址不一定相邻)
- 这样处理的好处是对于数组繁琐的插入和删除操作,对于链表来说,不需要去做一个for循环移动数组中的元素,只要前后指针域的连接即可实现。
- 而下文主要记录的带表头的单链表和单链表操作一致,只是增加一个不存储数据的head结点,但插入删除更加方便,而且书上没有全部给出带表头的单链表的操作,所以我在这里整理一下。
在不会引起混淆的情况下,将不明确区分结点和元素这两个词。如果必要,结点指的是上文定义的整个结构体, 而其中的数据元素称为该结点的元素
链表
- 关于链表我还想再多说几句,当线性表(a0, a1, …, an-1)以单链表的形式进行存储时,第一个元素所在的结点是头结点,而带表头的单链表是在这个头结点前在定义一个表头结点,这是因为插入和删除不用去分是不是头结点之类的。
- 单链表的最后一个元素的指针域为NULL;
- 对链表进行插入删除操作时,经常需要在定义一个指针保存当前结点的前一个或后一个结点的地址,否则容易“断链”或是不方便操作。
线性表的定义
typedef struct {
Node *head;
int n;
}HeaderList;
- head保存表头结点的地址,n是元素的个数。
基本操作
初始化
以下函数均默认输入的数据合法,舍去了错误检查机制
void Init(HeadList *h) {
h->head = (Node*)malloc(sizeof(Node));
h->head->link = NULL;
h->n = 0;
return;
}
- 带表头结点的单链表为空时,就已经有一个表头结点了。
查找
- 取出第i个元素的值
int Find(HeaderList *h, int i) {
Node *p = h->head;
for ( int j=0; j