对一个单链表进行升序排序,尽量节省内存,提高速度。
不遍历列表获取链表长度,防止列表长度过长影响速度;
不移动每个结点中数据域的内容,防止因为数据量大影响速度;
尽量少创建节点变量,省内存;
使用冒泡排序算法进行排序;
冒泡排序原理,动图演示:
(动图转自网络,如有侵权请通知,我会第一时间删除)
假设链表头结点为Head, Head节点内数据域无有效数据,地址域中保存了A的地址;
第一个含有有效数据的结点为A,往后按照 B C D……排序
内循环每次都从排序在第一的有效结点开始比较;
第一次应当比较A和B,如果A大于B互换位置,那么Head的地址域就应该指向了B,B的地址域指向A,A的地址域指向C
此时,前两个结点已经完成了比较,并且将较大的一个放在了第二位,那么就要比较这个较大的和第三个结点的大小
所以我们需要一个指针来定位做比较的两个结点,我把这个指针叫做 pBefore 它的初始值指向Head结点,比较完成后将向后移动一个结点。如上图,第一次比较完成后 pBefore 将指向B结点
以此类推,在最后一次比较前
最后一次比较后
这个时候Z一定是数据最大的一个结点。同时,第二个要比较的结点为NULL,所以,可以用第二个需要比较的结点地址是否为NULL来判断内循环是否结束。
当第二次循环开始后,在比较到第二个需要比较的结点为Z时,就不需要再比较了,因为Z一定是最大的,Y一定比Z小。
所以我们就需要一个指针,来定位内循环的结束,我把这个指针叫做 pCure ,初始值设置为NULL,第一次比较完成后将向前移动一位,所以当第二个要比较的结点与pCure指向的相同时,内循环结束(终止条件);
每次内循环都要从第一个有效结点开始,所以每次外循环都要初始化pBefore;
每次内循环结束时,都要更新pCure,用来让下一次内循环在适当的位置结束;
最后一次内循环一定比较的是第一和第二个节点的大小,当它结束时,pCure一定指向了第一个结点,外循环结束(终止条件)
typedef struct Node
{
int data;//数据域
struct Node* pNext;//指针域
}NODE,*PNODE;//NODE等价于struct Node类型,*PNODE等价于struct Node*类型
void sort_list(PNODE pHead)//形参为头指针,既指向头结点的指针
{
if (pHead->pNext == NULL)//当头结点地址域为空时,代表链表为空
{
printf("这个链表为空,不能排序");
return;
}
/*======》 以下请看代码详解 《======*/
PNODE pt, pCure, pBefore;
pCure = NULL;
while (pHead->pNext != pCure)
{
pBefore = pHead;
while (pBefore->pNext->pNext != pCure )
{
if (pBefore->pNext->data > pBefore->pNext->pNext->data)
{
pt = pBefore->pNext->pNext->pNext;
pBefore->pNext->pNext->pNext = pBefore->pNext;
pBefore->pNext = pBefore->pNext->pNext;
pBefore->pNext->pNext->pNext = pt;
}
pBefore = pBefore->pNext;
}
pCure = pBefore->pNext;
}
return;
}
PNODE pt, pCure, pBefore;
pCure = NULL;
设置了3个指针 pt 将用来存放临时地址,pCure 将存放指向结束的结点的地址,pBefore 将存放需要比较的两个节点之前的一个节点的地址
pBefore = pHead;
内循环初始化pBefore指向Head
内循环详解
以此图为例:
头指针为pHead,存放的是头结点的地址;
头结点Head的地址域存放的数据为A结点的地址pNext,用头指针表示为 pHead->pNext;
pHead->pNext就是A的地址,pHead->pNext->data 为A存储的数据,pHead->pNext->pNext为A的地址域,存放的是B的地址
pHead->pNext->pNext就是B的地址,pHead->pNext->pNext->data 为B存储的数据,pHead->pNext->pNext->pNext为B的地址域,存放的是C的地址
pHead->pNext->pNext->pNext 就是C的地址
比较后,需要调换A,B的位置,具体操作分下列步骤
至此,换位工作完成,当前节点顺序为
此时,将开始比较A和C,那么pBefore应该指向B,pBefore之前指向H,那么pBefore->pNext就指向B,代码: pBefore = pBefore->pNext;
至此,一次循环结束,进入下一次循环
当循环运行,判断完最后两个节点,如下图
pBefore将指向Y,Z的地址域pBefore->pNext->pNext为NULL,与pCure相等,内循环判断语句 while (pBefore->pNext->pNext != pCure )为假,内循环结束
pCure更新为Z的地址,pBefore->pNext;
继续外循环
最后,随着pCure前移,最后一次内循环必然比较的是最前面两个节点,比较完成后,pCure将被更新为第一节点的地址。此时外循环判断语句 while (pHead->pNext != pCure)为假,外循环也结束了
至此,整个链表排序完成!