快速排序
快速排序与归并排序类似,也是运用递归分治的思想,不过它不是从中间把数组a平分成2个子数组,而是选择一个切分元素a[lo]进行切分,切分过程中把比a[lo]小的元素都移到a[lo]的左边,把比a[lo]大的元素都移到a[lo]的右边,切分完成后根据a[lo]的最终位置j,把
数组分成两个子数组a1和a2,这样a1的元素都<=a[j],a2的元素都>=a[j],再用同样的切分方式把a1和a2切分,递归下去当a1和a2有序后,a就自然有序了,切分示意图如下:
代码如下
public static void sort(Comparable[] a) {
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
assert isSorted(a);
}
// quicksort the subarray from a[lo] to a[hi]
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int j = partition(a, lo, hi);
sort(a, lo, j-1);
sort(a, j+1, hi);
assert isSorted(a, lo , hi);
}
// partition the subarray a[lo .. hi] by returning an index j
// so that a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
private static int partition(Comparable[] a, int lo, int hi) {
int i = lo;
int j = hi+1;
Comparable v = a[lo];
while (true) {
while(less(a[++i], v)) if(i== hi) break;
while(less(v, a[--j])) if(j== lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
切分函数是partition(),可以结合下图理解切分的过程,待排序数组是K,R,A...C,X,O,S,初始值i = 0, j = 16,一般选择数组第1个元素v=a[lo]作为切分元素,子循环while(less(a[++i], v))递增i直到找到一个大于等于v的元素a[i],子循环while(less(v, a[--j]))递减j直到找到一个小于等于v的元素a[j],然后exch(a, i, j) i和j的元素交换位置,使i左边的元素都<=v,j右边的元素都>=v,while (true)重复这个过程直到i >= j,i和j相遇时,j左边的元素都<=v,j右边的元素都>=v,最后调用exch(a, lo, j);把v放到正确的位置j,返回j结束切分的过程。切分的过程要注意i,j别越过数组的边界 2.3.11 假如在遇到和切分元素重复的元素时我们继续扫描数组而不是停下来,证明使用这种方法的快速排序在处理只有若干种元素值的数组时运行时间是平方级别的。
快速排序最好情况是每次切法都正好能将数组对半分,比较次数时间复杂度O(NlogN),可以结合前文归并排序的依赖树来理解。
快速排序最坏的情况是每次切分后,总有一个数组为空,需要N次切法,每次切法的比较次数是N,N-1,N-2,...2,1,所以总的比较次数是N+(N-1)+(N-2)+...+2+1=(N+1)N/2,时间复杂度是O(N^2),
所以快速排序开始会先调用lStdRandom.shuffle(a);把数组随机打乱,减少对输入的依赖,这样平均情况下时间复杂度O(NlogN)
# java SortCompare Quick Merge 5000 100
For 5000 random Doubles
Quick is 1.3 times faster than Merge
For 5000 random Doubles
Quick is 0.8 times faster than Merge
运行SortCompare可以看出快速排序和希尔排序,归并排序效率差不多,一般比希尔和归并排序快,因为快速排序内循环中移动数据次数比较少。
三向切分的快速排序
快速排序有两个问题,一是切分过程中遇到与切分元素相同的元素也会停下来交换元素,二是对元素全部重复的子数组仍然会递归进行切分,所以对于有大量重复元素的数组快速排序还有很大的改进潜力,改进算法如下,对于有大量重复元素的数组它可以把时间复杂度从线性对数基本降低到线性级别。切分示意图如下:
切分过程中把数组分成三部分,小于,等于,大于切分元素的数组,这样当子数组都是重复元素时不会继续递归下去
public class Quick3way {
public static void sort(Comparable[] a) {
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
assert isSorted(a);
}
// quicksort the subarray a[lo .. hi] using 3-way partitioning
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int lt = lo, gt = hi;
Comparable v = a[lo];
int i = lo + 1;
while (i <= gt) {
int cmp = a[i].compareTo(v);
if (cmp < 0) exch(a, lt++, i++);
else if (cmp > 0) exch(a, i, gt--);
else i++;
}
// a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi].
sort(a, lo, lt-1);
sort(a, gt+1, hi);
assert isSorted(a, lo, hi);
}
// ex2.3.5 给出一段代码将已知只有两种主键值的数组排序。
public class Sort2distinct {
public static void sort(Comparable[] a) {
int lt = 0;
int gt = a.length - 1;
int i = 1;
while(i <= gt) {
int cmp = a[i].compareTo(a[lt]);
if (cmp < 0) exch(a, i++, lt++);
else if(cmp > 0) {i++; lt++;}
else i++;
}
}
public static void sort2(Comparable[] a) {
int lt = 0;
int gt = a.length - 1;
int i = 1;
while(i <= gt) {
int cmp = a[i].compareTo(a[lt]);
if (cmp < 0) exch(a, i++, lt++);
else if(cmp > 0) exch(a, i, gt--);
else i++;
}
}
//ex 2.3.18 三取样切分,为快速排序实现正文所述的三取样切分(参见 2.3.3.2 节),运行双倍测试来确认这项改动的效果。
// ex2.3.18
public class Quick_ex2_3_18 {
public static void sort(Comparable[] a) {
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
assert isSorted(a);
}
// quicksort the subarray from a[lo] to a[hi]
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int j = partition(a, lo, hi);
sort(a, lo, j-1);
sort(a, j+1, hi);
assert isSorted(a, lo , hi);
}
// partition the subarray a[lo .. hi] by returning an index j
// so that a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
private static int partition(Comparable[] a, int lo, int hi) {
int n = hi - lo + 1;
int m = median3(a, lo, lo + n/2, hi);
exch(a, m, lo); //中位数作为切分元素
int h = less(a[lo + n/2], a[hi]) ? hi : lo + n/2;
exch(a, h, hi); // 较大的值放最右侧作为哨兵,可以去掉if(i== hi)和if(j== lo)的判断
int i = lo;
int j = hi+1;
Comparable v = a[lo];
while (true) {
while(less(a[++i], v));//if(i== hi) break;
while(less(v, a[--j]));//if(j== lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
// return the index of the median element among a[i], a[j], and a[k]
private static int median3(Comparable[] a, int i, int j, int k) {
return (less(a[i], a[j]) ?
(less(a[j], a[k]) ? j : less(a[i], a[k]) ? k : i) :
(less(a[k], a[j]) ? j : less(a[k], a[i]) ? k : i));
}
// ex 2.3.22
public class QuickBentleyMcIlroy {
// cutoff to insertion sort, must be >= 1
private static final int INSERTION_SORT_CUTOFF = 8;
// cutoff to median-of-3 partitioning
private static final int MEDIAN_OF_3_CUTOFF = 40;
// This class should not be instantiated.
private QuickBentleyMcIlroy() { }
/**
* Rearranges the array in ascending order, using the natural order.
* @param a the array to be sorted
*/
public static void sort(Comparable[] a) {
sort(a, 0, a.length - 1);
}
private static void sort(Comparable[] a, int lo, int hi) {
int n = hi - lo + 1;
// cutoff to insertion sort
if (n <= INSERTION_SORT_CUTOFF) {
insertionSort(a, lo, hi);
return;
}
// use median-of-3 as partitioning element
else if (n <= MEDIAN_OF_3_CUTOFF) {
int m = median3(a, lo, lo + n/2, hi);
exch(a, m, lo);
}
// use Tukey ninther as partitioning element
else {
int eps = n/8;
int mid = lo + n/2;
int m1 = median3(a, lo, lo + eps, lo + eps + eps);
int m2 = median3(a, mid - eps, mid, mid + eps);
int m3 = median3(a, hi - eps - eps, hi - eps, hi);
int ninther = median3(a, m1, m2, m3);
exch(a, ninther, lo);
}
// Bentley-McIlroy 3-way partitioning
int i = lo, j = hi+1;
int p = lo, q = hi+1;
Comparable v = a[lo];
while (true) {
while (less(a[++i], v))
if (i == hi) break;
while (less(v, a[--j]))
if (j == lo) break;
// pointers cross
if (i == j && eq(a[i], v))
exch(a, ++p, i);
if (i >= j) break;
exch(a, i, j);
if (eq(a[i], v)) exch(a, ++p, i);
if (eq(a[j], v)) exch(a, --q, j);
}
i = j + 1;
for (int k = lo; k <= p; k++)
exch(a, k, j--);
for (int k = hi; k >= q; k--)
exch(a, k, i++);
sort(a, lo, j);
sort(a, i, hi);
}
// sort from a[lo] to a[hi] using insertion sort
private static void insertionSort(Comparable[] a, int lo, int hi) {
for (int i = lo; i <= hi; i++)
for (int j = i; j > lo && less(a[j], a[j-1]); j--)
exch(a, j, j-1);
}
// return the index of the median element among a[i], a[j], and a[k]
private static int median3(Comparable[] a, int i, int j, int k) {
return (less(a[i], a[j]) ?
(less(a[j], a[k]) ? j : less(a[i], a[k]) ? k : i) :
(less(a[k], a[j]) ? j : less(a[k], a[i]) ? k : i));
}
/***************************************************************************
* Helper sorting functions.
***************************************************************************/
// is v < w ?
private static boolean less(Comparable v, Comparable w) {
if (v == w) return false; // optimization when reference equal
return v.compareTo(w) < 0;
}
// does v == w ?
private static boolean eq(Comparable v, Comparable w) {
if (v == w) return true; // optimization when reference equal
return v.compareTo(w) == 0;
}
// exchange a[i] and a[j]
private static void exch(Object[] a, int i, int j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
/***************************************************************************
* Check if array is sorted - useful for debugging.
***************************************************************************/
private static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i-1])) return false;
return true;
}
// print array to standard output
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
StdOut.println(a[i]);
}
}
/**
* Reads in a sequence of strings from standard input; quicksorts them
* (using an optimized version of quicksort);
* and prints them to standard output in ascending order.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
String[] a = StdIn.readAllStrings();
QuickBentleyMcIlroy.sort(a);
assert isSorted(a);
show(a);
}
}
优先队列
假如文本account.txt存储了N条交易信息,格式如下,需要从中找出account最大的前M条记录,一种方法是把N条记录都读出来排序后
打印前M条,如果N很大将占用很大的存储空间,此方法不可行,另一种方法是先读取M条记录存储起来,后面每读取1条记录与这M条记录比较找出最小的删除,如果M比较大这种方法代价也比较高昂。
name time account
* Thompson 2/27/2000 4747.08
* vonNeumann 2/12/1994 4732.35
* vonNeumann 1/11/1999 4409.74
* Hoare 8/18/1992 4381.21
* vonNeumann 3/26/2002 4121.85
使用优先队列可以很好的解决这个问题,对于size为M的优先队列MinPQ,插入和删除最小元素的时间是lg(M),对于这个问题我们可以一直从文本里读取数据,读1条插入一条到优先队列MinPQ,当队列size大于M时,删除最小的元素,当文本内容读完后,MinPQ里存的就是前M条最大记录,代码如下:
public class TopM {
// Print the top M lines in the input stream.
public static void main(String[] args) {
int M = Integer.parseInt(args[0]);
MinPQ<Transaction> pq = new MinPQ<Transaction>(M+1);
while (StdIn.hasNextLine()) {
// Create an entry from the next line and put on the PQ.
String line = StdIn.readLine();
Transaction transaction = new Transaction(line);
pq.insert(transaction);
// remove minimum if M+1 entries on the PQ
if (pq.size() > M)
pq.delMin();
} // top M entries are on the PQ
// print entries on PQ in reverse order
Stack<Transaction> stack = new Stack<Transaction>();
for (Transaction transaction : pq)
stack.push(transaction);
for (Transaction transaction : stack)
StdOut.println(transaction);
}
}
二叉堆可以很好的实现优先队列,当一棵二叉树的每个节点都大于等于或小于等于它的两个子节点时,它被称为堆有序,这颗二叉树也被称为二叉堆,堆的根节点要么最大,要么最小,根节点最小的二叉堆堆称为最小堆,反之叫最大堆。我们下面默认以最小堆为例。
在一个二叉堆中,对于位置k的节点,它的父节点在k/2,两个子节点在2k,2k+1.
不管怎么存储每个节点,对节点k,只要满足父节点在k/2,两个子节点在2k,2k+1并且每个节点都大于等于或小于等于它的两个子节点,这些节点组成的数据结构就是二叉堆。一般用数组实现二叉堆,代码如下:
public class MinPQ<Item> implements Iterable<Item> {
Item[] pq;
int N;
public MinPQ(int initCapacity) {
pq = (Item[]) new Object[initCapacity+1];
}
/**
* Initializes an empty priority queue.
*/
public MinPQ() {
this(1);
}
public boolean isEmpty() { return N == 0; }
public int size() { return N; }
/**
* Returns a smallest key on this priority queue.
*
* @return a smallest key on this priority queue
* @throws NoSuchElementException if this priority queue is empty
*/
public Item min() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
return pq[1]; //不使用位置0,因为当k=0时,2k=0,无法表示子节点,从1开始更方便
}
private void resize(int capacity) {
assert capacity >= N;
Item[] temp = (Item[]) new Object[capacity];
for(int i = 1; i <= N; i++)
temp[i] = pq[i];
pq = temp;
}
//插入操作首先把节点插入到叶子节点,然后上浮到合适的位置,上浮过程见函数swim(),插入过程如下图所示
public void insert(Item x) {
if (N == pq.length-1) resize(pq.length*2);
// add x, and percolate it up to maintain heap invariant
pq[++N] = x;
swim(N);
assert isMinHeap();
}
/**
* Removes and returns a smallest key on this priority queue.
*
* @return a smallest key on this priority queue
* @throws NoSuchElementException if this priority queue is empty
*/
//删除操作,获取根节点的元素就是最小元素,然后把最后一个叶子节点元素交换到根节点,再把根节点元素下沉到指定位置。
//下沉操作键函数sink(),删除过程如下图
public Item delMin() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
Item min = pq[1];
exch(1, N--);
sink(1);
pq[N+1] = null; // to avoid loiterig and help with garbage collection
if ((N > 0) && (N == (pq.length - 1) / 4)) resize(pq.length / 2);
assert isMinHeap();
return min;
}
//与父节点比较,如果比父节点小则与父节点交换位置,直到到达根节点或者比父节点大
private void swim(int k) {
while(k > 1 && greater(k/2, k)) {
exch(k/2, k);
k = k/2;
}
}
//与两个子节点中较小的元素进行比较,如果比子节点大则交换位置,直到比子节点小或到达叶子节点
private void sink(int k) {
while (2*k <= N) {
int j = 2*k;
if (j < N && greater(j, j+1)) j++;
if (!greater(k, j)) break;
exch(k, j);
k = j;
}
}
private void exch(int i, int j) {
Item swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
}
// ex2.4.15
// is pq[1..N] a min heap?
private boolean isMinHeap() {
return isMinHeap(1);
}
// is subtree of pq[1..n] rooted at k a min heap?
private boolean isMinHeap(int k) {
if (k > N) return true;
int left = 2*k;
int right = 2*k + 1;
if (left <= N && greater(k, left)) return false;
if (right <= N && greater(k, right)) return false;
return isMinHeap(left) && isMinHeap(right);
}
/***************************************************************************
* Helper functions for compares and swaps.
***************************************************************************/
private boolean greater(int i, int j) {
return ((Comparable<Item>)pq[i]).compareTo(pq[j]) > 0;
}
}
索引优先队列
除了获取优先队列中的最小最大元素,有时候还需要引用优先队列中的其他元素,简单的方法是为每个元素增加一个索引,通过
keyOf(int i)获取指定索引对应的值。
public class IndexMinPQ<Key extends Comparable<Key>> implements Iterable<Integer> {
private int maxN; // maximum number of elements on PQ
private int n; // number of elements on PQ
private int[] pq; // binary heap using 1-based indexing
private int[] qp; // inverse of pq - qp[pq[i]] = pq[qp[i]] = i
private Key[] keys; // keys[i] = priority of i
/**
* Initializes an empty indexed priority queue with indices between {@code 0}
* and {@code maxN - 1}.
* @param maxN the keys on this priority queue are index from {@code 0}
* {@code maxN - 1}
* @throws IllegalArgumentException if {@code maxN < 0}
*/
public IndexMinPQ(int maxN) {
if (maxN < 0) throw new IllegalArgumentException();
this.maxN = maxN;
n = 0;
keys = (Key[]) new Comparable[maxN + 1]; // make this of length maxN??
pq = new int[maxN + 1];
qp = new int[maxN + 1]; // make this of length maxN??
for (int i = 0; i <= maxN; i++)
qp[i] = -1;
}
/**
* Returns true if this priority queue is empty.
*
* @return {@code true} if this priority queue is empty;
* {@code false} otherwise
*/
public boolean isEmpty() {
return n == 0;
}
/**
* Is {@code i} an index on this priority queue?
*
* @param i an index
* @return {@code true} if {@code i} is an index on this priority queue;
* {@code false} otherwise
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
*/
public boolean contains(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
return qp[i] != -1;
}
/**
* Returns the number of keys on this priority queue.
*
* @return the number of keys on this priority queue
*/
public int size() {
return n;
}
/**
* Associates key with index {@code i}.
*
* @param i an index
* @param key the key to associate with index {@code i}
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if there already is an item associated
* with index {@code i}
*/
public void insert(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (contains(i)) throw new IllegalArgumentException("index is already in the priority queue");
n++;
qp[i] = n;
pq[n] = i;
keys[i] = key;
swim(n);
}
/**
* Returns an index associated with a minimum key.
*
* @return an index associated with a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public int minIndex() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
/**
* Returns a minimum key.
*
* @return a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public Key minKey() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return keys[pq[1]];
}
/**
* Removes a minimum key and returns its associated index.
* @return an index associated with a minimum key
* @throws NoSuchElementException if this priority queue is empty
*/
public int delMin() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
int min = pq[1];
exch(1, n--);
sink(1);
assert min == pq[n+1];
qp[min] = -1; // delete
keys[min] = null; // to help with garbage collection
pq[n+1] = -1; // not needed
return min;
}
/**
* Returns the key associated with index {@code i}.
*
* @param i the index of the key to return
* @return the key associated with index {@code i}
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public Key keyOf(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
else return keys[i];
}
/**
* Change the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to change
* @param key change the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void changeKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
keys[i] = key;
swim(qp[i]);
sink(qp[i]);
}
/**
* Change the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to change
* @param key change the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @deprecated Replaced by {@code changeKey(int, Key)}.
*/
@Deprecated
public void change(int i, Key key) {
changeKey(i, key);
}
/**
* Decrease the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to decrease
* @param key decrease the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if {@code key >= keyOf(i)}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void decreaseKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
if (keys[i].compareTo(key) <= 0)
throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key");
keys[i] = key;
swim(qp[i]);
}
/**
* Increase the key associated with index {@code i} to the specified value.
*
* @param i the index of the key to increase
* @param key increase the key associated with index {@code i} to this key
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws IllegalArgumentException if {@code key <= keyOf(i)}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void increaseKey(int i, Key key) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
if (keys[i].compareTo(key) >= 0)
throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key");
keys[i] = key;
sink(qp[i]);
}
/**
* Remove the key associated with index {@code i}.
*
* @param i the index of the key to remove
* @throws IllegalArgumentException unless {@code 0 <= i < maxN}
* @throws NoSuchElementException no key is associated with index {@code i}
*/
public void delete(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
int index = qp[i];
exch(index, n--);
swim(index);
sink(index);
keys[i] = null;
qp[i] = -1;
}
/***************************************************************************
* General helper functions.
***************************************************************************/
private boolean greater(int i, int j) {
return keys[pq[i]].compareTo(keys[pq[j]]) > 0;
}
private void exch(int i, int j) {
int swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
qp[pq[i]] = i;
qp[pq[j]] = j;
}
/***************************************************************************
* Heap helper functions.
***************************************************************************/
private void swim(int k) {
while (k > 1 && greater(k/2, k)) {
exch(k, k/2);
k = k/2;
}
}
private void sink(int k) {
while (2*k <= n) {
int j = 2*k;
if (j < n && greater(j, j+1)) j++;
if (!greater(k, j)) break;
exch(k, j);
k = j;
}
}
索引优先队列的用例Multiway
有N个文本文件,每个文件里存储着按顺序排列的字母,读取所有文件内容,按顺序打印出来
使用索引优先队列首先读取N个文件的第1个字母,插入优先队列,索引从0到N代表N个文件的编号,值是每个文本里第1个字母
后面从队列中每打印删除一个最小元素,同时可以获取这个元素所在文件的索引编号,再从这个文件读取一个字母插入优先队列
保持队列大小为N,直到所有文件内容都读取完,对于代码如下
这个用例也可以用一般的优先队列实现,用一个对象封装两个成员index,value,这样在删除元素时可以通过这个对象的index成员获取对应的文件编号
}
/*************************************************************************
* Compilation: javac Multiway.java
* Execution: java Multiway input1.txt input2.txt input3.txt ...
* Dependencies: IndexMinPQ.java In.java StdOut.java
*
* Merges together the sorted input stream given as command-line arguments
* into a single sorted output stream on standard output.
*
* % more m1.txt
* A B C F G I I Z
*
* % more m2.txt
* B D H P Q Q
*
* % more m3.txt
* A B E F J N
*
* % java Multiway m1.txt m2.txt m3.txt
* A A B B B C D E F F G H I I J N P Q Q Z
*
*************************************************************************/
public class Multiway {
public static void merge(In[] streams) {
int N = streams.length;
IndexMinPQ<String> pq = new IndexMinPQ<String>(N);
for (int i = 0; i < N; i++)
if (!streams[i].isEmpty())
pq.insert(i, streams[i].readString());
// Extract and print min and read next from its stream.
while (!pq.isEmpty()) {
StdOut.print(pq.minKey() + " ");
int i = pq.delMin();
if (!streams[i].isEmpty())
pq.insert(i, streams[i].readString());
}
StdOut.println();
}
public static void main(String[] args) {
int N = args.length;
In[] streams = new In[N];
for (int i = 0; i < N; i++)
streams[i] = new In(args[i]);
merge(streams);
}
}
堆排序
由优先队列衍生的的排序算法叫堆排序,思想很容易理解,把待排序的数组每个元素插入优先队列,再反复删除优先队列中每个元素回写到待排序数组。
这样需要增加额外的存储空间,可以直接在待排序数组中构造优先队列进行排序,代码如下:堆排序的时间复杂度是O(NlogN)
public class Heap {
/**
* Rearranges the array in ascending order, using the natural order.
* @param pq the array to be sorted
*/
public static void sort(Comparable[] pq) {
int n = pq.length;
//从k=n/2开始构造最大堆,这样从k到1都是有子节点的父节点,通过下沉操作,让k和它的子节点构成的子树堆有序,再让k-1和它的子节点构成的子树堆有序
//最后让1和它的子节点构成的树堆有序,这样就把整个数组构成了一个最大堆,示意图如下:
for (int k = n/2; k >= 1; k--)
sink(pq, k, n);
//最大堆构造完成后模拟删除操作,反复删除队列中最大元素依次放到数组n到1的位置,这样数组就有序了,示意图如下:
while (n > 1) {
exch(pq, 1, n--);
sink(pq, 1, n);
}
}
/***************************************************************************
* Helper functions to restore the heap invariant.
***************************************************************************/
private static void sink(Comparable[] pq, int k, int n) {
while (2*k <= n) {
int j = 2*k;
if (j < n && less(pq, j, j+1)) j++;
if (!less(pq, k, j)) break;
exch(pq, k, j);
k = j;
}
}
/***************************************************************************
* Helper functions for comparisons and swaps.
* Indices are "off-by-one" to support 1-based indexing.
***************************************************************************/
//这里二叉堆中节点k的元素是存储在数组k-1的位置的,前面说过不管节点的元素存储在哪里,只要通过k,k/2,2k,2k+1可以找到相应的父子节点,并且k/2>=k>=2k,2k+1,这些节点构成的我就是二叉堆
private static boolean less(Comparable[] pq, int i, int j) {
return pq[i-1].compareTo(pq[j-1]) < 0;
}
private static void exch(Object[] pq, int i, int j) {
Object swap = pq[i-1];
pq[i-1] = pq[j-1];
pq[j-1] = swap;
}
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
StdOut.println(a[i]);
}
}
public static void main(String[] args) {
Integer[] arr = {7,1,3,78,23,45,2,4,9,99,16};
Heap.sort(arr);
show(arr);
}
}
/*
* ex 2.4.25
*
* 计算数论。
* 编写程序 CubeSum.java,
* 在不使用额外空间的条件下,
* 按大小顺序打印所有 a^3+b^3 的结果,
* 其中 a 和 b 为 0 至 N 之间所有的整数。
* 思路:这个问题可以用类似Mutiway问题的思路来理解,当a=0时,b可以取0~N并且值是递增的,当a=1时,b可以取1~N,当a=N时,b可以取N~N
* 把a=0时的所有情况的值归为1个文本中,把a=1时的所有情况的值归为1个文本中...把a=N时的所有情况的值归为1个文本中,可以归为N+1个文本,
* 跟Mutiway类似先把每个文本的第1个值插入优先队列,就是a=b=0,a=b=1到a=b=N.然后每从队列删除打印一个值就从对应文本读一个值再插入到优先队列
* 保持队列大小为N+1,直到所有文本读完。
* 用这段程序找出 0 到 10^6 之间
* 所有满足 a^3+b^3 = c^3+d^3 的不同整数 a, b, c, d。
* 当a^3+b^3 = c^3+d^3时,a,b,c,d会在相邻位置打印,打印时记住上次打印的值对比下就找到了,如下列打印结果的1729
* 1728 = 0^3 + 12^3
* 1729 = 1^3 + 12^3
* 1729 = 9^3 + 10^3
* 1736 = 2^3 + 12^3
* 1755 = 3^3 + 12^3
/******************************************************************************
* Compilation: javac CubeSum.java
* Execution: java CubeSum n
* Dependencies: MinPQ.java
*
* Print out integers of the form a^3 + b^3 in sorted order, where
* 0 <= a <= b <= n.
*
* % java CubeSum 10
* 0 = 0^3 + 0^3
* 1 = 0^3 + 1^3
* 2 = 1^3 + 1^3
* 8 = 0^3 + 2^3
* 9 = 1^3 + 2^3
* ...
* 1729 = 9^3 + 10^3
* 1729 = 1^3 + 12^3
* ...
* 3456 = 12^3 + 12^3
*
* Remarks
* -------
* - Easily extends to handle sums of the form f(a) + g(b)
* - Prints out a sum more than once if it can be obtained
* in more than one way, e.g., 1729 = 9^3 + 10^3 = 1^3 + 12^3
*
******************************************************************************/
public class CubeSum implements Comparable<CubeSum> {
private final int sum;
private final int i;
private final int j;
public CubeSum(int i, int j) {
this.sum = i*i*i + j*j*j;
this.i = i;
this.j = j;
}
public int compareTo(CubeSum that) {
if (this.sum < that.sum) return -1;
if (this.sum > that.sum) return +1;
return 0;
}
public String toString() {
return sum + " = " + i + "^3" + " + " + j + "^3";
}
public static void main(String[] args) {
int n = Integer.parseInt(args[0]);
// initialize priority queue
MinPQ<CubeSum> pq = new MinPQ<CubeSum>();
for (int i = 0; i <= n; i++) {
pq.insert(new CubeSum(i, i));
}
// find smallest sum, print it out, and update
while (!pq.isEmpty()) {
CubeSum s = pq.delMin();
StdOut.println(s);
if (s.j < n)
pq.insert(new CubeSum(s.i, s.j + 1));
}
}
}