[双向链表排序]—-对双向链表中结(节)点的成员排序(冒泡排序)「建议收藏」
2023-06-13 09:14:42 时间
1. 双向链表的定义
【百度百科】
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
- 链表中的每个节点的成员由两部分组成: 1. 数据域:专门用来保存各个成员的信息数据。 2. 指针域:专门用来与其他节点链接。
2. 结构体中的两个重要指针
直接后继 & 直接前驱:
- 直接后继:我个人习惯称之为后向指针,也习惯定义为pnext,该指针指向下一个节点,如果该节点为尾节点,那么pnext指向NULL。
- 直接前驱:同样我习惯称之为后向指针,也习惯定义为prev,该指针指向前一个节点,如果该节点为头节点,那么prev指向NULL。
3. 双向链表中节点的成员排序(冒泡排序)
在排序之前我们需要明确一点:<明确我们操作的链表的头节点的数据域是否写有数据>
因为有时候程序员写代码时为了链表方便操作会专门创建一个表头(头结点)
,即不存放数据的表头。
3.1头节点数据域为空
接下来我们来看几段代码:
typedef struct student
{
int num;
char name[20];
float score;
struct student *prev;
struct student *pnext;
}STU,*PSTU;
//1.首先我们定义一个结构体,有数据域(前三个)和指针域(后两个)两部分
//2.将结构体和结构体指针分别重命名为STU,PSTU
冒泡排序代码如下:
#incldue <stdio.h>
PSTU score_sort(PSTU head) //函数返回值为头指针,形参也为头指针
{
int i=0,j=0;
int n=num_of_stu(head); //调用统计节点个数的函数
PSTU p=head; //定义两个临时指针来进行数据处理
PSTU pn=head; //p和pn总是两个相邻的节点,且pn在p之后
//****冒泡排序****//
for(i=0;i<n;i++)
{
p=head->pnext;
pn=p->pnext;
for(j=0;j<n-1-i;j++)
{
if(p->score < pn->score) //判断,条件成立之后交换位置
{
if(pn->pnext==NULL) //判断是否为尾节点
{
//**交换位置代码**//
p->pnext=pn->pnext;
pn->prev=p->prev;
pn->pnext=p;
p->prev->pnext=pn;
p->prev=pn;
//**位置交换结束**//
}
else
{
//**交换位置代码**//
p->pnext=pn->pnext;
pn->prev=p->prev;
pn->pnext->prev=p;
p->prev->pnext=pn;
//下一行请注意//
pn->pnext=p;
p->prev=pn;
//**位置交换结束**//
pn=p->pnext; //位置交换结束之后进行指针偏移,pn指向p的下一个节点
}
else //条件不成立
{
p=p->pnext;
pn=p->pnext;
//如果未发生位置交换,则两个指针都要发生偏移
}
}
}
return head;
}
重点:
在上一段代码中一定要注意我在代码前面注释的那一行,并且要与不是尾结点
的情况对比来看,你会发现少了一行代码, pn->pnext->prev=p
,我先解释一下这一行代码是什么意思,从上面的代码可以看出两个临时指针的位置关系为p
总是在Pn
的前面,那也就是说满足交换位置条件之后进行位置交换,交换之后两个临时指针位置就随之交换,在交换的过程中,假如有尾结点,那么pn的后向指针指向NULL,随之 pn->pnext->prev
就会出现段错误
。
3.2头节点数据域不为空(一般不建议)
这种方式在数据处理上面会比较麻烦,一旦头结点的数据发生位置交换(比如排序,插入结点,删除结点等),那么在函数的封装是就要考虑将新的头结点返回。
接下来我们来看几段代码:
typedef struct student
{
int num;
char name[20];
float score;
struct student *prev;
struct student *pnext;
}STU,*PSTU;
//1.首先我们定义一个结构体,有数据域(前三个)和指针域(后两个)两部分
//2.将结构体和结构体指针分别重命名为STU,PSTU
冒泡排序部分核心代码如下:
#incldue <stdio.h>
PSTU score_sort(PSTU head) //函数返回值为头指针,形参也为头指针
{
int i=0,j=0;
int n=num_of_stu(head); //调用统计节点个数的函数
PSTU p=head; //定义两个临时指针来进行数据处理
PSTU p1=head; //p和pn总是两个相邻的节点,且pn在p之后
//****冒泡排序****//
for(i=0;i<n;i++)
{
p=head->pnext;
pn=p->pnext;
for(j=0;j<n-1-i;j++)
{
if(p->score < pn->score) //判断,条件成立之后交换位置
{
if(j==0)//***重点参考 //判断头结点是否会参与位置交换
{
head=p1;
p->pnext=p1->pnext;
p1->prev=p->prev;
p1->pnext=p;
p1->pnext->prev=p;
p->prev=p1;
p1=p->pnext;
}
......//中间省略部分代码//......
else
{
//**交换位置代码**//
p->pnext=pn->pnext;
pn->prev=p->prev;
pn->pnext->prev=p;
p->prev->pnext=pn;
//下一行请注意//
pn->pnext=p;
p->prev=pn;
//**位置交换结束**//
pn=p->pnext; //位置交换结束之后进行指针偏移,pn指向p的下一个节点
}
else //条件不成立
{
p=p->pnext;
pn=p->pnext;
//如果未发生位置交换,则两个指针都要发生偏移
}
}
}
return head;
}
重点: 在看上面的代码时我们需要与3.1节的内容对比来看,因为3.2节的中要单独考虑的情况有四种:
- 头结点发生改变:
重点要考虑头指针的的前向指针为NULL;
- 尾结点发生改变:
重点要考虑尾结点的的后向向指针为NULL;
- 有且仅有两个结点(即头结点和尾结点):
重点要考虑头指针的的前向指针为NULL且尾结点的的后向向指针为NULL;
- 发生位置交换的结点不包含头结点和尾结点:
这种情况下交换位置的6行代码都不能少;
以上就是就是本次的所有内容,朋友如若发现问题,随时欢迎交流,希望能帮到你!!!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183253.html原文链接:https://javaforall.cn
相关文章
- 21. 合并两个有序链表
- java链表打印_java链表打印
- 选择排序 c语言(链表法)「建议收藏」
- 链表结构
- LeetCode排序链表C++解法(详解)
- LeetCode142. 环形链表 II(C++俩种解法)
- 【说站】python链表法的优缺点
- 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
- 82. 删除排序链表中的重复元素 II
- 148. 排序链表
- 常用链表排序算法_单链表的排序算法
- 链表排序java_java有序链表
- 链表排序之选择排序法_单链表直接选择排序
- js的链表排序_排序js
- 【Day 01】力扣(LeetCode)每日一刷[506.相对名次][264.丑数][23.合并N个升序链表]
- 手把手教你完整实现一个链表
- 二叉搜索树转双向链表
- 【数据结构】链表
- 算法-合并两个排序的链表详解编程语言
- 合并两条排序的链表详解编程语言
- Oracle中高效使用链表查询技巧(oracle中链表查询)
- Redis链表实现从理论到实践(redis链表实现)
- Redis灵活设置链表,快速存取数据(redis设置链表)