前言
链表是一种常见的基础数据结构,结构体指针在这里得到了充分的利用。链表可以动态的进行存储分配,也就是说,链表是一个功能极为强大的数组,他可以在节点中定义多种数据类型,还可以根据需要随意增添,删除,插入节点。链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的。链表中每个节点都分为两部分,一个数据域,一个是指针域。说到这里你应该就明白了,链表就如同车链子一样,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。
一、链表的基本概念
- 链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,有一系列结点(地址)组成,结点可动态地生成。
- 结点包括两个部分
(1)存储数据元素的数据域(内存空间)
(2)存储指向下一个结点地址的指针域。 - 相对于线性表顺序结构,操作复杂。
- 链表分类
(1)单链表 (2)双链表 (3)单向循环链表 (4)双向循环链表
二、链表的基本操作
1. 单链表的初始化
LinkList InitList() {
LinkList* head = (LinkList*)malloc(sizeof(LinkList));//定义指针变量并为其分配空间
head->next = NULL;//头节点的指针域为空
return head;//返回头节点的地址,即头指针
}
2. 单链表的建立
尾插法是将新结点插到当前链表的表尾。
//尾插法建立链表:
#include <stdio.h>
#include <stdlib.h>//里面包含了malloc函数
struct LinkList {//创建结构体
int data;//数据域,用来保存数据。
struct LinkList* next;//指针域,用来连接其他结点。
};
int main(void) {
struct LinkList* head = (struct LinkList*)malloc(sizeof(struct LinkList));//创建头结点,并分配内存,需要的内存大小就是结构体的大小。别忘了在malloc前进行强制类型转换。(struct LinkList*)
head->next = NULL;//头结点指针初始化
struct LinkList* p ;//创建p结点 p结点在尾插法中用于记录链表尾结点,将来插入结点的时候只需要向p结点后插入即可。
p = head;//p结点指针赋值为头结点
int n;//保存链表长度,即结点的个数。
printf("请输入链表长度:\n");
scanf("%d", &n);
printf("输入数据:\n");
for (int i = 0; i < n; i++) { //循环创建结点
struct LinkList* s = (struct LinkList*)malloc(sizeof(struct LinkList));//创建s结点,并分配内存
scanf("%d", &s->data);//给s结点赋值。
//尾插法建立链表
p->next = s;//因为插完第一个结点后用p保存了上一个结点,所以再插结点时应该插在p节点后面,这就是尾插。
s->next = NULL;//别忘了让插在尾部的s结点的指针指向NULL。
p = s;//p结点保存刚才的s结点;以保证p始终为链表的最后一个节点
}//至此,链表的创建已经完成了。
p = head;//让p指针从"头"开始。
while (p->next != NULL) { //如果p的下一个结点不为NULL,也就是说如果p结点后还有节点存在,那就输出p后面结点中保存的数据。
printf("%-5d", p->next->data);
p = p->next;//p结点后移
}
printf("\n");
return 0;
}//至此完成了链表的遍历输出。
```//头插法建立链表:
#include <stdio.h>
#include <stdlib.h>//里面包含了malloc函数
struct LinkList { //创建结构体
int data;//数据域,用来保存数据。
struct LinkList *next;//指针域,用来连接其他结点。
};
int main(void) {
struct LinkList *head = (struct LinkList *) malloc(
sizeof(struct LinkList));//创建头结点,并分配内存,需要的内存大小就是结构体的大小。别忘了在malloc前进行强制类型转换。(struct LinkList*)
head->next = NULL;//头结点指针初始化
struct LinkList *p;
int n;//保存链表长度,即结点的个数。
printf("请输入链表长度:\n");
scanf("%d", &n);
printf("输入数据:\n");
for (int i = 0; i < n; i++) {//循环创建结
struct LinkList *s = (struct LinkList *) malloc(sizeof(struct LinkList));//创建s结点,并分配内存
scanf("%d", &s->data);//给s结点赋值。
//头插法建立链表
s->next = head->next; //将链表除链表头的所有节点连接在新建节点之后
head->next = s;//在让头结点指向新节点 注意:和上一语句顺序不能颠倒
}//至此,链表的创建已经完成了。
p = head;//让p指针从"头"开始。
while (p->next != NULL) { //如果p的下一个结点不为NULL,也就是说如果p结点后还有节点存在,那就输出p后面结点中保存的数据。
printf("%-5d", p->next->data);
p = p->next;//p结点后移
}
printf("\n");
return 0;
}//至此完成了链表的遍历输出。
头插法建表从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直至读入结束标志为止。
//头插法建立链表:
#include <stdio.h>
#include <stdlib.h>//里面包含了malloc函数
struct LinkList { //创建结构体
int data;//数据域,用来保存数据。
struct LinkList *next;//指针域,用来连接其他结点。
};
int main(void) {
struct LinkList *head = (struct LinkList *) malloc(
sizeof(struct LinkList));//创建头结点,并分配内存,需要的内存大小就是结构体的大小。别忘了在malloc前进行强制类型转换。(struct LinkList*)
head->next = NULL;//头结点指针初始化
struct LinkList *p;
int n;//保存链表长度,即结点的个数。
printf("请输入链表长度:\n");
scanf("%d", &n);
printf("输入数据:\n");
for (int i = 0; i < n; i++)//循环创建结点
{
struct LinkList *s = (struct LinkList *) malloc(sizeof(struct LinkList));//创建s结点,并分配内存
scanf("%d", &s->data);//给s结点赋值。
//头插法建立链表
s->next = head->next; //将链表除链表头的所有节点连接在新建节点之后
head->next = s;//在让头结点指向新节点 注意:和上一语句顺序不能颠倒
}//至此,链表的创建已经完成了。
p = head;//让p指针从"头"开始。
while (p->next != NULL)//如果p的下一个结点不为NULL,也就是说如果p结点后还有节点存在,那就输出p后面结点中保存的数据。
{
printf("%-5d", p->next->data);
p = p->next;//p结点后移
}
printf("\n");
return 0;
}//至此完成了链表的遍历输出。
总结:
- 头插法相对简便,但插入的数据与插入的顺序相反。
- 尾插法操作相对复杂,但插入的数据与插入顺序相同。
3. 单链表的遍历
void OutPut(Linklist* head) {
Linklist* p;
p = head->next;
while (p) {
printf("姓名: %s\n",p->name);
printf("学号:%d\n",p->number);
p = p->next;
}
}
4. 单链表的插入
插入节点就是用插入前节点的指针域链接上插入节点的数据域,再把插入节点的指针域链接上插入后节点的数据域。即:e->next = head->next; head->next = e;
5. 单链表的删除
删除链表的元素也就是把前节点的指针域越过要删除的节点指向下下个节点。即:p->next = q->next; 然后放出q节点的空间,即free(q);
void delet(LinkList *head, int n) {
LinkList *p = head, *s;
int i = 0;
while (i < n && p != NULL) {
s = p;
p = p->next;
i++;
}
if (p != NULL) {
s->next = p->next;
free(p);
} else {
puts("节点不存在");
}
}
6. 单链表的查找
按序号查找
struct LinkList* search(Linklist L,int i) {
struct LinkList* p;
p = l->next;
j = 0;
while (p && (j < i)) {
p = p->next;
j++;
}
if (i == j) {
return p;
} else {
return NULL;
}
}
按内容查找
struct LinkList* search(Linklist L,DataType key) {
struct LinkList* p;
p = L->next;
while (p != NULL) {
if (p->data != key) {
p = p->next;
j++;
}
return j;
}
}