链表的常见操作
链表分为单向链表,双向链表,循环链表,双向循环链表
其中常见操作有:创建,增加,删除,修改,查找,以及单向链表的逆置
链表的定义
链表是一种常见的数据结构链表的存储元素的个数是不受限定的,当要添加更多元素时,存储的个数会随之增加,这种方式就是链表。
链表结点内容分为指针域和数据域,链表就是由这样的一个个结点通过指针域指向连接而成。
单向链表
- 首先定义一个单向链表
typedef struct a {
int x; //数据域
struct a * next; //指针域
}Item; //Item为链表的一个结点
typedef Item * linklist; //linklist为链表
一、链表的创建分为两种:
一种是头插法创建链表
另一种是尾插法创建链表
1.头插法创建链表
核心代码
pnew->next = head->next; //让新结点指向第一个结点
head->next = pnew; //让头结点指向新结点
完整代码
linklist toucha() {
linklist head, pnew; //定义头结点和新结点
head = (linklist) malloc (sizeof(Item)); //为头结点分配空间
head->next = NULL;
int x; //用x存储临时值
printf("请输入一个数(输入负数结束输入):");
scanf("%d", &x);
while (x>=0) { //若x为非负数进入循环
pnew = (linklist) malloc (sizeof(Item)); //为新结点分配空间
pnew->x = x; //将临时值赋值给新结点的数据域
pnew->next = head->next; //让新结点指向第一个结点
head->next = pnew; //让头结点指向新结点
printf("请输入下一个数(输入负数结束输入):");
scanf("%d", &x); //获取下一个值
}
return head; //返回头结点的地址
}
2.尾插法创建链表
核心代码
q->next = NULL; //q为新结点,让新结点指向空
p->next = q; //p为上一个结点,让上一个结点指向新结点
p = q; //让p指向当前结点
完整代码
linklist chuangjian () {
int x; //x存储临时值
printf("请输入一个数(输入负数结束输入):");
scanf("%d", &x); //获取临时值
linklist head, p, q; //定义头结点head,尾指针p,新结点q
head = (linklist) malloc (sizeof(Item)); //为头结点分配空间
head->next = NULL; //让头结点指向空
p = head; //让p指向头结点
while (x>=0) { //若临时值非负,则进入循环
q = (linklist) malloc (sizeof(Item)); //为新结点分配空间
q->x = x; //将临时值赋值给新结点的数据域
q->next = NULL; //让新结点指向空
p->next = q; //让上一个结点指向新结点
p = q; //让p指向当前结点
printf("请输入下一个数(输入负数结束输入):");
scanf("%d", &x); //获取临时值
}
return head; //返回头结点的地址
}
二、链表的遍历
void Print(linklist phead) {
linklist p = phead->next; //让p指向第一个结点
while (p) { //若p不为最后一个结点
printf("%d\n", p->x); //打印结点内容
p = p->next; //让p指向下一个结点
}
}
三、链表的增加
void add (linklist phead) {
linklist p = (linklist) malloc (sizeof(Item)); //创建新结点并为之分配空间
linklist q = phead; //q为尾指针
if (!q) { //若分配空间失败
puts("添加失败!"); //输出错误信息
return; //退出函数
}
else { //若分配成功
puts("请输入添加的数:");
scanf("%d", &p->x); //获取添加的值
while(q->next) //让q指向最后一个结点
q = q->next;
q->next = p; //让q指向新结点
p->next = NULL; //让新结点指向空
}
}
四、链表的删除
void del (linklist phead) {
int x; //存储要删除的数字
linklist p = phead; //p为前一个结点
linklist q = phead->next; //q为要删除的结点
puts("请输入你要删除的数字:");
scanf("%d", &x); //获取要删除的数字
while (q && q->x != x) { //若q不为最后一个结点且不是要删除的结点
p = q; //让p指向当前结点
q = q->next; //让q指向下一个结点
}
if (q==NULL) //若q指向最后一个结点且不为要删除的结点
puts("未找到该数字!"); //提示未找到该数字
else { //若找到要删除的结点
p->next = q->next; //让前一个结点指向要删除的下一个结点
free(q); //释放q所在的空间
puts("已删除!"); //输出已删除的信息
}
}
五、链表的修改
void change(linklist phead) {
linklist p = phead->next; //p为要修改的结点
int x; //存储临时值
printf("请输入要修改的数字:");
scanf("%d", &x); //获取要修改的数字
while (p && p->x != x) //若p不为最后一个结点且不是要修改的结点
p = p->next; //让p指向下一个结点
if (!p) //若p为最后一个结点且不是要删除的结点
puts("未找到该数字!"); //提示未找到该数字
else { //若找到
printf("请输入修改之后的数字:");
scanf("%d", &p->x); //修改数字
}
}
六、链表的查找
void find (linklist phead) {
linklist p = phead->next; //p为要查找的结点
int x; //存储要查找的数字
printf("请输入要查找的数字:");
scanf("%d", &x); //获取要查找的数字
while (p && p->x != x) //若p不为最后一个结点且不是要修改的结点
p = p->next; //让p指向下一个结点
if (!p) //若p为最后一个结点且不是要查找的结点
puts("未找到该数字!"); //提示未找到该数字
else //若找到
printf("%d\n", p->x); //打印数字
}
七、链表逆置
void NiZhi (linklist phead) {
linklist p1, p2;
p1 = p2 = phead->next;
phead->next = NULL;
while (p1) {
p2 = p1->next;
p1->next = phead->next;
phead->next = p1;
p1 = p2;
}
}
八、链表排序(冒泡排序)
1.交换数据域
void sort (linklist phead) {
linklist q = phead->next ; //让q指向第一个结点
int flag = 1 ;
for (int i=0; i<length && flag; i++) { //lengeh为链表元素个数,若链表无序且未完成排序
q = phead->next ; //让q指向第一个结点
flag = 0 ; //假定链表有序
for (; q->next!=NULL; q = q->next) //q从第一个结点遍历至倒数第二个结点
if (q->x>q->next->x) {
flag = 1 ;
Item r = q->Student ;
q->Student = q->next->Student ;
q->next->Student = r ;
}
}
}
2.交换指针域
void sort(linklist phead) {
int change = 1; //假定链表无序
for (int i=0; i<length-1 && change; i++) { //length为链表元素个数,
linklist p_pre = phead; //p_pre指向头结点
linklist p = p_pre->next; //p指向第一个结点
linklist q = p->next; //q指向第二个结点
change = 0; //假定链表有序
for (; q!=NULL; p = p->next) { //若q不指向空,则进入循环
if (p->x>q->x) { //若前一个值大于后一个值
change = 1; //链表无序
p_pre->next = q; //使p的前一个结点指向p的后一个结点
p->next = q->next; //使p指向q的后一个结点
q->next = p; //使q指向p
//交换p,q
linklist r = p; //使r指向p
p = q; //使p指向q
q = r; //使q指向r
}
p_pre = p; //使p_pre指向当前结点
q = q->next; //使q指向q的下一个结点
}
}
}
双向循环链表
双向循环链表是单项循环链表的一种拓展(循环链表与双向链表的组合),
循环链表即让最后一个结点不指向空,而是指向头结点,
双向链表的指针域为两个指针,一个指向前驱(前一个结点),一个指向后继(后一个结点)
- 首先定义一个双向循环链表
typedef struct student { //定义一个结构体
int x;
struct student * llist; //前驱结点的指针
struct student * rlist; //后继结点的指针
} Node, * linklist; //Node位一个结点, linklist为结点的指针
一、 双向循环链表的创建
linklist chuangjian() { //尾插法创建链表
int x; //存储一个临时值
printf("请输入一个数字, 键入负数退出!");
scanf("%d", &x); //获取临时值
linklist head, p, q;
head = (linklist) malloc (sizeof(Node));
head->llist = NULL;
head->rlist = NULL;
p = head;
while (x>=0) {
//如果临时值为非负数就创建一个结点
q = (linklist) malloc (sizeof(Node));
//将临时值赋值给结点的数据
q->x = x;
//使新结点的前驱指针指向原本的前一个结点
q->llist = p;
//使新结点的后继指针指向空
q->rlist = NULL;
//使前一个结点的后继指针指向新创建的结点
p->rlist = q;
//使p指向当前结点
p = q;
printf("请输入下一个数字, 键入负数退出!");
//获取临时值
scanf("%d", &x);
}
q->rlist = head;
head->llist = q;
return head;
}
二、双向循环链表的增加
void add (linklist phead) { //添加结点到链表尾部
linklist n;
n = (linklist) malloc (sizeof(Node)); //为要添加的结点动态分配内存
printf("请输入你要添加的非负数:");
scanf("%d", &n->x); //获取添加的结点的数据
n->rlist = phead; //使新结点的前驱指向头结点
n->llist = phead->llist; //使新结点的后继指向第一个结点
phead->llist->rlist = n; //使最后一个结点的后继指向新结点
phead->llist = n; //使头结点的后继指向新结点
}
三、双向循环链表的删除
void del(linklist phead) { //删除结点
int x;
printf("请输入你要删除的数字:");
scanf("%d", &x); //获取要删除的数字
linklist p, q;
p = q = phead; //使p和q都指向头结点
while (q->rlist != phead && q->x != x) { //如果q不为最后一个节点且q的值不为要删除的数字
p = q; //使p指向当前结点, 即指向前一个结点
q = q->rlist; //使q指向下一个结点
}
if (q->rlist == phead->rlist) //若q指向头结点, 则表明未找到该数字
puts("未找到该数字!");
else { //找到要删除的数字
p->rlist = q->rlist; //使前一个结点指向要删除的下一个结点
q->rlist->llist = p; //使要删除的下一个结点的前驱指向要删除的前一个结点
free(q); //释放删除的结点空间
}
}
四、双向循环链表的遍历
void Print(linklist phead, int n) { //打印链表内容
linklist p = phead; //p指向第一个结点
if (n==1) //n=1时表示正序输出
while (p->rlist != phead) { //若p不为最后一个结点
p = p->rlist; //让p指向后一个结点
printf("%d\n", p->x); //打印该结点的数据
}
else //否则表示逆序输出
while (p->llist != phead) { //若p不为最后一个结点
p = p->llist; //让p指向前一个结点
printf("%d\n", p->x); //打印该结点的数据
}
}