纸上谈来终觉浅,绝知此事要躬行
先说这两天的反思,有点急于求成了,想着多做几道题,由于这几天已经做了不少题,觉得分析画图的时候可以快一点,结构就导致我在这道题上困了一天没有走出来,后来在老师的指导下才打通任督二脉,由此我也得出一个结论:
一定要画图分析!!!一定要画图分析!!!一定要画图分析!!!
重要的事情说三遍!!!
原题链接:https://leetcode.cn/problems/sort-list/
PS:给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
先来谈一下我对这道题的理解
![](https://img-blog.csdnimg.cn/img_convert/b6a97689f0003bb4362cd755d1a0e734.png)
没进行一次范围比较就将该范围的值进行一次排序,而后下一次的大范围比较每组数据的值都是相对有序的,那么在进行一个值的插入即可,直到排序完。
如果数据的个数没有2^N次幂个,那也是能排序的,因为这一段数据保持着该范围内的相对有序,再做一个尾部特殊处理,把他链接至上一个尾部,再参与比较,举个栗子:
![](https://img-blog.csdnimg.cn/img_convert/62c7da6a2919c5502e15a8c323b9a936.png)
可见,2在单独的范围类,保持着相对有序,所以对尾部进行一个排序+链接即可
先上草稿图,如果能够能看懂就更好了,整个循环全都在这张草稿上
![](https://img-blog.csdnimg.cn/img_convert/99078dee9473f395c401e955ffdc7617.jpeg)
struct ListNode* sortList(struct ListNode* head){
int num = 0;//链表总长度
//int node = 0;//表示范围的终止节点
int endK = 0;//表示每一个范围的个数
struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->val = 0;
dummy->next = head;
//这里弄一个带哨兵位的,排序会更加方便
struct ListNode* cur = head;//定义一个变量,指向链表首地址,用来判断该链表的长度为多少
while (cur)
{
cur = cur->next;
++num;
}//算出链表的总长度
//printf("n:%d\n", num);
for (int step = 1; step < num; step *= 2)//表示在一个单独小范围内,头结点与尾节点可以偏移几个单位,同时也表示了该范围有多少的数据
{
cur = dummy;//重定向让cur指向链表首地址让cur每次指向链表的首地址
for (int node = 1; node + step <= num; node = node + 2 * step)//下一次范围的头节点,每一次会便宜step*2个单位,直到走到末尾
{
struct ListNode* begin = cur->next;//定义一个begin和end指针,begin指向该范围的左边界,end指向该范围内的右边界
struct ListNode* end = begin;
for (endK = 0; endK < step; endK++)
end = end->next;//要让end偏移step个单位才会指向右边界
int begin_Left = 0, end_Left = 0;//表示begin指针移动了几次,即进行了几次数值交换
while ((begin_Left < step) && (end_Left < step) && (begin && end))
{//当begin_Left 或者 end_Left的值等于step的时候,说明此时要么小的数全部排了,要么就是大的数全部排了,当排完还剩下一部分数据再链接在当前数据的尾部,当begin与end分别有一个为空时,也就是说此时此刻指针走到整个链表的右边界了,就需要停止
if (begin->val <= end->val)
{如果begin->val的值小于end->val,那么就让cur->next指向begin,并且要立即让cur走到begin这个位置上来,然后再让begin走到下一个位置去,然后让begin_left++。如果此时begin->next==NULL,那么进入上一层的while循环就会退出,并且链接end剩余的数
cur->next = begin;
cur = begin;
begin = begin->next;
begin_Left++;
}
else
{//与上同理
cur->next = end;
cur = end;
end = end->next;
end_Left++;
}
}
//当上一级循环退出只有二种情况,begin走到范围尽头了,end走到范围尽头了。也就是begin或者end走了step步,或者是走到NULL这个位置了
while (begin_Left < step && begin)
{
cur->next = begin;
cur = begin;
begin = begin->next;
begin_Left++;
}
while (end_Left < step && end)
{
cur->next = end;
cur = end;
end = end->next;
end_Left++;
}
cur->next = end;这一步就是让cur指向下一个小范围的首节点
}
}
return dummy->next;
}