本题来自147. 对链表进行插入排序 - 力扣(LeetCode)
在代码里面就有思路,要是看不懂去就看思路梳理
目录
题面:
给定单个链表的头 head
,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
示例 1:
输入: head = [4,2,1,3] 输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0] 输出: [-1,0,3,4,5]
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode node;
struct ListNode* insertionSortList(struct ListNode* head)
{
// 排除空链表和单链表的情况
if (head==NULL || head->next==NULL)
return head;
// 至少有两个节点的链表
node* newhead = (node*)malloc(sizeof(node));// 新的链表头// 哨兵位
node* cur = NULL;//cur是新链表的当前节点
node* prev = NULL;//prev是cur的前一个节点
node* headNext = NULL;//原链表头节点的下一个节点
headNext = head->next; // 让headNext永远在head的下一个节点
newhead->next = head; // 第一个节点不用任何判断直接连接到新链表头节点的后面
prev = newhead; // prev从新链表开始 // prev在哨兵位
cur = prev->next; // cur是新链表的第一个节点
cur->next = NULL; // 给新链表一个尾
head = headNext; // 原链表指针后移
headNext = headNext->next; // headNext是原链表的第二节点
while (head) // 直接用head作为指针作为原链表的指针使用,当head指向NULL时,程序结束
{
prev = newhead; // 每次都将prev放在新链表头节点
cur = prev->next; // cur是新链表的第一个有效节点
while (1) // 死循环来遍历新链表,以确认新数据插入的位置
{
if (head->val < cur->val) // 如果原链表节点val小于cur的val
{
// 就把原链表的节点插在当前节点cur的前面,也就是prev的后面
prev->next = head;
head->next = cur;
break;
}
else // 如果原链表拿过来的节点的val比当前节点cur的val大,将cur依次后移
{
prev = cur; // prev随着cur移动
cur = cur->next;
if (!cur) // 新链表的尾节点 // 到这一步说明新链表的所有节点都比原链表的节点小
{ // 也就意味着原链表的节点直接接在新链表的尾节点就可以了
prev->next = head;
head->next = NULL; // 尾节点置空,成为新的尾
break;
}
}
}
head = headNext; // 原链表节点后移
if (head != NULL) // 防止原链表为空
// 也就是说假如原链表的所有节点都处理完了,就退出了,不用继续后移了,这里的headNext也不能后移了
headNext = headNext->next;
}
return newhead->next; // 把哨兵位越过去
}
思路梳理:
题给链表head指针,我们就用这个指针来遍历原链表
先考虑特殊情况:
1. head为NULL,这种情况不用处理,把NULL返回去即可
2. 就给了一个节点,也不用排序,把head返回去就可以了
这两种情况直接让head返回去就可以了
特殊情况排除之后,链表就可以排序了,到这一步,链表肯定是两个以上的节点
这里我们新建一个链表头,考虑到节点排序的时候可能会头插,频繁改动头节点有些许不妥,所以决定用哨兵位,采用哨兵位的好处就是,头插的时候不需要改变头节点,只需要让头节点指向新节点就可以了
对于单链表而言最大的痛楚就是不能直接访问上一个节点,因此还是采用两个指针的方式来遍历链表
对于原链表来说,我们用head来访问每一个节点,但是我们是直接将head指向的节点插入到新链表,那么head的下一个节点需要提前保存一下,headNext就是head在原链表的下一个节点
head的下一个节点被保存之后,head就可以了拿到新链表去插入了
在新链表的什么地方插入呢,肯定是在比它大的节点之前,比它小的节点之后,这里我们用一个循环来找哪一个节点比head的val值大,将head的val与cur的val比较,如果找到了就把cur的前节点连接到head,head再连接到cur,也就是插入新链表了
这里要考虑特殊情况,假如新链表所以的节点都比head小,就把head尾插到新链表,head的next指向NULL,成为新的尾
头插呢,前面也说了,咱们默认cur每次都从新链表的第一个有效位开始比较,prev就从哨兵位开始,比新链表的头节点还小,把head接在哨兵位后面即可
返回新链表的指针时,要越过哨兵位
这次改变了一下写博客的顺序,大家投个票,我看看你们喜欢哪一种排版