大家好,这里记录下归并排序、快速排序、优先队列的练习过程。
本博客代码示例均来自:算法 Algorithmes Forth Edition
[美] Robert Sedgewick Kevin Wayne 著 谢路云译
文章目录
一、归并排序
1) 自顶向下的归并排序
package com.chm.algorithms;
import org.junit.jupiter.api.Test;
import static com.chm.algorithms.Example.merge;
/**
* Author:meice Huang
* Time: 2020/3/21 下午8:58
* <p>
* 自顶向下的归并排序—— 高效算法设计中分治思想的最典型例子
*/
public class Merge {
private static Comparable[] aux; //归并所需要的辅助数组
public static void sort(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
private static void sort(Comparable[] a, int lo, int hi) {
//将数组a[lo..hi]排序
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid);// 将左半边排序
sort(a, mid + 1, hi);//将右半边排序
merge(a, lo, mid, hi);
}
@Test
public void testMergeSort() {
sort(new Comparable[]{10, 3, 7, 8, 5, 1, 4, 9, 2, 6});
}
}
2)自底向上的归并排序
package com.chm.algorithms;
/**
* Author:meice Huang
* Time: 2020/3/21 下午10:18
* <p>
* 基于堆的优先队列
*/
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;//基于堆的完全二叉树
private int N = 0; //存储与pq[1 .. N ]中,pq[0]没有使用
public MaxPQ(int maxN) {
pq = (Key[]) new Comparable[maxN + 1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public void insert(Key v) {
pq[++N] = v;
swim(N);
}
public Key delMax() {
Key max = pq[1];//从根节点得到最大元素
exch(1, N--);//和最后一个节点交换
pq[N + 1] = null;//防止对象游离
sink(1);//恢复堆的有序性
return max;
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
private void exch(int i, int j) {
Key t = pq[i];
pq[i] = pq[j];
pq[j] = t;
}
private void swim(int k) {
while (k > 1 && less(k / 2, k)) {
exch(k / 2, k);
k = k / 1;
}
}
private void sink(int k) {
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(j, j + 1)) {
j++;
}
if (!less(k, j)) {
break;
}
exch(k, j);
k = j;
}
}
/**
* 堆排序
*/
public void sort(Comparable[] a) {
int N = a.length;
for (int k = N / 2; k >= 1; k--) {
// sink(a,k,N);
while (N > 1) {
Example.exch(a, 1, N--);
// sink(a,1,N);
}
}
}
}
二、快速排序
1) 基本快速排序
package com.chm.algorithms;
import edu.princeton.cs.algs4.StdRandom;
import static com.chm.algorithms.Example.exch;
import static com.chm.algorithms.Example.less;
/**
* Author:meice Huang
* Time: 2020/3/21 下午9:28
* <p>
* 快速排序
* 快速排序也是分治的思想,把一个数组分成2个子数组,将两部分独立的排序,子数组有序后整个数组自然就有序了。
* 归并排序子数组有序后还需要再次归并成有序的。
*/
public class Quick {
public static void sort(Comparable[] a) {
StdRandom.shuffle(a);//消除对输入的依赖
sort(a, 0, a.length - 1); // 这里调用可以换成三向切分的私有sort()方法(Quick3way)
}
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);//将左半部分a[lo .. j-1]排序
sort(a, j + 1, hi);//将右半部分a[j+1 .. hi]排序
}
private static int partition(Comparable[] a, int lo, int hi) {
//将素组切分为a[lo.. i-1], a[i],a[i+1..hi]
int i = lo, 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);//将v = a[j]放入正确的位置
return j;//a[lo..j-1] <= a[j] <= a[j+1..hi] Bingo!
}
}
2)三向切分快排
package com.chm.algorithms;
import static com.chm.algorithms.Example.exch;
/**
* Author:meice Huang
* Time: 2020/3/21 下午9:46
* 三向切分的快速排序
* 荷兰国旗问题
*/
public class Quick3way {
private static void sort(Comparable[] a, int lo, int hi) {
//调用此私有方法,可通过Quick类的共有方法调用
if (hi <= lo) return;
int lt = lo, i = lo + 1, gt = hi;
Comparable v = a[lo];
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);
}
}
三、优先队列(堆实现)
1、一个优先队列的用例
package com.chm.algorithms;
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Transaction;
/**
* Author:meice Huang
* Time: 2020/3/21 下午10:09
* 优先队列
*/
public class TopM {
public static void main(String[] args) {
//打印输入流中最大的M行
int M = Integer.parseInt(args[0]);
MinPQ<Transaction> pq = new MinPQ<Transaction>(M + 1);
while (StdIn.hasNextLine()) {
//为下一行输入创建一个元素并放入优先队列中
pq.insert(new Transaction(StdIn.readLine()));
if (pq.size() > M) {
pq.delMin();//如果优先队列中存在M+1个元素则删除其中最小的元素
}
}//最大的M个元素都在优先队列中
Stack<Transaction> stack = new Stack<>();
while (!pq.isEmpty()) {
stack.push(pq.delMin());
}
for (Transaction t : stack) {
StdOut.println(t);
}
}
}
2、基于堆的优先队列
package com.chm.algorithms;
/**
* Author:meice Huang
* Time: 2020/3/21 下午10:18
* <p>
* 基于堆的优先队列
*/
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;//基于堆的完全二叉树
private int N = 0; //存储与pq[1 .. N ]中,pq[0]没有使用
public MaxPQ(int maxN) {
pq = (Key[]) new Comparable[maxN + 1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public void insert(Key v) {
pq[++N] = v;
swim(N);
}
public Key delMax() {
Key max = pq[1];//从根节点得到最大元素
exch(1, N--);//和最后一个节点交换
pq[N + 1] = null;//防止对象游离
sink(1);//恢复堆的有序性
return max;
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
private void exch(int i, int j) {
Key t = pq[i];
pq[i] = pq[j];
pq[j] = t;
}
private void swim(int k) {
while (k > 1 && less(k / 2, k)) {
exch(k / 2, k);
k = k / 1;
}
}
private void sink(int k) {
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(j, j + 1)) {
j++;
}
if (!less(k, j)) {
break;
}
exch(k, j);
k = j;
}
}
/**
* 堆排序
*/
public void sort(Comparable[] a) {
int N = a.length;
for (int k = N / 2; k >= 1; k--) {
// sink(a,k,N);
while (N > 1) {
Example.exch(a, 1, N--);
// sink(a,1,N);
}
}
}
}
3、使用优先队列的多项归并
package com.chm.algorithms;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.IndexMinPQ;
import edu.princeton.cs.algs4.StdOut;
/**
* Author:meice Huang
* Time: 2020/3/21 下午10:32
* 使用优先队列的多项合并
*/
public class Multiway {
public static void merge(In[] streams) {
int N = streams.length;
IndexMinPQ<String> pq = new IndexMinPQ<>(N);
for (int i = 0; i < N; i++) {
if (!streams[i].isEmpty()) {
pq.insert(i, streams[i].readString());
}
}
while (!pq.isEmpty()) {
StdOut.println(pq.minKey());
int i = pq.delMin();
if (!streams[i].isEmpty()) {
pq.insert(i, streams[i].readString());
}
}
}
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);
}
}
}
挺难理解的,你们呢?