携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情
一、前言
归并排序:是由冯·诺伊曼提出的一种基于分治思想的高效排序算法。
它的算法思想是:
- 把当前序列平分成两个子序列
- 然后递归地对子序列进行排序
- 最后把排序好的子序列再合并成一个有序的序列
举栗,动图如下:可视化归并
实现方式有二:递归方式 和 非递归方式。
- 递归方式:
public class MergeSort {
// Time: O(n * log(n)), Space: O(n)
public void sortRecursive(int [] arr) {
if (arr == null || arr.length == 0) return;
int [] tmp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, tmp);
}
private void mergeSort(int[] arr, int low, int high, int[] tmp) {
if (low < high) {
int mid = low + (high - low) / 2;
mergeSort(arr, low, mid, tmp);
mergeSort(arr, mid + 1, high, tmp);
merge(arr, low, mid, high, tmp);
}
}
private void merge(int[] arr, int low, int mid, int high, int[] tmp) {
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high) {
if (arr[i] <= arr[j]) tmp[k++] = arr[i++];
else tmp[k++] = arr[j++];
}
while (i <= mid) tmp[k++] = arr[i++];
while (j <= high) tmp[k++] = arr[j++];
System.arraycopy(tmp, 0, arr, low, k);
}
}
- 非递归方式
public class MergeSort {
// Time: O(n * log(n)), Space: O(n)
public void sortIterative(int [] arr) {
if (arr == null || arr.length == 0) return;
int n = arr.length;
int [] tmp = new int[n];
for (int len = 1; len < n; len = 2 * len) {
for (int low = 0; low < n; low += 2 * len) {
int mid = Math.min(low + len - 1, n - 1);
int high = Math.min(low + 2 * len - 1, n - 1);
merge(arr, low, mid, high, tmp);
}
}
}
private void merge(int[] arr, int low, int mid, int high, int[] tmp) {
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high) {
if (arr[i] <= arr[j]) tmp[k++] = arr[i++];
else tmp[k++] = arr[j++];
}
while (i <= mid) tmp[k++] = arr[i++];
while (j <= high) tmp[k++] = arr[j++];
System.arraycopy(tmp, 0, arr, low, k);
}
}
二、题目
单链表排序(中)
题干分析
这个题目说的是,给你一个单链表,你要写一个函数,对它进行排序,然后返回排序后的链表。
# 比如说,给你的单链表是:
4 -> 8 -> 2 -> 1
# 你要返回排序后的链表:
1 -> 2 -> 4 -> 8
思路解法
解法有二: 快排 和 归并排序。
- 方法二:归并排
- 不断把链表切半成 2 个链表
- 递归处理这 2 个链表
- 最后把这 2 个排好序的链表合并起来
使用快慢指针:慢指针一次走一步,快指针一次走 2 步。
// Time: O(n*log(n)), Space: O(log(n)), Faster: 97.67%
public ListNode mergeSortList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode fast = head, slow = head;
while (fast.next != null && fast.next.next != null) { // 1. 切分成 2 个链表
slow = slow.next;
fast = fast.next.next;
}
ListNode right = mergeSortList(slow.next); // 2. 递归拆分:右侧边
slow.next = null;
ListNode left = mergeSortList(head); // 2. 递归拆分:左侧边
return mergeTwoSortedLists(left, right); // 3. 合并并排序
}
// 排序并合并两个链表
private ListNode mergeTwoSortedLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0), p = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
p.next = l1;
l1 = l1.next;
} else {
p.next = l2;
l2 = l2.next;
}
p = p.next;
}
if (l1 != null) p.next = l1;
if (l2 != null) p.next = l2;
return dummy.next;
}