对链表进行插入排序

题目

Alt
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

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

示例 1:
输入: 4->2->1->3
输出: 1->2->3->4

示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insertion-sort-list

思路

自己的思路

首先,插入排序没什么可说的,就是在一个已排好序的数据中不断插入数据,这里的难点就是在单链表中进行插入排序,一开始我想的是设置两个指针,一个最小指针,放在头部,一个最大指针,放在尾部,这样就能少了很多判断条件,所以代码如下:

 ListNode* insertionSortList(ListNode* head) {
        if (head == NULL)
			return NULL;
		if (head->next == NULL)
			return head;
		ListNode* res =new ListNode(INT_MIN);
		ListNode* end = new ListNode(INT_MAX);
		ListNode* H = head->next;
		res->next = head;
		head->next = end;
		head = H;
		ListNode* start = res;
		ListNode* temp, * node;
	     while (head != NULL) {
			temp = head->next;
			while (start->next&&head->val > start->next->val)
				start=start->next;
			node = start->next;
			start->next = head;
			head->next = node;
			start = res;
			head = temp;
		}
		 while (start->next->next)
			 start = start->next;
		 start->next = NULL;
		return res->next;
}

这种思路存在的不足就是对于最后一个最大指针如何删除的问题,我使用的方法就是再遍历一遍排好序的链表,最后将最大指针删除即可,多了一遍循环,所以时间复杂度不是最优,代码也不够简洁明了。

参考思路

改进上诉思路的一种方法就是:我们同样是设置两个头尾指针,用于表示已排好序的链表,那么头指针就是新建一个头节点指针,尾指针就是head,具体代码如下:

ListNode* insertionSortList(ListNode* head) {
		ListNode* start = new ListNode(0);//设置头节点指针
		ListNode* temp;
		start->next = head;
		//该思路就是设置排序好的链表中的头,尾指针,头指针就是前面所设置的,尾指针就是head
		while (head && head->next) {
			if (head->val <= head->next->val) {//如果要比较的节点值大于head,则不用排序
				head = head->next;
				continue;
			}
			temp = start;
			while (head->next->val > temp->next->val)//否则进入内循环进行排序
				temp = temp->next;
			ListNode* curr = head->next;//保存下一个待插入的节点
			head->next = curr->next;//这里是移除要插入的节点,否则将会造成重复
			curr->next = temp->next;
			temp->next = curr;
		}
		return start->next;
		delete start;
	}

这里巧妙的地方在于将已排序的链表的尾指针设置为head,那么每次将要排序的节点就是head->next,如果待排序的节点值大于此时的尾节点,那么就不用再和组内的节点进行比较了,直接进行下一次的循环;否则,就进入内循环,逐个比较,知道找到合适的位置。这种思路理解的重点在于下面几个:

if (head->val <= head->next->val) {//如果要比较的节点值大于head,则不用排序
				head = head->next;
				continue;
			}

这里巧妙的地方在于尾指针和待插入的节点是相连接的,这样当待插入的节点大于尾指针时,则直接将此时待插入的节点更新为尾节点即可

还需注意的一个问题是:在我们每次准备进入内循环时,必须要保证是从头节点开始的,所以设置:

temp = start;

还有就是内循环的判断条件:

while (head->next->val > temp->next->val)//否则进入内循环进行排序
				temp = temp->next;

因为待插入的点已经判断出小于尾指针,那么在已排序的链表中一定能将该节点插入。

总结

这道题是关于插入排序在单链表中的应用,相比较在数组来说,因为单链表无随机访问性,所以在解题过程中我们可以尽量朝着数组类型的插入排序靠,理解这道题的一个难点和重点就是关于设置头尾指针的问题,一种巧妙的方法就是将尾指针设置为head,这样在待插入的节点大于已排好序的单链表中的最后一个节点时,就不用再进入内循环比较,直接进行下一次的插入操作,简化了很多步骤和代码。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值