(代码和图片来自何老师)
一. 各种简单比较
比较器的实现 comparable API 以及交换方法
v.compareTo(w)
return 0 if same
return 1 if v>w
return -1 if v<w
private static boolean less (Comparable v, Comparable w)
{
return v.compareTo(w) < 0;
}
private static void exch (Comparable[] a, int i, int j) {
Comparable swap = a[i];
a[i] = a[j];
a[j] = swap;
}
selection sort
从左到右每次循环找出后面最小的数并且与循环次数i交换
public static void sort (Comparable[] a) {
int N = a.length;
for (int i = 0; i < N; i++) {
int min = i;
for (int j = i+1; j < N; j++)
if (less(a[j], a[min]))
min = j;
exch( a, i, min);
}
}
insertion sort
从左到右i次循环,将前i-1个元素与第i个元素进行比较,让前i个元素排列有序
public static void sort (Comparable[] a) {
int N = a.length;
for (int i = 0; i < N; i++)
for (int j = i; j > 0; j--)
if (less(a[j], a[j-1]))
exch( a, j, j-1);
else break;
}
shell sort
设定一个h值作为评判标准,在每一次循环当中,从第i个元素开始(i=h),将i,i-h,i-2h...进行排序,并逐一递加i直到全部迭代完。
for (int i = h; i < N; i++) {
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h)
exch( a, j, j-h);
}
之后对h的数值进行更改,并且再次重述上述操作,下面实例以每次将h除以3
public static void sort (Comparable[] a) {
int N = a.length;
int h = 1;
while (h < N/3) h = 3*h + 1; // 1, 4, 13, 40, 121, 364, ...
while (h >= 1) {
// h-sort the array.
for (int i = h; i < N; i++) {
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h)
exch( a, j, j-h);
}
h = h/3;
}
}
对于三种排序方式,时间复杂度如上,可见如果出现最差情况会大幅度影响计算速度,因此可以使用shuffling 操作对数组进行打乱洗牌以求不出现最差情况、
二. merge sort
merge 即将两个各自排好序的数组进行合并
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if (i > mid) a[k] = aux[j++];
else if (j > hi) a[k] = aux[i++];
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else a[k] = aux[i++];
}
}
merge sort的思想即是递归调用sort 之后逐一对小数组进行合并
public class Merge {
private static void merge (...) {
/* as before */
}
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid+1, hi);
if (!less(a[mid+1], a[mid])) return;//如果两个数组前一个的最大值小于后一个最小值不需要排序
merge(a, aux, lo, mid, hi);
}
public static void sort (Comparable[] a) {
Comparable[] aux = new Comparable[a.length];
sort( a, aux, 0, a.length - 1);
}
}
三. quick sort
以第一个元素为基准,对于数组后续元素进行查找,如果前端有一个大于基准元素的则会与后面小于基准元素的元素进行互换,设置出循环条件之后即可
实现:partition 方法 返回基准元素的所在位置
代码如下,逐一递归:
public class Quick {
private static int partition(Comparable[] a, int lo, int hi) {
/* see previous slide */
}
public static void sort (Comparable[] a) {
StdRandom.shuffle( a);
sort( a, 0, a.length - 1);
}
private static void sort (Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
if (hi <= lo + CUTOFF - 1) {
Insertion.sort( a, lo, hi);
return;
}//添加条件,当元素较少的时候可以直接进行insertion sort
int j = partition( a, lo, hi);
sort( a, lo, j-1);
sort( a, j+1, hi);
}
}
quick sort可以进行元素查找,例如查找在数组a中第k个元素的大小
public static Comparable select(Comparable[] a, int k)
{
StdRandom.shuffle( a);
int lo = 0, hi = a.length - 1;
while (hi > lo) {
int j = partition( a, lo, hi);
if (j < k) lo = j + 1;
else if (j > k) hi = j - 1;
else return a[k];
}
return a[k];
}
由于quick sort本身的局限性,导致如果数组里面和基准元素相等的元素数量比较多,会造成反复的相同变换,因此这里有一个升级版的3-ways quick sort
原理:在数组遍历的时候,如果元素比基准元素小,则交换到相同数组之前,并且lo++
如果元素比基准元素大,则直接置换到gt后,i不变从而防止将大的置换过来未处理
如果元素等于基准元素,则将i递加,随着相同元素逐渐增多,i和lt的差距也越来越大
四. Priority queues
在介绍优先级队列之前,先来了解binary heap
二进制堆丢弃了数组的零元素,将第一个元素放置在位置1,这样的好处是让n元素的父节点为n/2,子节点为2n,2n+1,便于比较查找以及上浮下沉。
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;
private int n;
public MaxPQ(int capacity)
{ pq = (Key[]) new Comparable[capacity+1]; }
public boolean isEmpty()
{ return n == 0; }
public void insert(Key x){
pq[++n]=x;
swim(n);
}
public Key delMax() {
Key max=pq[1];
exch(1,n--);
sink(1);
pq[n+1]=null;
return max;
}
private void swim(int k){
while(k>1&& less(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&&less(j,j+1))j++;
if (!less(k,j)) break;
exch(k,j);
k=j;
}
}
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; }
}
除此之外还有minpq最小堆,只需要将less的符号转换即可,有了堆之后,就可以进行heap构建以及排序
public class Heap{
public static void sort (Comparable[] a) {
int n = a.length;
for (int k = n/2; k >= 1; k--)
sink( a, k, n);
while (n > 1) {
exch( a, 1, n);
sink( a, 1, --n);
}
}
private static void sink(Comparable[] a, int k, int n)
{ /* as before */ }
private static boolean less(Comparable[] a, int i, int j)
{ /* as before */ }
private static void exch(Object[] a, int i, int j)
{ /* as before */ }
}