链表 —— 由一连串的结构(称为结点node)组成,每个结点都包含指向下一个结点的指针,最后一个为空指针。
一、声明结点类型
struct node{
int value;
struct node *next; //指向下一个结点
};
struct node *first = NULL; //始终指向表中第一个结点的变量, 初始化为空
- node 为结构标记,通常可使用结构标记或 typedef 来定义一种特殊的结构类型。但若在结构中有一个指向相同结构类型的指针成员时,要求使用结构标记。
二、创建结点
创建结点的步骤:
① 为新结点分配内存单元;
② 将数据存储到新结点中;
③ 将新节点插入到链表中。
//步骤1:
struct node *new_node; //指向新结点的临时指针
new_node = (struct node*)malloc(sizeof(struct node));
//步骤2:
new_node->value = 10; //等价于(*new_node).value = 10;
三、->
运算符
->
—— 右箭头选择 (right arrow selection)
- 需要通过一个指向结构的指针访问该结构的某个成员时,可使用
->
运算符。 - 运算对象必须为指向结构的指针。
//下面两者等价
new_node->value = 10;
(*new_node).value = 10;
->
运算符产生左值,可以在任何允许使用普通变量的地方使用它,例如:
scanf("%d", &new_node->value);
四、在链表中插入结点
链表的优势在于可以在链表的任何位置添加结点。
我们在链表中插入一个新结点之前,需要先确定插入位置,由于插入后需要重新拼接,为此需要确定两个位置,分别为插入位置,和其前一个结点的位置。
- 插入结点之前需要前确定插入位置是否为链表开头
- 插入步骤:
- 创建新结点 new_node (分配内存,存入数据);
- 确定插入位置 cur,和插入位置的前一个结点位置 prev;
- 链接:将 cur 、new_node 和 prev 链接起来。
//创建新结点, 并存储好数据
struct node *new_node = (struct node*)malloc(sizeof(struct node));
new_node->value = 10;
//确定插入位置
struct node *cur = fisrt, *prev = NULL; //待插入位置和其前一个结点的位置, 初始化
...
//向链表中插入结点
new_node->next = cur;
if(prev == NULL) //插入的是链表开头
first = new_node;
else //插入到非首个结点的位置
prev->next = new_node;
往链表开头中插入结点:
- 参数1:指向旧链表首结点的指针;
- 参数2:需要存储在新结点的数据。
- 返回:新链表的首地址
struct node *add_to_list(struct node *list, int n)
{
//创建新结点并存储数据
struct node *new_node = (struct node*)malloc(sizeof(struct node));
if(new_node == NULL){
printf("Error: maloc failed in add_to_list.\n");
exit(EXIT_FAILURE); //结束进程
}
new_node->value = n;
//结点添加至链表
new_node->next = list;
return new_node;
}
函数的使用:
first = add_to_list(first, 10);
first = add_to_list(first, 20);
五、链表的搜索
惯用法:
for(p = first; p != NULL; p = p->next){
...
}
链表搜索函数:
//原始版本:
struct node *search_list(struct node *list, int n)
{
struct node *p = NULL;