Mergesort
Merging with smaller auxiliary array. Suppose that the subarray ?[?] to ?[?−?] is sorted and the subarray ?[?] to ?[?∗?−?] is sorted. How can you merge the two subarrays so that ?[?] to ?[?∗?−?] is sorted using an auxiliary array of length n(instead of 2n)?
只用一半长度的辅助数组来实现归并排序:将前半数组复制到辅助数组中,将新数组元素直接保存在原数组中。
public class SmallerAux {
public static void merge(Comparable[] a, int n) {
// 特殊情况直接返回
if (a[n-1] <= a[n]) {
return;
}
Comparable[] aux = new Comparable[n];
for (int i = 0; i < n; i++) {
aux[i] = a[i];
}
int i = 0;
int j = n;
for (int k = 0; k < 2 * n; k++) {
if (i >= n) {
break; // 左半边数组元素用完后,无需移动右半边数组剩余元素
} else if (j >= 2 * n) {
a[k] = aux[i++];
} else if (less(aux[i], a[j])) {
a[k] = aux[i++];
} else {
a[k] = a[j++];
}
}
}
private static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
}
Counting inversions. An inversion in an array a[] is a pair of entries a[i] and a[j] such that i<j but a[i]>a[j]. Given an array, design a linearithmic algorithm to count the number of inversions.
统计给定数组中的逆序数对数。在归并排序的过程中处理:对于两个子数列a[left] - a[mid]和a[mid+1] - a[right],如果排序过程中a[j]<a[i],说明从a[i]到a[mid]都可以与a[j]构成逆序数对,即逆序数对增加mid-i+1个。
public class Inversions {
private int count;
private int[] copy; // 防止修改原数组
public Inversions(int[] a) {
copy = new int[a.length];
for (int i = 0; i < a.length; i++) {
copy[i] = a[i];
}
}
public int count() {
sort(copy, 0, copy.length - 1);
return count;
}
private void sort(int[] a, int left, int right) {
if (left >= right) {
return;
}
int mid = (left + right) / 2;
sort(a, left, mid);
sort(a, mid + 1, right);
merge(a, left, mid, right);
}
private void merge(int[] a, int left, int mid, int right) {
int[] aux = new int[a.length];
for (int i = left; i <= right; i++) {
aux[i] = a[i];
}
int i = left;
int j = mid + 1;
for (int k = left; k <= right; k++) {
if (i > mid) {
a[k] = aux[j++];
} else if (j > right) {
a[k] = aux[i++];
} else if (aux[j] < aux[i]) {
a[k] = aux[j++];
count += mid - i + 1; // 逆序数对增加
} else {
a[k] = aux[i++];
}
}
}
}
Shuffling a linked list. Given a singly-linked list containing nn items, rearrange the items uniformly at random. Your algorithm should consume a logarithmic (or constant) amount of extra memory and run in time proportional to nlogn in the worst case.
将给定的链表顺序随机打乱。只要在归并排序时从左右两数组中随机选择一个元素放入辅助数组中即可。
import java.util.Random;
public class ShuffleList {
public Node randomSort(Node first) {
if (first == null || first.next == null) {
return first;
}
Node mid = findMid(first);
Node left = first;
Node right = mid.next;
mid.next = null; // 断开左右链表
left = randomSort(left);
right = randomSort(right);
return randomMerge(left, right);
}
private Node randomMerge(Node left, Node right) {
Random r = new Random();
Node head = new Node();
Node current = head;
while (left != null || right != null) {
if (left == null) {
current.next = right;
break;
} else if (right == null) {
current.next = left;
break;
} else {
// 随机归并左右其中一个元素
int x = r.nextInt(2);
if (x == 0) {
current.next = left;
current = current.next;
left = left.next;
} else {
current.next = right;
current = current.next;
right = right.next;
}
}
}
return head.next;
}
private Node findMid(Node first) {
Node mid = first;
Node last = first;
// 使用不同的递进速率来定位中间元素
while (mid.next != null && last.next != null && last.next.next != null) {
mid = mid.next;
last = last.next.next;
}
return mid;
}
}
class Node {
int item;
Node next;
}
参考**