1. 线性结构
什么是线性结构
?
如:
排队
学号
英文字母表
(ABCDEFG.....)
.......
线性结构中的数据可以是各种各样的,但同一线性表中的元素必定有相同的特性,即数据同一数据对
象,相邻的元素之间存在着顺序关系。
有以下特性:
存在唯一的一个被称为
"
第一个
"
的数据元素
存在唯一的一个被称为
"
最后一个
"
的数据元素
除了第一个元素之外,其它元素均只有一个前驱结点
(
前一个元素
)
除了最后一个元素之外,其它元素均只有一个后驱结点
(
前一个元素
)
线性表的物理结构实现
(
在计算机中怎样存储线性表
):
(1)
顺序结构
线性表中的顺序结构是指用一组地址连续的存储单元依次存储线性表中的数据
====>
数组
(2)
链式结构
链式结构存储的地址不是连续的
====>
链表
2. 问题的引入
我们在定义数组时,需要规定数组的长度,定义好了就不能修改了。
缺陷
:
1
、可能会造成内存资源的浪费
2
、不能完全保证分配的空间一定够用
3
、元素的插入和删除比较麻烦
===>
需要一种可以按需分配,还可以灵活的插入和删除元素的线性结构
:
链表
新内容,却是老面孔:
因为链表就是由一个或者多个结构体指针的指向关系构成
![](https://img-blog.csdnimg.cn/direct/1995e1d683f74b21921b6bdf1fc90895.png)
定义结构体变量的方式
:
1
、 栈空间
:
变量名不能重复,过了生存期就会被释放
(
生存期随它的控制语句
)
2
、 堆空间
:
只需要保存地址即可,生存期随进程持续性
(
或者手动释放
free)
3. 链表的概念
链表就是由一个或者多个含有指针成员变量的结构体,通过其指针成员的地址指向形成一种
逻辑
上的链
式数据结构
我们把每个结构体变量称为该链表的
结点
(note)
首结点
(first):
链表中唯一一个只指向别的结点,而不被其他结点所指向的结点
尾结点
(last):
链表中唯一一个只被别的结点指向,而不指向其他结点的结点,
最后一个结点的指针域为
NULL
因此:
(
1
) 只需要知道首结点的地址,则其他结点的数据都能被访问
(
2
) 保存新的数据,只需要创建一个结点再把这个结点加入到链表中即可
4. 链表的创建
1
、 每获取到一个数据,就创建一个结点
(
结构体
)
2
、 把数据写入到新的结点中
3
、 把结点加入到链表中
4.1
创建过程
4.1.1
从无到有
第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
4.1.2
从少到多
尾插法
新结点接在最后一个节点的后面,即原来的最后一个结点指向新的结点,此时新的结点作为现在的
最后一个结点。
特点
:
先链入的结点在前面,后链入的结点在后面
![](https://img-blog.csdnimg.cn/direct/72f004e4d7754d0e983145a5095cf4b7.png)
last
->
next
=
pnew
;
//
原来的最后一个结点指向新插入的结点
last
=
pnwe
;
//
更新现在的最后一个结点
头插法
新结点接在第一个结点的前面,即新结点指向原来的第一个结点,然后新结点称为了现在的第一个
结点
特点
:
先链入的结点在后面,后链入的结点在前面
![](https://img-blog.csdnimg.cn/direct/b2154625f79241cca75716539ef59a62.png)
pnew
->
next
=
first
;
//
新的结点指向链表中的第一个结点
first
=
pnew
;
//
更新现在的第一个结点
基本操作
:
增,删,改,查
无头结点的单链表
(LinkedList)
带头结点的单链表
(LinkedListWithHead)(
重点
)
双向链表
(BothwayLinkedList)
循环链表
(CircleLinkedList)
5.链表的基本操作
5.1 增加结点
在链表中查找一个数据域为
var
的结点,并在这个结点前面添加一个新的数据域为
x
的新结点
1
、 如果链表中没有找到数据域为
var
的结点,则将新的结点添加到链表的最后面
2
、 如果链表中有多个数据域为
var
的结点,则将新的结点添加第一个数据域为
var
的结点前面
step1 :
遍历链表,查找是否存在数据域为
var
的结点
用两个指针,如
:
p :
用来遍历链表
pre:
用来指向
p
对应的结点的前一个结点
step2:
根据查找情况进行结点的插入
(1) var
对应的结点在链表的中间区域
![](https://img-blog.csdnimg.cn/direct/b575f26af5404b4fbdc093667e7eeab9.png)
(2) var
对应的结点就是链表中的第一个结点
![](https://img-blog.csdnimg.cn/direct/b8dc164168f34525a4dfc1a214a2331b.png)
(
3
)链表中找不到数据域为
var
的结点
![](https://img-blog.csdnimg.cn/direct/84d77eeae4e447e2bbf5044f3c54e1f5.png)
5.2 删除结点
删除数据域为
var
的所有结点,如果没有查找到数据域为
var
的结点,则提示没找到相应的结点。
step1 :
遍历链表,查找是否存在数据域为
var
的结点
用两个指针,如
:
p :
用来遍历链表
pre:
用来指向
p
对应的结点的前一个结点
step2:
当查找到需要进行删除的结点,则对其进行删除
(
1
) 需要删除的结点就是链表中的第一个结点
![](https://img-blog.csdnimg.cn/direct/a8fa2e1b3a9e48bdb59ff81c081059ac.png)
(2)
需要删除的结点在链表中间或者在最后面
![](https://img-blog.csdnimg.cn/direct/7a2df9ad4c014c4daded564de7c77b30.png)
5.3 修改结点的数据
查找链表中数据域为
x
的结点, 并把它的数据域修改为
a
step1 :
遍历链表,查找是否存在数据域为
x
的结点
,
如
:
node* p :
用来遍历链表
step2:
当查找到指定的数据,则修改这个结点的数据域
5.4 创建一个有序的链表
如:
输入:
1 3 5 2 4 0
链表中结点的排布是
: 1 2 3 4 5
1
、 每获取到一个数据,就创建一个结点
(
结构体
)
2
、 把数据写入到新的结点中
3
、 把结点加入到链表中
5.4.1 从无到有
第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
5.4.2 从少到多
(1)
新增的结点的数据域中的数据比链表当前所有结点中的数据域中的数据都要大
新增的结点插入在链表的最后面
如
:
链表中的结点目前的数据为
: 1 3
新增的结点的数据域为
: 5
则链入后为
: 1 3 5
(2)
当找到一个数据域中数据比新增结点数据域中数据要大的结点
(p),
新增的结点链入到
p
的前面
如:
链表中的结点目前的数据为
: 1 3 5
新增的结点的数据域为
: 2
则链入后为
: 1 2 3 5
注意
:
当新增的结点数据域中的数据比链表中第一个结点数据域域中的数据还要小,则进行头插
6. 调试方法
1
、打印法
通过打印相关信息查找程序中的段错误
2
、
GDB
调试
printf
(
"----------%d------------%s----------\n"
,
__LINE__
,
__FUNCTION__
);
gcc main
.
c LinkedList
.
c
-
g
==> -
g
:
生成一个可以使用
GDB
调试的可执行文件夹
gdb
.
/
a
.
out
==>
使用
GDB
调试当前目录下
a
.
out
这个可执行文件
调试命令
:
(
gdb
)
r
//run
从头开始运行程序
(
gdb
)
q
//
退出
7. 带头结点的单链表
头结点: 用来保存链表的属性信息的一个结构体
(
结点
)
作用
:
头领,管理者
(
管理链表,访问链表
)
注意
:
头结点不是链表中的第一个结点,它不属于链表
![](https://img-blog.csdnimg.cn/direct/6fe6a6e20bdc41319763e8b5712036e2.png)
8.带头结点的双向链表
struct
node
{
int
data
;
struct
node
*
next
;
//
指向后一个结点
struct
node
*
prev
;
//
指向前一个结点
};
![](https://img-blog.csdnimg.cn/direct/21a6483dde5c473da67d23b0ed6567bd.png)
8.1 插入结点
8.2 删除结点
9. 带头结点的循环链表
struct
node
{
int
data
;
struct
node
*
next
;
};
![](https://img-blog.csdnimg.cn/direct/047e1e6537f744fdaef348219e667d2c.png)
9.1 判断链表是否有环
有一链表和一个指向这个链表第一个结点的指针
(p)
,怎么来判断这个链表是否循环?
快慢指针:
step1:
定义两个指针
p1, p2
step2:
如:
p1
每次走一步
p2
每次走两步
注意
:
判断
p2
是否为
NULL
step3:
如果
p1 == p2 :
链表有环
如果
p2 == NULL :
链表无环
10. 带头结点的双向循环链表
实现带头结点的双向循环链表的创建,打印,添加新的结点,删除结点
11. 静态链表(了解)
静态链表是用一维数组来实现线性链表,是一个存储结构。
在静态链表中,各个结点存储逻辑上相邻的数据元素,存储在指定的一块内存空间中,数据元素只允许
在这块空间中随机存放。
这种存储结构生成的链表称为
静态链表
,实际上就是一个结构体数组
下面来展示以下各个部分的代码(建议先自己打一遍,再看我的)
单链表代码
LinkedList.c
#include "LinkedList.h"
/*
功能: 初始化一个结点,并把数据写入到结点中
参数:
var: 结点的数据
返回值:
结点的地址
*/
node* node_init(ElemType var)
{
//先申请一个结点那么大的存储空间
node* pnew = malloc(sizeof(node));
//往结点中写入数据
pnew->data = var;
pnew->next = NULL;
return pnew;
}
/*
功能: 创建一个单链表
返回值:
返回第一个结点的地址
*/
node* create_list()
{
node* first = NULL; //用来保存链表中第一个结点的地址
node* last = NULL; //用来保存链表中最后一个结点的地址
//进行链表的创建
while(1)
{
//从终端接受数据
ElemType var;
scanf("%d", &var);
//当接收到0的时候,就结束数据的获取
if(var == 0)
break;
//1、 每获取到一个数据,就创建一个结点(结构体)
//2、 把数据写入到新的结点中
node* new_node = node_init(var);
//3、 把结点加入到链表中
//a. 从无到有
if(first == NULL)
{
//第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
first = new_node;
last = new_node;
}
//b. 从少到多
else
{
//尾插
last->next = new_node; //最后一个结点指向新的结点
last = new_node; //新插入的结点称为现在的最后一个结点
//头插
//new_node->next = first; //新插入的结点的指针域保存原来链表的第一个结点的地址(新的结点指向原来的第一个结点)
//first = new_node;
}
}
return first;
}
/*
功能: 打印链表中结点的数据
参数:
链表的第一个结点的地址
*/
void print_node(node* first)
{
node* temp = first; //遍历指针,用来遍历链表
//对链表进行遍历
while(temp != NULL) //当temp==NULL时说明链表已经遍历完毕
{
printf("%d ", temp->data);
temp = temp->next; //往后遍历
}
printf("\n");
}
/*
功能:
在链表中查找一个数据域为var的结点,并在这个结点前面添加一个新的数据域为x的新结点
1、 如果链表中没有找到数据域为var的结点,则将新的结点添加到链表的最后面
2、 如果链表中有多个数据域为var的结点,则将新的结点添加第一个数据域为var的结点前面
参数:
first: 链表的第一个结点的地址
var: 需要进行查找的数据
x: 需要进行插入的数据
返回值:
返回插入结点之后的链表的第一个结点的地址
*/
node* add_one_node(node* first, ElemType var, ElemType x)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
//进行遍历查找
while(p != NULL)
{
if(p->data == var) //查找到了指定的数据
break;
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
//step2: 根据查找情况进行结点的插入
//初始化需要新增的结点
node* new_node = node_init(x);
//(1) var对应的结点就是链表中的第一个结点
if(p == first) //pre == NULL
{
new_node->next = first; //相当于头插
first = new_node;
}
//(2) 链表中找不到数据域为var的结点
else if(p == NULL)
{
pre->next = new_node; //当p==NULL时,此时pre指向的就是最后一个结点
}
//(3) var对应的结点在链表的中间区域
else
{
//将新增的结点链入到两个已有结点的中间
pre->next = new_node;
new_node->next = p;
}
return first;
}
/*
功能: 删除数据域为var的所有结点,如果没有查找到数据域为var的结点,则提示没找到相应的结点。
参数:
first: 链表的第一个结点的地址
var: 删除数据域为var的所有结点
返回值:
返回链表中第一个结点的地址
*/
node* delete_node(node* first, ElemType var)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
int delete_flag = 0; //删除标志位 0表示链表中未删除结点,1表示删除了结点
//step2: 当查找到需要进行删除的结点,则对其进行删除
while(p != NULL)
{
//查找到了需要进行删除的结点
if(p->data == var)
{
//(1) 需要删除的结点就是链表中的第一个结点
if(p == first)
{
first = first->next; //first指向原来链表中的第二个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = first; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
//(2) 需要删除的结点在链表中间或者在最后面
else //先建立新的链接,再断开旧的链接
{
pre->next = p->next; //p的前一个结点指向p的后一个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = pre->next; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
}
else //没有查找到对应的数据,则继续往后找
{
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
}
if(delete_flag == 0)
printf("链表中结点未发生变化\n");
return first;
}
/*
功能: 查找链表中数据域为x的结点, 并把它的数据域修改为a
参数:
first: 链表的第一个结点的地址
x: 查找链表中数据域为x的结点
a: 查找到指定的结点后,将该结点的数据域修改为a
*/
void update_node(node* first, ElemType x, ElemType a)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点, 如:
node* p = first; //node* p : 用来遍历链表
//step2: 当查找到指定的数据,则修改这个结点的数据域
while(p != NULL)
{
if(p->data == x) //查找链表中数据域为x的结点
{
p->data = a; //查找到指定的结点后,将该结点的数据域修改为a
}
p = p->next; //继续往后遍历
}
}
/*
功能: 创建一个有序的单链表
返回值:
返回第一个结点的地址
*/
node* create_ordered_list()
{
node* first = NULL; //用来保存链表中第一个结点的地址
node* last = NULL; //用来保存链表中最后一个结点的地址
//进行链表的创建
while(1)
{
//从终端接受数据
ElemType var;
scanf("%d", &var);
//当接收到0的时候,就结束数据的获取
if(var == 0)
break;
//1、 每获取到一个数据,就创建一个结点(结构体)
//2、 把数据写入到新的结点中
node* new_node = node_init(var);
//3、 把结点加入到链表中
//a. 从无到有
if(first == NULL)
{
//第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
first = new_node;
last = new_node;
}
//b. 从少到多
else
{
//每次新增一个结点需要与链表中现有的结点进行对比,用来决定新增结点的插入位置
node* p = first; //遍历指针
node* pre = NULL; //指向p对应的结点的前一个结点
while(p != NULL)
{
//当找到一个数据域中数据比新增结点数据域中数据要大的结点(p), 新增的结点链入到p的前面
if(new_node->data <= p->data)
{
//当新增的结点数据域中的数据比链表中第一个结点数据域域中的数据还要小,则进行头插
if(p == first)
{
new_node->next = first;
first = new_node;
break;
}
else //插入到链表的中间
{
pre->next = new_node;
new_node->next = p;
break;
}
}
pre = p; //先让pre保存p现在指向的结点
p = p->next; //p继续往后遍历
}
//新增的结点的数据域中的数据比链表当前所有结点中的数据域中的数据都要大
//新增的结点插入在链表的最后面
if(p == NULL)
{
//尾插
last->next = new_node;
last = new_node;
}
}
}
return first;
}
LinkedList.h
#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__
#include <stdio.h>//尖括号 ,在系统指定的路径下查找头文件
#include <stdlib.h>
typedef int ElemType; //方便存储的数据的类型做修改
//typedef: 给已有的数据类型起一个别名
//在这里是给struct Node起了一个短一点的名字node
typedef struct Node
{
ElemType data; //数据域
struct Node* next; //指针域
}node;
node* node_init(ElemType var);
node* create_list();
void print_node(node* first);
node* add_one_node(node* first, ElemType var, ElemType x);
node* delete_node(node* first, ElemType var);
void update_node(node* first, ElemType x, ElemType a);
node* create_ordered_list();
#endif
main.c
#include "LinkedList.h" //双引号
//先在当前目录下查找头文件,再到用户指定的路径查找头文件
//最后到系统指定的路径查找头文件
int main()
{
//创建一个单链表
//node* first = create_list();
//创建一个有序链表
node* first = create_ordered_list();
//打印链表中结点的数据
print_node(first);
//添加新的结点
// printf("链表中添加新的结点: \n");
// first = add_one_node(first, 10, 100);
// print_node(first);
//删除结点
// printf("链表中删除结点: \n");
// first= delete_node(first, 100);
//修改结点
// printf("修改链表中的结点: \n");
// update_node(first, 10, 100);
// print_node(first);
return 0;
}
带头结点的单链表
LinkedListWithHead.c
#include "LinkedListWithHead.h"
/*
功能: 初始化一个结点,并把数据写入到结点中
参数:
var: 结点的数据
返回值:
结点的地址
*/
node* node_init(ElemType var)
{
//先申请一个结点那么大的存储空间
node* pnew = malloc(sizeof(node));
//往结点中写入数据
pnew->data = var;
pnew->next = NULL;
return pnew;
}
/*
功能: 初始化一个头结点
返回值:
返回头结点的地址
*/
List* list_init()
{
//先申请一个头结点那么大的存储空间
List* list = malloc(sizeof(List));
list->first = NULL;
list->last = NULL;
list->num = 0;
return list;
}
/*
功能: 创建一个带头结点的单链表
参数:
list: 头结点的地址
返回值:
头结点的地址
*/
List* create_list(List* list)
{
//进行链表的创建
while(1)
{
//从终端接受数据
ElemType var;
scanf("%d", &var);
//当接收到0的时候,就结束数据的获取
if(var == 0)
break;
//1、 每获取到一个数据,就创建一个结点(结构体)
//2、 把数据写入到新的结点中
node* new_node = node_init(var);
//3、 把结点加入到链表中
//a. 从无到有
if(list->first == NULL)
{
//第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
list->first = new_node;
list->last = new_node;
list->num++; //链表中结点数量加一
}
//b. 从少到多
else
{
//尾插
list->last->next = new_node; //最后一个结点指向新的结点
list->last = new_node; //新插入的结点称为现在的最后一个结点
list->num++; //链表中结点数量加一
}
}
return list;
}
/*
功能: 打印链表中结点的数据
参数:
头结点的地址
*/
void print_node(List* list)
{
node* temp = list->first; //遍历指针,用来遍历链表
int num = list->num;
//对链表进行遍历
while(num != 0) //当num == 0时说明链表已经遍历完毕
{
printf("%d ", temp->data);
temp = temp->next; //往后遍历
num--;
}
printf("\n");
printf("目前链表中的结点数为: %d\n", list->num);
}
/*
功能:
在链表中查找一个数据域为var的结点,并在这个结点前面添加一个新的数据域为x的新结点
1、 如果链表中没有找到数据域为var的结点,则将新的结点添加到链表的最后面
2、 如果链表中有多个数据域为var的结点,则将新的结点添加第一个数据域为var的结点前面
参数:
list: 头结点的地址
var: 需要进行查找的数据
x: 需要进行插入的数据
无返回值
*/
void add_one_node(List* list, ElemType var, ElemType x)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = list->first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
//进行遍历查找
while(p != NULL)
{
if(p->data == var) //查找到了指定的数据
break;
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
//step2: 根据查找情况进行结点的插入
//初始化需要新增的结点
node* new_node = node_init(x);
//(1) var对应的结点就是链表中的第一个结点
if(p == list->first) //pre == NULL
{
new_node->next = list->first; //相当于头插
list->first = new_node;
}
//(2) 链表中找不到数据域为var的结点
else if(p == NULL)
{
pre->next = new_node; //当p==NULL时,此时pre指向的就是最后一个结点
list->last = new_node; //last指向新的链表中的最后一个结点
}
//(3) var对应的结点在链表的中间区域
else
{
//将新增的结点链入到两个已有结点的中间
pre->next = new_node;
new_node->next = p;
}
list->num++; //链表中的结点加一
return ;
}
/*
功能: 删除数据域为var的所有结点,如果没有查找到数据域为var的结点,则提示没找到相应的结点。
参数:
list: 头结点的地址
var: 删除数据域为var的所有结点
返回值:
返回链表中第一个结点的地址
*/
void delete_node(List* list, ElemType var)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = list->first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
int delete_flag = 0; //删除标志位 0表示链表中未删除结点,1表示删除了结点
//step2: 当查找到需要进行删除的结点,则对其进行删除
while(p != NULL)
{
//查找到了需要进行删除的结点
if(p->data == var)
{
//(1) 需要删除的结点就是链表中的第一个结点
if(p == list->first)
{
list->first = list->first->next; //first指向原来链表中的第二个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = list->first; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
//(2) 需要删除的结点在链表最后面
else if(p == list->last)
{
list->last = pre; //原来的倒数第二个结点成为新的最后一个结点
list->last->next = NULL; //断开需要删除的结点与原来链表之间的联系
free(p);
list->num--;
break;
}
else //先建立新的链接,再断开旧的链接
{
pre->next = p->next; //p的前一个结点指向p的后一个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = pre->next; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
list->num--; //链表中结点的数量减一
}
else //没有查找到对应的数据,则继续往后找
{
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
}
if(delete_flag == 0)
printf("链表中结点未发生变化\n");
return ;
}
/*
功能: 查找链表中数据域为x的结点, 并把它的数据域修改为a
参数:
list: 头结点的地址
x: 查找链表中数据域为x的结点
a: 查找到指定的结点后,将该结点的数据域修改为a
*/
void update_node(List* list, ElemType x, ElemType a)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点, 如:
node* p = list->first; //node* p : 用来遍历链表
//step2: 当查找到指定的数据,则修改这个结点的数据域
while(p != NULL)
{
if(p->data == x) //查找链表中数据域为x的结点
{
p->data = a; //查找到指定的结点后,将该结点的数据域修改为a
}
p = p->next; //继续往后遍历
}
}
/*
功能:
链表逆序
如:
链表中各结点的数据分别为: 1 2 3 4 5
执行完链表逆序的函数之后,链表中各结点的数据分别为:5 4 3 2 1
参数:
list: 头结点的地址
*/
void reverse_order_list(List* list)
{
//step1: 定义两个指针,一个用来遍历链表(p),一个用来指向需要拆下来的结点(pchai)
node* p = list->first;
node* pchai = NULL;
//step2: 把头结点与链表的联系断开
list->first = NULL;
list->last = NULL;
list->num = 0;
while(p != NULL)
{
//step3: pchai指向需要拆下来的结点,p往后遍历
pchai = p; //指向需要拆下来的结点
p = p->next; //p往后遍历
//step4: 把需要拆下来的结点与原来链表之间的联系断开
pchai->next = NULL; //将需要拆下的结点与原来链表之间的联系断开
//step5: 用拆下来的结点重新创建一个新的链表
//从无到有
if(list->first == NULL)
{
list->first = pchai;
list->last = pchai;
list->num++;
}
//从少到多
else
{
//头插
pchai->next = list->first;
list->first = pchai;
list->num++;
}
}
}
/*
功能: 对一个链表中的结点进行排序
参数:
list: 头结点的地址
*/
void list_sort(List* list)
{
//step1: 定义两个指针,一个用来遍历链表(p),一个用来指向需要拆下来的结点(pchai)
node* p = list->first;
node* pchai = NULL;
//step2: 把头结点与链表的联系断开
list->first = NULL;
list->last = NULL;
list->num = 0;
while(p != NULL)
{
//step3: pchai指向需要拆下来的结点,p往后遍历
pchai = p; //指向需要拆下来的结点
p = p->next; //p往后遍历
//step4: 把需要拆下来的结点与原来链表之间的联系断开
pchai->next = NULL; //将需要拆下的结点与原来链表之间的联系断开
//step5: 用拆下来的结点重新创建一个新的有序的链表
//从无到有
if(list->first == NULL)
{
list->first = pchai;
list->last = pchai;
list->num++;
}
//从少到多
else
{
//每次新增一个结点需要与现有的结点中的数据作对比,用来决定插入的位置
node* p1 = list->first; //遍历指针,遍历进行结点插入的链表
node* pre = NULL; //指向p1对应的结点的前一个结点
while(p1 != NULL)
{
//找到一个比新链入的结点数据域大的结点p1, 则把新拆下来的结点链入到p1前面
if(p1->data >= pchai->data)
{
if(list->first == NULL) //头插
{
pchai->next = list->first;
list->first = pchai;
list->num++;
break;
}
else //在p1的前面链入一个新的结点
{
pre->next = pchai;
pchai->next = p1;
list->num++;
break;
}
}
pre = p1; //先保存p1现在指向的结点
p1 = p1->next; //p1继续往后遍历
}
//新拆下来的结点中的数据域比现有链表中所有结点的数据域都要大
if(p1 == NULL) //尾插
{
list->last->next = pchai;
list->last = pchai;
list->num++;
}
}
}
}
LinkedListWithHead.h
#ifndef __LINKEDLISTWITHHEAD_H__
#define __LINKEDLISTWITHHEAD_H__
#include <stdio.h>//尖括号 ,在系统指定的路径下查找头文件
#include <stdlib.h>
typedef int ElemType; //方便存储的数据的类型做修改
//typedef: 给已有的数据类型起一个别名
//在这里是给struct Node起了一个短一点的名字node
typedef struct Node
{
ElemType data; //数据域
struct Node* next; //指针域
}node;
//定义头结点的类型,用于存储链表的一些属性信息
typedef struct LinkedList
{
node* first; //用来指向链表中的第一个结点
node* last; //用来指向链表中的最后一个结点
int num; //记录链表中结点的数量
}List;
node* node_init(ElemType var);
List* list_init();
List* create_list(List* list);
void print_node(List* list);
void add_one_node(List* list, ElemType var, ElemType x);
void delete_node(List* list, ElemType var);
void update_node(List* list, ElemType x, ElemType a);
void reverse_order_list(List* list);
void list_sort(List* list);
#endif
main.c
#include "LinkedListWithHead.h" //双引号
//先在当前目录下查找头文件,再到用户指定的路径查找头文件
//最后到系统指定的路径查找头文件
int main()
{
//创建一个头结点
List* list = list_init();
//创建一个带头结点的单链表
list = create_list(list);
//打印链表中的结点的数据
print_node(list);
//添加一个结点
//add_one_node(list, 10, 100);
//删除指定的结点
delete_node(list, 100);
print_node(list);
//修改指定的结点
//printf("修改链表中结点的数据域: \n");
//update_node(list, 10, 100);
//链表逆序
//printf("链表逆序:\n");
//reverse_order_list(list);
//链表排序
//printf("链表排序: \n");
//list_sort(list);
//print_node(list);
return 0;
}
带头节点的双向链表
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; //方便存储的数据的类型做修改
//typedef: 给已有的数据类型起一个别名
//在这里是给struct Node起了一个短一点的名字node
typedef struct Node
{
ElemType data; //数据域
struct Node* next; //指针域 ,指向逻辑上的后一个结点
struct Node* prev; //指针域,指向逻辑上的前一个结点
}node;
//定义头结点的类型,用于存储链表的一些属性信息
typedef struct LinkedList
{
node* first; //用来指向链表中的第一个结点
node* last; //用来指向链表中的最后一个结点
int num; //记录链表中结点的数量
}List;
/*
功能: 初始化一个结点,并把数据写入到结点中
参数:
var: 结点的数据
返回值:
结点的地址
*/
node* node_init(ElemType var)
{
//先申请一个结点那么大的存储空间
node* pnew = malloc(sizeof(node));
//往结点中写入数据
pnew->data = var;
pnew->next = NULL;
pnew->prev = NULL;
return pnew;
}
/*
功能: 初始化一个头结点
返回值:
返回头结点的地址
*/
List* list_init()
{
//先申请一个头结点那么大的存储空间
List* list = malloc(sizeof(List));
list->first = NULL;
list->last = NULL;
list->num = 0;
return list;
}
/*
功能: 创建一个带头结点的双向链表
参数:
list: 头结点的地址
返回值:
头结点的地址
*/
List* create_list(List* list)
{
//进行链表的创建
while(1)
{
//从终端接受数据
ElemType var;
scanf("%d", &var);
//当接收到0的时候,就结束数据的获取
if(var == 0)
break;
//1、 每获取到一个数据,就创建一个结点(结构体)
//2、 把数据写入到新的结点中
node* new_node = node_init(var);
//3、 把结点加入到链表中
//a. 从无到有
if(list->first == NULL)
{
//第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
list->first = new_node;
list->last = new_node;
list->num++; //链表中结点数量加一
}
//b. 从少到多
else
{
//尾插
list->last->next = new_node; //最后一个结点指向新的结点
new_node->prev = list->last; //新的结点的prev指针指向原来链表中的最后一个结点
list->last = new_node; //新插入的结点称为现在的最后一个结点
list->num++; //链表中结点数量加一
}
}
return list;
}
/*
功能: 打印链表中结点的数据
参数:
头结点的地址
*/
void print_node(List* list)
{
node* temp = list->first; //遍历指针,用来遍历链表
int num = list->num;
//对链表进行遍历
while(num != 0) //当num == 0时说明链表已经遍历完毕
{
printf("%d ", temp->data);
temp = temp->next; //往后遍历
num--;
}
printf("\n");
printf("目前链表中的结点数为: %d\n", list->num);
node* temp1 = list->last; //遍历指针,从后往前遍历链表
int num1 = list->num;
//对链表进行遍历
while(num1 != 0) //当num == 0时说明链表已经遍历完毕
{
printf("%d ", temp1->data);
temp1 = temp1->prev; //往前遍历
num1--;
}
printf("\n");
printf("目前链表中的结点数为: %d\n", list->num);
}
/*
功能:
在链表中查找一个数据域为var的结点,并在这个结点前面添加一个新的数据域为x的新结点
1、 如果链表中没有找到数据域为var的结点,则将新的结点添加到链表的最后面
2、 如果链表中有多个数据域为var的结点,则将新的结点添加第一个数据域为var的结点前面
参数:
list: 头结点的地址
var: 需要进行查找的数据
x: 需要进行插入的数据
无返回值
*/
void add_one_node(List* list, ElemType var, ElemType x)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点
node* p = list->first; //p : 用来遍历链表
//进行遍历查找
while(p != NULL)
{
if(p->data == var) //查找到了指定的数据
break;
p = p->next; //p继续往后遍历
}
//step2: 根据查找情况进行结点的插入
//初始化需要新增的结点
node* new_node = node_init(x);
//(1) var对应的结点就是链表中的第一个结点
if(p == list->first)
{
//(新的结点链接到原来链表的第一个结点前面)
new_node->next = list->first; //新的结点的后驱结点为原来链表中的第一个结点
list->first->prev = new_node; //原来链表中的第一个结点前面多了一个新的结点new_node
list->first = new_node; //更新链表中的第一个结点
}
//(2) 链表中找不到数据域为var的结点
else if(p == NULL)
{
//新的结点链接在原来链表的最后一个结点的后面
list->last->next = new_node; //原来链表中的最后一个结点的next指向新的结点
new_node->prev = list->last; //新链入的结点的prev指向原来链表中的最后一个结点
list->last = new_node; //last指向新的链表中的最后一个结点
}
//(3) var对应的结点在链表的中间区域
else
{
//将新增的结点链入到两个已有结点的中间
//注意: 先建立新的链接,再断开旧的链接
new_node->next = p;
new_node->prev = p->prev;
p->prev->next = new_node;
p->prev = new_node;
}
list->num++; //链表中的结点加一
return ;
}
/*
功能: 删除链表中数据域为var的结点,如果有多个数据域为var的结点,则全部删除
参数:
list: 链表的头结点的地址
var: 删除链表中数据域为var的结点
*/
void delete_node(List* list, ElemType var)
{
node* p = list->first; //遍历指针
//遍历链表中各结点,查找需要进行删除的结点
while(p != NULL)
{
//找到了需要删除的结点
if(p->data == var)
{
list->num--; //结点数量减一
//(1): 需要删除的结点是链表中的第一个结点
if(p == list->first)
{
list->first = list->first->next; //原来的第二个结点成为新的第一个结点
p->next = NULL; //断开需要删除的结点与原来链表之间的联系
free(p);
p = list->first; //p继续遍历链表
if(list->num == 0) //如果删除第一个结点之后就没有结点了
{
list->last = NULL;
}
else //后面还有其他结点
{
list->first->prev = NULL;
}
}
//(2)需要删除的结点是最后一个
else if(p == list->last)
{
list->last = p->prev; //原来的倒数第二个结点成为新的最后一个结点
list->last->next = NULL; //断开需要删除的结点与原来链表之间的联系
p->prev = NULL;
free(p);
break;
}
//(3)删除中间结点
else
{
node* psave = p->next;
//先建立p前面的结点与p后面的结点之间的联系
p->prev->next = p->next;
p->next->prev = p->prev;
断开需要删除的结点与原来链表之间的联系
p->prev = NULL;
p->next = NULL;
free(p);
p = psave; //p继续往后遍历
}
}
else
{
p = p->next; //本结点不是需要删除的结点,则继续往后遍历
}
}
}
int main()
{
//创建一个头结点
List* listA = list_init();
//创建一个带头结点的单链表
listA = create_list(listA);
printf("ListA : ");
print_node(listA);
//printf("新增结点: \n");
//add_one_node(listA, 10, 100);
printf("删除结点:\n");
delete_node(listA, 10);
print_node(listA);
return 0;
}
带头节点的循环链表
#include <stdio.h>//尖括号 ,在系统指定的路径下查找头文件
#include <stdlib.h>
typedef int ElemType; //方便存储的数据的类型做修改
//typedef: 给已有的数据类型起一个别名
//在这里是给struct Node起了一个短一点的名字node
typedef struct Node
{
ElemType data; //数据域
struct Node* next; //指针域
}node;
//定义头结点的类型,用于存储链表的一些属性信息
typedef struct LinkedList
{
node* first; //用来指向链表中的第一个结点
node* last; //用来指向链表中的最后一个结点
int num; //记录链表中结点的数量
}List;
/*
功能: 初始化一个结点,并把数据写入到结点中
参数:
var: 结点的数据
返回值:
结点的地址
*/
node* node_init(ElemType var)
{
//先申请一个结点那么大的存储空间
node* pnew = malloc(sizeof(node));
//往结点中写入数据
pnew->data = var;
pnew->next = NULL;
return pnew;
}
/*
功能: 初始化一个头结点
返回值:
返回头结点的地址
*/
List* list_init()
{
//先申请一个头结点那么大的存储空间
List* list = malloc(sizeof(List));
list->first = NULL;
list->last = NULL;
list->num = 0;
return list;
}
/*
功能: 创建一个带头结点的单链表
参数:
list: 头结点的地址
返回值:
头结点的地址
*/
List* create_list(List* list)
{
//进行链表的创建
while(1)
{
//从终端接受数据
ElemType var;
scanf("%d", &var);
//当接收到0的时候,就结束数据的获取
if(var == 0)
break;
//1、 每获取到一个数据,就创建一个结点(结构体)
//2、 把数据写入到新的结点中
node* new_node = node_init(var);
//3、 把结点加入到链表中
//a. 从无到有
if(list->first == NULL)
{
//第一个结点诞生,此时第一个结点和最后一个结点都是这一个唯一的结点
list->first = new_node;
list->last = new_node;
list->num++; //链表中结点数量加一
}
//b. 从少到多
else
{
//尾插
list->last->next = new_node; //最后一个结点指向新的结点
list->last = new_node; //新插入的结点称为现在的最后一个结点
list->num++; //链表中结点数量加一
}
}
if(list->num != 0)
{
//链表的最后一个结点的next指向链表的第一个结点
//可以通过链表的最后一个结点访问到链表的第一个结点
list->last->next = list->first;
}
return list;
}
/*
功能: 打印链表中结点的数据
参数:
头结点的地址
*/
void print_node(List* list)
{
node* temp = list->first; //遍历指针,用来遍历链表
int num = (list->num) * 2;
//对链表进行遍历
while(num != 0) //当num == 0时说明链表已经遍历完毕
{
printf("%d ", temp->data);
temp = temp->next; //往后遍历
num--;
}
printf("\n");
printf("目前链表中的结点数为: %d\n", list->num);
}
/*
功能:
在链表中查找一个数据域为var的结点,并在这个结点前面添加一个新的数据域为x的新结点
1、 如果链表中没有找到数据域为var的结点,则将新的结点添加到链表的最后面
2、 如果链表中有多个数据域为var的结点,则将新的结点添加第一个数据域为var的结点前面
参数:
list: 头结点的地址
var: 需要进行查找的数据
x: 需要进行插入的数据
无返回值
*/
void add_one_node(List* list, ElemType var, ElemType x)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = list->first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
//进行遍历查找
int n = list->num;
while(n--)
{
if(p->data == var) //查找到了指定的数据
break;
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
//step2: 根据查找情况进行结点的插入
//初始化需要新增的结点
node* new_node = node_init(x);
//(1) var对应的结点就是链表中的第一个结点
if(p == list->first && n != -1)
{
new_node->next = list->first; //相当于头插
list->first = new_node;
}
//(2) 链表中找不到数据域为var的结点
else if(n == -1)
{
list->last->next = new_node; //当p==NULL时,此时pre指向的就是最后一个结点
list->last = new_node; //last指向新的链表中的最后一个结点
}
//(3) var对应的结点在链表的中间区域
else
{
//将新增的结点链入到两个已有结点的中间
pre->next = new_node;
new_node->next = p;
}
list->num++; //链表中的结点加一
//链表的最后一个结点的next指向链表的第一个结点
//可以通过链表的最后一个结点访问到链表的第一个结点
list->last->next = list->first;
return ;
}
/*
功能: 删除数据域为var的所有结点,如果没有查找到数据域为var的结点,则提示没找到相应的结点。
参数:
list: 头结点的地址
var: 删除数据域为var的所有结点
返回值:
返回链表中第一个结点的地址
*/
void delete_node(List* list, ElemType var)
{
//step1 : 遍历链表,查找是否存在数据域为var的结点用两个指针,如:
node* p = list->first; //p : 用来遍历链表
node* pre = NULL; //pre: 用来指向p对应的结点的前一个结点
int delete_flag = 0; //删除标志位 0表示链表中未删除结点,1表示删除了结点
//step2: 当查找到需要进行删除的结点,则对其进行删除
int n = list->num;
while(n--)
{
//查找到了需要进行删除的结点
if(p->data == var)
{
//(1) 需要删除的结点就是链表中的第一个结点
if(p == list->first)
{
list->first = list->first->next; //first指向原来链表中的第二个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = list->first; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
//(2) 需要删除的结点在链表最后面
else if(p == list->last)
{
list->last = pre; //原来的倒数第二个结点成为新的最后一个结点
list->last->next = NULL; //断开需要删除的结点与原来链表之间的联系
free(p);
list->num--;
break;
}
else //先建立新的链接,再断开旧的链接
{
pre->next = p->next; //p的前一个结点指向p的后一个结点
p->next = NULL; //断掉需要删除的结点与链表之间的关系
free(p); //释放p指向的空间
p = pre->next; //p继续往后遍历
delete_flag = 1; //1表示删除了结点
}
list->num--; //链表中结点的数量减一
}
else //没有查找到对应的数据,则继续往后找
{
pre = p; //pre先保存p现在指向的结点
p = p->next; //p继续往后遍历
}
}
if(delete_flag == 0)
printf("链表中结点未发生变化\n");
if(list->num != 0)
{
//链表的最后一个结点的next指向链表的第一个结点
//可以通过链表的最后一个结点访问到链表的第一个结点
list->last->next = list->first;
}
return ;
}
int main()
{
//创建一个头结点
List* listA = list_init();
//创建一个带头结点的单链表
listA = create_list(listA);
printf("ListA : ");
print_node(listA);
printf("添加结点:\n");
add_one_node(listA, 10, 100);
printf("ListA : ");
print_node(listA);
//printf("删除结点: \n");
//delete_node(listA, 10);
//printf("ListA : ");
//print_node(listA);
return 0;
}
带头节点的双向循环链表
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
struct node //数据结点的数据类型
{
ElemType data;/*数据域*/
struct node *next;/*指针域*/
struct node *prev;
};
typedef struct LinkedList //头结点的数据类型
{
struct node *first;//指向第一个数据结点
struct node *last;//指向最后一个数据结点
int num;//保存该链表中数据结点的数量
}List;
/*
struct List *create_list()
功能:用来根据用户的输入,创建一个双向链表
返回值:
链表的首地址
*/
List *create_list()
{
ElemType d;//保存输入的数据
struct node *pnew=NULL;//指向新创建的结点
/*step0: 创建头结点并初始化*/
List *list = malloc(sizeof(*list));
list->first = NULL;//指向第一个数据结点
list->last = NULL;//指向最后一个数据结点
list->num = 0;//保存数据结点的数量
while(1)
{
/*step1:每需要保存一个数据,就创建一个结点(结构体)*/
scanf("%d",&d);
if(d == 0)//输入0结束
{
break;
}
pnew = malloc(sizeof(*pnew));
/*step2:把数据写入到结点中去*/
pnew->data = d;
pnew->next = NULL;
pnew->prev = NULL;
/*step3:把结点加入到链表*/
if(list->first == NULL)//从无到有,此时,首结点和尾结点都是pnew
{
list->first = pnew;
list->last = pnew;
}
else//从少到多
{
/*尾插法*/
list->last->next = pnew;//尾结点指向新结点
pnew->prev = list->last;//新结点的prev也指向原来的尾结点
list->last = pnew;//尾结点被新结点替代
#if 0
/*头插法*/
pnew->next = list->first;//新结点指向首结点
list->first->prev = pnew;//首结点的prev指向pnew
list->first = pnew;//首结点被新结点替代
#endif
}
list->num++;
}
if(list->num !=0 )
{
list->last->next = list->first;
list->first->prev = list->last;
}
return list;
}
/*
add_a_node:在链表h中的值为x的结点的前面,增加一个结点,
新增加的结点的值为a,然后把新链表的首地址返回。
假如没有值为x的结点,则在尾部添加。
假如有多个值为x的结点,则只在第一个结点前面添加
*/
List *add_a_node(List *list,ElemType a,ElemType x)
{
struct node *p=NULL;//遍历指针
/*step1:创建一个新的结点,并赋值a*/
struct node *pnew = malloc(sizeof(*pnew));
pnew->data = a;
pnew->next = NULL;
pnew->prev = NULL;
/*step2:找到值为x的结点*/
int n = list->num;
p = list->first;
while(n--)
{
if(p->data == x)//找到了
{
break;
}
else //此结点不是,找下一个结点
{
p = p->next;
}
}
/*step3:把新建的结点插入链表*/
if(n != -1)//找到了
{
if(p == list->first)//找到的结点是首结点,用头插
{
pnew->next = list->first;
list->first->prev = pnew;
list->first = pnew;
}
else//插中间
{
pnew->next = p;//给pnew的成员赋值
pnew->prev = p->prev;//给pnew的成员赋值
p->prev->next = pnew;//给p->prev的成员赋值
p->prev = pnew;//给p的成员赋值
}
}
else//没找到,尾插
{
list->last->next = pnew;
pnew->prev = list->last;
list->last = pnew;
}
list->num++;
if(list->num != 0)
{
list->last->next = list->first;
list->first->prev = list->last;
}
return list;
}
/*
delete_node:在h指向的链表中,删除所有值为x的结点
*/
void delete_node(List *list,ElemType x)
{
struct node *p=NULL;//p为遍历指针
int n = list->num;
p = list->first;
while(n--)
{
if(p->data == x)//找到了,就删除
{
list->num--;
if(p == list->first)//如果要删首结点
{
list->first = list->first->next;
p->next = NULL;//断链接
free(p);//释放p指向的空间
p = list->first;//p往后走
if(list->num == 0)//如果删除首结点了就没有结点了,则last也要更新为NULL
{
list->first = NULL;
list->last = NULL;
}
else
{
list->first->prev = NULL;//如果只有一个结点,则first走到了NULL
}
}
else if(p == list->last)//如果删除尾结点
{
list->last = p->prev;//last往前走
list->last->next = NULL;//断链接
p->prev = NULL;//断链接
free(p);
break;
}
else//删除中间结点,先给前后的成员赋值,再给p的成员赋值
{
struct node *psave = p->next;
p->prev->next = p->next;
p->next->prev = p->prev;
p->prev = NULL;
p->next = NULL;
free(p);
p = psave;//p往后走
}
}
else //本次结点不是,寻找下一个
{
p = p->next;
}
}
if(list->num != 0)
{
list->last->next = list->first;
list->first->prev = list->last;
}
}
/*
update_node:把h指向的链表中值为x的结点,赋值为a
*/
List *update_node(List *list,ElemType x, ElemType a)
{
struct node *p = list->first;
int n = list->num;
while(n--)
{
if(p->data == x)
{
p->data = a;
}
p = p->next;
}
return list;
}
/*
print_list()
功能:把指定的链表中的结点的数据打印出来
*/
void print_list( List *list)
{
int n = list->num*2;
struct node *p = list->last;
while(n--)
{
printf("%d ",p->data);
p = p->prev;
}
putchar('\n');
}
int main()
{
List *list = create_list();
//delete_node(list, 10);
add_a_node(list, 10, 100);
print_list(list);
return 0;
}