1. 概述
我们在讨论快速排序和归并排序的时候通常是针对数组来进行讨论的,常见的的算法教科书和文档也几乎都是讨论的数组。
快速排序和归并排序对链表是同样的。只要把其思想应用于链表。
?
2. 链表的快速排序
算法步骤
快速排序的思想就是每次选出一个作为划分点,在这个划分点的左边全部是比这个划分点小的,在右边全部是比这个划分点大的。只是在链表的快速排序中,这个划分点是一个结点,而在数组的快速排序中,这个划分点是一个数。
1.pivot选择为链表的第一个结点,small指向比pivot结点值小的最后一个结点,cur指针遍历整个链表。
当cur的值比pivot的值小的话,就将cur结点的值与small的下一个结点交换。这样到最后small结点及之前的结点都是比pivot值小的,small结点之后的都是比pivot值大的;
private ListNode partition(ListNode head, ListNode tail) {
int pivot = head.val;
ListNode small = head, cur = head;
while (cur != tail) {
if (cur.val < pivot) {
small = small.next;
int tmp = small.val;
small.val = cur.val;
cur.val = tmp;
}
cur = cur.next;
}
head.val = small.val;
small.val = pivot;
return small;
}
2.然后再将pivot结点归位,即pivot的值与small最后一个交换,那么pivot归到了本属于它的位置。
3.然后,根据快速排序的 Divide and Conquer 思想再对前半部分和后半部分分别进行排序。
public ListNode quickSortLinkedList(ListNode head) {
if (head == null || head.next == null) return head;
return quickSort(head, null);
}
private ListNode quickSort(ListNode head, ListNode tail) {
if (head == tail) return head;
ListNode mid = partition(head, tail);
quickSort(head, mid);
quickSort(mid.next, tail);
return head;
}
?
测试
package cn.pku.edu.algorithm.leetcode.plus;
import cn.pku.edu.algorithm.leetcode.common.ListNode;
public class LinkedListQuickSort {
public ListNode quickSortLinkedList(ListNode head) {
if (head == null || head.next == null) return head;
return quickSort(head, null);
}
private ListNode quickSort(ListNode head, ListNode tail) {
if (head == tail) return head;
ListNode mid = partition(head, tail);
quickSort(head, mid);
quickSort(mid.next, tail);
return head;
}
private ListNode partition(ListNode head, ListNode tail) {
int pivot = head.val;
ListNode small = head, cur = head;
while (cur != tail) {
if (cur.val < pivot) {
small = small.next;
int tmp = small.val;
small.val = cur.val;
cur.val = tmp;
}
cur = cur.next;
}
head.val = small.val;
small.val = pivot;
return small;
}
public static void main(String[] args) {
ListNode head = new ListNode(4);
ListNode listNode2 = new ListNode(1);
ListNode listNode3 = new ListNode(2);
ListNode listNode4 = new ListNode(7);
ListNode listNode5 = new ListNode(6);
ListNode listNode6 = new ListNode(3);
ListNode listNode7 = new ListNode(5);
head.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
listNode5.next = listNode6;
listNode6.next = listNode7;
ListNode.printList(head);
LinkedListQuickSort linkedListQuickSort = new LinkedListQuickSort();
ListNode res = linkedListQuickSort.quickSortLinkedList(head);
ListNode.printList(res);
}
}
?
3. 链表的归并排序
算法步骤
链表的归并排序跟快速排序虽有一些区别的,但是都是采用的 Divide and Conquer 思想来处理问题的,它每次将一个链表拆分成两段,对两段分别再归并排序,完成后再将merge两个有序的子链表。
1.将原链表拆分成左右两段,通过快慢指针找出链表的中间位置断开为两个子链表;
2.再分别对左右两个子链表采用归并排序;即会递归地调用归并排序,直到子链表为空或者子链表中只有一个结点,就可以直接返回链表本身,因为它已经是有序的了;
public ListNode mergeSort(ListNode head) {
if (head == null || head.next == null) return head;
ListNode fast = head, slow = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
fast = slow.next;
slow.next = null;
ListNode left = mergeSort(head);
ListNode right = mergeSort(fast);
return mergeTwoLists(left, right);
}
3.对得到的两个有序的子链表采用merge操作,使得整个链表成为有序链表;
private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1), pre = dummy;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
pre.next = list1;
list1 = list1.next;
} else {
pre.next = list2;
list2 = list2.next;
}
pre = pre.next;
}
if (list1 != null) pre.next = list1;
if (list2 != null) pre.next = list2;
return dummy.next;
}
?
测试
package cn.pku.edu.algorithm.leetcode.plus;
import cn.pku.edu.algorithm.leetcode.common.ListNode;
public class LinkedListMergeSort {
public ListNode mergeSort(ListNode head) {
if (head == null || head.next == null) return head;
ListNode fast = head, slow = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
fast = slow.next;
slow.next = null;
ListNode left = mergeSort(head);
ListNode right = mergeSort(fast);
return mergeTwoLists(left, right);
}
private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1), pre = dummy;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
pre.next = list1;
list1 = list1.next;
} else {
pre.next = list2;
list2 = list2.next;
}
pre = pre.next;
}
if (list1 != null) pre.next = list1;
if (list2 != null) pre.next = list2;
return dummy.next;
}
public static void main(String[] args) {
ListNode head = new ListNode(4);
ListNode listNode2 = new ListNode(1);
ListNode listNode3 = new ListNode(2);
ListNode listNode4 = new ListNode(7);
ListNode listNode5 = new ListNode(6);
ListNode listNode6 = new ListNode(3);
ListNode listNode7 = new ListNode(5);
head.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
listNode5.next = listNode6;
listNode6.next = listNode7;
ListNode.printList(head);
LinkedListMergeSort mergeSort = new LinkedListMergeSort();
ListNode res = mergeSort.mergeSort(head);
ListNode.printList(res);
}
}
?
参考文献
[1] https://blog.csdn.net/yueguangmuyu/article/details/119102492 [2] https://iq.opengenus.org/quick-sort-on-linked-list/
|