中等题 对链表进行插入排序

本题来自147. 对链表进行插入排序 - 力扣(LeetCode)

在代码里面就有思路,要是看不懂去就看思路梳理

目录

题面:

代码:

思路梳理:


题面:

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

示例 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接在哨兵位后面即可

返回新链表的指针时,要越过哨兵位

这次改变了一下写博客的顺序,大家投个票,我看看你们喜欢哪一种排版

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值