开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第22天,点击查看活动详情。
对链表进行插入排序
描述
从第一个元素开始,该链表可以被认为已经部分排序。每次迭代时,从输入数据中移除一个元素,并原地将其插入到已排好序的链表中。 插入排序算法:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
难度
中等
示例
输入: 4->2->1->3
输出: 1->2->3->4
输入: -1->5->3->4->0
输出: -1->0->3->4->5
思路
根据插入排序算法,将待插入的元素和左边有序序列依次从后往前比较,但是链表不能从后向前移动,所以这里选择从头节点向后移动依次比较。
遍历链表,将当前节点 current 的下一个节点 next 依次和之前的所有节点比较,找到合适的插入位置,在插入前,先将当前节点 current 的下一个节点指向 next 的下一个节点,然后再插入节点,继续下一次循环。如果比较节点时没有发生插入,将 current 后移一个节点。
代码
Go
type ListNode struct {
Val int
Next *ListNode
}
func insertionSortList(head *ListNode) *ListNode {
newHead := &ListNode{Val: math.MinInt32}
newHead.Next = head
current := newHead
for current != nil && current.Next != nil {
// 用来插入的节点
next := current.Next
temp := newHead
// 从头节点开始循环到 next 的前一个节点
for temp != next {
// 将用来插入节点一次和每个节点比较
if next.Val < temp.Next.Val {
// 在节点插入到有序的子链表前, 先将 current 节点指向用来插入的节点的下一个节点
current.Next = next.Next
// 将节点插入到合适位置
next.Next = temp.Next
temp.Next = next
break
}
temp = temp.Next
}
// 表示没有节点插入, 继续从下一个节点开始走
// 如果有节点插入, current 节点已经正确指向了下一个节点
if temp == next {
current = current.Next
}
}
return newHead.Next
}
func printListNode(node *ListNode) {
var str = ""
for node != nil {
str += fmt.Sprintf("%d->", node.Val)
node = node.Next
}
str = strings.TrimSuffix(str, "->")
fmt.Println(str)
}
func TestInsertionSortList(t *testing.T) {
node1 := &ListNode{Val: 4}
node2 := &ListNode{Val: 2}
node3 := &ListNode{Val: 1}
node4 := &ListNode{Val: 3}
node1.Next = node2
node2.Next = node3
node3.Next = node4
printListNode(node1)
node := insertionSortList(node1)
printListNode(node)
node1 = &ListNode{Val: -1}
node2 = &ListNode{Val: 5}
node3 = &ListNode{Val: 3}
node4 = &ListNode{Val: 4}
node5 := &ListNode{Val: 0}
node1.Next = node2
node2.Next = node3
node3.Next = node4
node4.Next = node5
printListNode(node1)
node = insertionSortList(node1)
printListNode(node)
}
运行结果:
4->2->1->3
1->2->3->4
-1->5->3->4->0
-1->0->3->4->5
Java
public class Main {
public static class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public static ListNode insertionSortList(ListNode head) {
ListNode newHead = new ListNode(Integer.MIN_VALUE);
newHead.next = head;
ListNode current = newHead;
while (current != null && current.next != null) {
// 用来插入的节点
ListNode next = current.next;
ListNode temp = newHead;
// 从头节点开始循环到 next 的前一个节点
while (temp != next) {
// 将用来插入节点一次和每个节点比较
if (next.val < temp.next.val) {
// 在节点插入到有序的子链表前, 先将 current 节点指向用来插入的节点的下一个节点
current.next = next.next;
// 将节点插入到合适位置
next.next = temp.next;
temp.next = next;
break;
}
temp = temp.next;
}
// 表示没有节点插入, 继续从下一个节点开始走
// 如果有节点插入, current 节点已经正确指向了下一个节点
if (temp == next) {
current = current.next;
}
}
return newHead.next;
}
private static void printListNode(ListNode node) {
StringBuilder sb = new StringBuilder();
while (node != null) {
sb.append(node.val).append("->");
node = node.next;
}
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
System.out.println(sb.toString());
}
public static void main(String[] args) {
ListNode node1 = new ListNode(4);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(1);
ListNode node4 = new ListNode(3);
node1.next = node2;
node2.next = node3;
node3.next = node4;
printListNode(node1);
ListNode node = insertionSortList(node1);
printListNode(node);
node1 = new ListNode(-1);
node2 = new ListNode(5);
node3 = new ListNode(3);
node4 = new ListNode(4);
ListNode node5 = new ListNode(0);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
printListNode(node1);
node = insertionSortList(node1);
printListNode(node);
}
}
运行结果:
4->2->1->3
1->2->3->4
-1->5->3->4->0
-1->0->3->4->5