1.排序类算法模板
快速排序是最快的通用排序算法
java对于原始数据类型使用快速排序
引用类型使用归并排序
/*
如果一个排序算法能够保留数组中重复元素的相对位置则可以被称为稳定的 插入排序 归并排序
选择排序 希尔排序 快速排序 堆排序 为不稳定的
堆排序的比较次数是归并排序的两倍 ,两者访问数组的次数都比快速排序多的多
归并排序不是原地排序,其余都是原地排序
算法 是否稳定 是否为原地排序 时间复杂度 空间复杂度
选择排序 否 是 n2 1
插入排序 是 是 n--n2 1
希尔排序 否 是 ?n6/5 nlogn 不确定 1
快速排序 否 是 nlogn lgn
三项快速排序 否 是 n-nlogn lgn
归并排序 是 否 nlogn n
堆排序 否 是 nlogn 1
*/
public class Example {
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
2.选择排序
1.运行时间和输入无关
2.数据移动是最少的
/*
选择排序 找到数组中最小的元素,放在第i=0位,i++,然后是次小的元素,放在第i=1位,
最后找到次最大的位置,放在a.length-2的位置,最后一位直接放在a.length
每次都是至交换一个位置 所以交换次数是N次
比较次数是n-1 + n-2 ....1 n2/2
对于长度为N的数组,选择排序需要大约 N2/2次 比较和 N次交换
1.运行时间和输入无关
2.数据移动是最少的
*/
public class SelectionSort {
public static void sort(Comparable[] a) {
//将a[] 按升序排列
int N = a.length; //数组长度
for (int i = 0; i < N; i++) {
//将a[i]和a[i+1]...a[n] 中最小的元素交换
int min = i; // 最小元素的索引
for (int j = i + 1; j < N; j++) {
if (less(a[j], a[min])) min = j;
}
exch(a, i, min);
}
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3};
sort(a);
assert isSorted(a);
show(a);
}
}
3.插入排序
/*
插入排序 插入手牌,将每一张牌插入到其他已经有序的牌中的适当位置
索引左边都是有序的,但他们的最终位置却不确定,当索引到达最右端的时候排序已经完成
和选择排序不同的是插入排序的时间取决于输入中元素的初始顺序
对于随机排列的长度为N且主键不重复的数组,平均情况下插入排序需要 N2/4 次比较 以及N2/4次交换
最坏则需要N2/2次交换和比较 最好N-1次比较 0次交换
*/
public class InsertionSort {
public static void sort(Comparable[] a){
//将a[]按升序排列
int N=a.length;
for (int i = 1; i <N ; i++) {
//将 a[i] 插入到 a[i-1] a[i-2] a[i-3]中
for (int j =i ; j >0&&less(a[j],a[j-1]) ; j--) { //less a[j]<a[j-1]
exch(a,j,j-1);
}
}
}
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int i,int j){
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
private static void show(Comparable[] a){
//在单行打印数组
for (int i = 0; i <a.length ; i++) {
System.out.print(a[i]+ " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a){
//测试数组元素是否有序
for (int i = 1; i <a.length ; i++) {
if (less(a[i],a[i-1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
4.希尔排序
/*
希尔排序: 基于插入排序的快速排序法
对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端
思想:使数组中任意间隔为H的元素都是有序的,称为h有序数组,一个h有序数组就是h个相互独立的有序数组编制在一起组成一个有序数组
希尔排序高效的原因 它权衡了子数组的规模和有序性
*/
public class ShellSort {
public static void sort(Comparable[] a) {
//将 a[] 按升序排列
int n = a.length;
System.out.println("length " + n);
int h = 1;
while (h < n / 3) h = 3 * h + 1;
System.out.println("h= " + h);
int k = 0;
while (h >= 1) {
//将 数组变为h 有序
for (int i = h; i < n; i++) {
// 将a[i] 插入到 a[i-h] ,a[i-2h] ,a[i-3h]....之中
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
exch(a, j, j - h);
}// for i
} // for j
h = h / 3;
} // while
} //sort
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
show(a);
sort(a);
assert isSorted(a);
show(a);
}
}
5.归并排序
1.自顶向下的归并排序
public class MergeSort3 {
public static void sort(Comparable[] arr) {
Comparable[] temp = new Comparable[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
mergeSort(arr, 0, arr.length - 1, temp);
}
private static void mergeSort(Comparable[] arr, int left, int right, Comparable[] temp) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);//左边归并排序,使得左子序列有序
mergeSort(arr, mid + 1, right, temp);//右边归并排序,使得右子序列有序
merge(arr, left, mid, right, temp);//将两个有序子数组合并操作
}
}
/*
a(a,0,0,1,temp)
*/
private static void merge(Comparable[] arr, int left, int mid, int right, Comparable[] temp) {
int i = left;//左序列指针 0
int j = mid + 1;//右序列指针 1
int t = 0;//临时数组指针
while (i <= mid && j <= right) {
if (less(arr[i], arr[j])) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
while (i <= mid) {//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while (j <= right) {//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right) {
arr[left++] = temp[t++];
}
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
简化merge
/*
归并排序(1)——自顶向下
保证长度为N的数组排序所需的时间和NlogN成正比,缺点:额外空间和N成正比
对于任意长度的为N的任意数组,自顶向下的归并排序需要0.5NlgN至NlgN次比较
对于任意长度的为N的数组,自顶向下的归并排序最多需要访问数组6NlgN次
2N次用来复制,2N次用来将排好序的元素移动回去,另外最多比较2N次
*/
public class MergeSort {
private static Comparable[] temp; //归并所需的辅助数组
public static void sort(Comparable[] a) {
int n = a.length;
temp = new Comparable[n]; //一次性分配空间
sort(a, 0, n - 1);
}
private static void sort(Comparable[] a, int lo, int hi) {
//将数组a[lo...hi] 排序
if (lo >= hi) return;
int mid = (lo + hi) / 2;
sort(a, lo, mid); //对左半边排序
sort(a, mid + 1, hi); //对右半边排序
merge(a, lo, mid, hi); //归并排序结果
}
public static void merge(Comparable[] a, int lo, int mid, int hi) {
//将a[lo...mid] 和 a[mid+1....hi] 归并
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
temp[k] = a[k];
}// 将a[lo...hi] 复制到temp[lo..hi] 首先将所有的元素复制到temp【】中
//然后再归并回a[]中,
for (int k = lo; k <= hi; k++) {
if (i > mid) a[k] = temp[j++];// 左半边用尽(取右半边的元素)
else if (j > hi) a[k] = temp[i++]; // 右半边用尽(取左半边的元素)
//右半边的当前元素小于左半边的当前元素(取右半边的元素)
else if (less(temp[j], temp[i])) a[k] = temp[j++];
//右半边的当前元素大于等于左半边的当前元素(取左半边元素)
else a[k] = temp[i++];
}
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
2.自底向上的归并排序
/*
归并排序(2)——自底向上
保证长度为N的数组排序所需的时间和NlogN成正比,缺点:额外空间和N成正比
对于任意长度的为N的任意数组,自顶向下的归并排序需要0.5NlgN至NlgN次比较
对于任意长度的为N的数组,自顶向上的归并排序每一遍会访问数组6N次
自底向上的归并排序比较适用于链表组织的数据 1,2,4,8的子链表进行排序
可以将链表进行原地排序
*/
public class MergeSort2 {
private static Comparable[] temp; //归并所需的辅助数组
public static void sort(Comparable[] a) {
int n = a.length;
temp = new Comparable[n]; //一次性分配空间
for (int i = 1; i < n; i += i) { // i 表示为子数组的大小
for (int lo = 0; lo < n - i; lo += i + i) { // lo 子数组的索引
merge(a, lo, lo + i - 1, Math.min(lo + i + i - 1, n - 1)); //(a,0,0,1) (a,2,2,3) (a,4,4,5) (a,6,6,7) (a,8,8,9) 第一组两两归并
// (a,0,1,3) (a,4,5,7) (a,8,9,11) 间隔为2
//间隔 为4
//自底向上的归并排序会多次遍历整个数组,根据子数组大小两两归并
// 子数组的大小i的初始值为1,每次加倍,最后一个子数组的大小只有在数组大小
// 是i的偶数倍的时候才会等于i(否则它会比i小)
}
}
}
public static void merge(Comparable[] a, int lo, int mid, int hi) {
//将a[lo...mid] 和 a[mid+1....hi] 归并
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
temp[k] = a[k];
}// 将a[lo...hi] 复制到temp[lo..hi] 首先将所有的元素复制到temp【】中
//然后再归并回a[]中,
for (int k = lo; k <= hi; k++) {
if (i > mid) a[k] = temp[j++];// 左半边用尽(取右半边的元素)
else if (j > hi) a[k] = temp[i++]; // 右半边用尽(取左半边的元素)
//右半边的当前元素小于左半边的当前元素(取右半边的元素)
else if (less(temp[j], temp[i])) a[k] = temp[j++];
//右半边的当前元素大于等于左半边的当前元素(取左半边元素)
else a[k] = temp[i++];
}
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
6.快速排序
/*
快速排序是一种分治的排序算法
它将一个数组分成两个子数组,将两部分独立的排序,快速排序和归并排序是互补的
快速排序:先分治,在排序
归并排序:先分成两个数组分别排序,再将归并的数组排序
对于长度为N的无重复元素数组排序,快速排序所需要的平均时间2NlnN次比较 (以及1/6次交换)
public static void shuffle(Object[] a) {
validateNotNull(a);
int n = a.length;
for (int i = 0; i < n; i++) {
int r = i + uniform(n-i); // between i and n-1
Object temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
*/
public class QuickSort {
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;
int j = partiton(a, lo, hi); // 切分
sort(a, lo, j - 1); //在左半部分a[lo......j-1]排序
sort(a, j + 1, hi); //将有半部分a[j+1.....hi]排序
}
/*
切分:
1.对于某个j,a[j]已经排定
2.a[lo]到a[j-1]中的所有元素都不大于a[j]
3.a[j+1]到a[hi] 中的所有元素都不小于a[j]
*/
private static int partiton(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]; //切分元素
//这段代码按照a[lo]的值v进行切分,当指针i和j相遇时主循环退出。
//在循环中,a[i]小于v时我们增大i,a[j]大于v时我们减小j,然后交换a[i] 和a[j]来保证i左侧的元素不大于v
//j的元素都不小于v 当指针相遇时 交换a[lo] 和a[j] ,切分结束
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]
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
2.三项切分的快速排序
public class Quick3Way {
public static void sort(Comparable[] a) {
int n = a.length;
sort(a, 0, n - 1);
}
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int lt = lo, i = lo + 1, gt = hi; //(i和gt都是)
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);
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static void show(Comparable[] a) {
//在单行打印数组
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
//测试数组元素是否有序
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i - 1])) return false;
}
return true;
}
public static void main(String[] args) {
Comparable[] a = {7, 8, 9, 4, 5, 6, 1, 2, 3, 4, 5, 6, 66, 5, 8, 78, 44, 65, 2, 55, 2, 5, 2, 58, 1, 5, 8, 2, 15, 2, 1, 5, 1, 5, 2, 2, 5, 24564, 54, 66, 6, 6, 4, 4};
sort(a);
assert isSorted(a);
show(a);
}
}
7.优先队列
最小堆 倒序排序(将最小值(1)与n–交换)
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
/*
优先队列 最小堆
对于一个含有N个元素的基于堆的优先队列,插入元素操作只需不超过logN+1次比较、
删除最小元素的操作需要不超过2lgN次比较
*/
public class MinPQ<Key> implements Iterable<Key> {
public Key[] pq; // 定义一个数组,完全二叉树
public int n; // 规模
public Comparator<Key> comparator; // 可选择的比较器
// 构造器
public MinPQ(int initCapacity) {
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
//构造器
public MinPQ() {
this(1);
}
// 构造器
public MinPQ(int initCapacity, Comparator<Key> comparator) {
this.comparator = comparator;
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
//构造器
public MinPQ(Comparator<Key> comparator) {
this(1, comparator);
}
//构造器
public MinPQ(Key[] keys) {
n = keys.length;
pq = (Key[]) new Object[keys.length + 1];
for (int i = 0; i < n; i++)
pq[i + 1] = keys[i];
for (int k = n / 2; k >= 1; k--)
sink(k);
assert isMinHeap();
}
public void sort(Comparable[] a) {//逆序排序 6 5 4 3 2 1
int N = a.length;
for (int k = N / 2; k >= 1; k--) sink(a, k, N);//sink将a[1]到a[n]排序 for循环构造了一个初始最小的堆
while (N > 1) {
exch(1, N--); //while将最小的元素与a[n]交换,之后在进行堆的修复,所以排序结果为9 8 7 6 5 4 3 2 1逆序
sink(a, 1, N);
}
}
private void sink(Comparable[] a, int i, int n) {
while (2 * i <= n) {
int j = 2 * i; //下沉操作,k的每个子节点都是左节点2K 右节点2K+1的关系
if (j < n && greater(j, j + 1)) j++; // k是父亲, 判断J<n 是否沉到了最底部,比较j(左子树)是否比j+1(右子树)大 大的话 j++ 放在右子树
if (!greater(i, j)) break; // 判断k是否大于J 如果k大于j 说明 k已经沉到了最底部
exch(i, j); //交换 k和J的值
i = j; //将j赋值给k开始下一轮循环下沉
}
}
// 是否为空的
public boolean isEmpty() {
return n == 0;
}
//返回规模大小
public int size() {
return n;
}
//查看最小值
public Key min() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
// 调整规模
public void resize(int capacity) {
assert capacity > n;
Key[] temp = (Key[]) new Object[capacity];
for (int i = 1; i <= n; i++) {
temp[i] = pq[i];
}
pq = temp;
}
//插入一个新的数据
public void insert(Key x) {
// 扩容
if (n == pq.length - 1) resize(2 * pq.length);
// 添加元素到末尾 ,让其上浮到合适的位置
pq[++n] = x;
swim(n);
assert isMinHeap();
}
//删除顶部的最小值
public Key delMin() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
Key min = pq[1]; //从根节点得到最小的元素
exch(1, n--); //将最小元素和最后一个元素交换
sink(1); //将n这个元素开始下沉,恢复堆的有序性
pq[n + 1] = null; // 防止越界
if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2);
assert isMinHeap();
return min;
}
//上浮
public void swim(int k) { //小的节点上浮,可以理解为等级制度,0为优先级最高,在树的最顶端
while (k > 1 && greater(k / 2, k)) {
exch(k, k / 2);
k = k / 2;
}
}
public void sink(int k) { //下沉不断地向下移动直到它的子节点都比它更大或者是到达了堆的底部
while (2 * k <= n) {
int j = 2 * k; //下沉操作,k的每个子节点都是左节点2K 右节点2K+1的关系
if (j < n && greater(j, j + 1)) j++; // k是父亲, 判断J<n 是否沉到了最底部,比较j(左子树)是否比j+1(右子树)大 大的话 j++ 放在右子树
if (!greater(k, j)) break; // 判断k是否大于J 如果k大于j 说明 k已经沉到了最底部
exch(k, j); //交换 k和J的值
k = j; //将j赋值给k开始下一轮循环下沉
}
}
private boolean greater(int i, int j) { //判断i是否>大于 j
if (comparator == null) {
return ((Comparable<Key>) pq[i]).compareTo(pq[j]) > 0;
} else {
return comparator.compare(pq[i], pq[j]) > 0;
}
}
private void exch(int i, int j) { //交换
Key swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
}
// 是否是一个最小堆
private boolean isMinHeap() {
return isMinHeap(1);
}
//是否是一个最小堆 实现
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);
}
public Iterator<Key> iterator() {
return new MinPQ.HeapIterator();
}
private class HeapIterator implements Iterator<Key> {
private MinPQ<Key> copy;
public HeapIterator() {
if (comparator == null) copy = new MinPQ<Key>(size());
else copy = new MinPQ<Key>(size(), comparator);
for (int i = 1; i <= n; i++)
copy.insert(pq[i]);
}
public boolean hasNext() {
return !copy.isEmpty();
}
public void remove() {
throw new UnsupportedOperationException();
}
public Key next() {
if (!hasNext()) throw new NoSuchElementException();
return copy.delMin();
}
}
public static void main(String[] args) {
MinPQ<String> pq = new MinPQ<String>();
while (!StdIn.isEmpty()) {
String item = StdIn.readString();
if (!item.equals("-")) pq.insert(item);
else if (!pq.isEmpty()) StdOut.print(pq.delMin() + " ");
}
StdOut.println("(" + pq.size() + " left on pq)");
}
}
最大堆 排序(最大元素1与n–交换)
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
/*
从N个输入中找到最大的M个元素所需成本
时间 空间
排序算法的用例: Nlogn N
调用初级实现的优先队列: NM M
调用基于堆实现的优先队列: NlogM M
//创建一个优先队列
MaxPQ(){}
//创建一个容量为max的优先队列
MaxPQ(int max){}
//用a[]中的元素创建一个优先队列
MaxPQ(key[] a){}
//向优先队列中插入一个元素
void Insert(key v){}
//返回最大元素
key max(){
return null;
}
//删除并返回最大元素
key delMax(){
return null;
}
//返回队列是否为空
boolean isEmpty(){
return false;
}
//返回优先队列中的元素个数
int size(){
return 0;
}
*/
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MaxPQ<Key> implements Iterable<Key> {
public Key[] pq; // store items at indices 1 to n
private int n; // number of items on priority queue
private Comparator<Key> comparator; // optional comparator
public MaxPQ(int initCapacity) {
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
public MaxPQ() {
this(1);
}
public MaxPQ(int initCapacity, Comparator<Key> comparator) {
this.comparator = comparator;
pq = (Key[]) new Object[initCapacity + 1];
n = 0;
}
public MaxPQ(Comparator<Key> comparator) {
this(1, comparator);
}
public MaxPQ(Key[] keys) {
n = keys.length;
pq = (Key[]) new Object[keys.length + 1];
for (int i = 0; i < n; i++)
pq[i + 1] = keys[i];
for (int k = n / 2; k >= 1; k--)
sink(k);
assert isMaxHeap();
}
public void sort(Comparable[] a) { //顺序排序 1 2 3 4 5 6
int N = a.length;
for (int k = N / 2; k >= 1; k--) sink(a, k, N);//构建一个最大堆
while (N > 1) {
exch(1, N--); //将最大的元素与a[1]交换,保证a[n]永远是大的元素
sink(a, 1, N);
}
}
private void sink(Comparable[] a, int i, int n) {
while (2 * i <= n) {
int j = 2 * i; //下沉操作,k的每个子节点都是左节点2K 右节点2K+1的关系
if (j < n && less(j, j + 1)) j++; // k是父亲, 判断J<n 是否沉到了最底部,比较j(左子树)是否比j+1(右子树)小 小的话 j++ 放在右子树
if (!less(i, j)) break; // 判断k是否小于J 如果k大于j 说明 k已经沉到了最底部
exch(i, j); //交换 k和J的值
i = j; //将j赋值给k开始下一轮循环下沉
}
}
public boolean isEmpty() {
return n == 0;
}
public int size() {
return n;
}
public Key max() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
// helper function to double the size of the heap array
private void resize(int capacity) {
assert capacity > n;
Key[] temp = (Key[]) new Object[capacity];
for (int i = 1; i <= n; i++) {
temp[i] = pq[i];
}
pq = temp;
}
public void insert(Key x) {
// double size of array if necessary
if (n == pq.length - 1) resize(2 * pq.length);
// add x, and percolate it up to maintain heap invariant
pq[++n] = x;
swim(n);
assert isMaxHeap();
}
public Key delMax() {
if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
Key max = 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 isMaxHeap();
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) {
if (comparator == null) {
return ((Comparable<Key>) pq[i]).compareTo(pq[j]) < 0;
} else {
return comparator.compare(pq[i], pq[j]) < 0;
}
}
private void exch(int i, int j) {
Key swap = pq[i];
pq[i] = pq[j];
pq[j] = swap;
}
// is pq[1..N] a max heap?
private boolean isMaxHeap() {
return isMaxHeap(1);
}
// is subtree of pq[1..n] rooted at k a max heap?
private boolean isMaxHeap(int k) {
if (k > n) return true;
int left = 2 * k;
int right = 2 * k + 1;
if (left <= n && less(k, left)) return false;
if (right <= n && less(k, right)) return false;
return isMaxHeap(left) && isMaxHeap(right);
}
public Iterator<Key> iterator() {
return new HeapIterator();
}
private class HeapIterator implements Iterator<Key> {
// create a new pq
private edu.princeton.cs.algs4.MaxPQ<Key> copy;
// add all items to copy of heap
// takes linear time since already in heap order so no keys move
public HeapIterator() {
if (comparator == null) copy = new edu.princeton.cs.algs4.MaxPQ<Key>(size());
else copy = new edu.princeton.cs.algs4.MaxPQ<Key>(size(), comparator);
for (int i = 1; i <= n; i++)
copy.insert(pq[i]);
}
public boolean hasNext() {
return !copy.isEmpty();
}
public void remove() {
throw new UnsupportedOperationException();
}
public Key next() {
if (!hasNext()) throw new NoSuchElementException();
return copy.delMax();
}
}
public static void main(String[] args) {
MaxPQ<String> pq = new MaxPQ<String>();
while (!StdIn.isEmpty()) {
String item = StdIn.readString();
if (!item.equals("-")) pq.insert(item);
else if (!pq.isEmpty()) StdOut.print(pq.delMax() + " ");
}
StdOut.println("(" + pq.size() + " left on pq)");
}
}
8.所以优先队列
import java.util.Iterator;
import java.util.NoSuchElementException;
/*
索引优先队列
优先队列:利用完全二叉树的结构删除队列中最大或最小的元素,当删除堆顶最小对象时,将末尾对象放到堆顶,然后执行下沉操作
优先队列存在一个缺点,就是不能访问已存在优先队列中的对象,并更新他们
pq存储的是与对象相关的整数值 注意数组pq是连续存放的,此时pq作为优先队列,但是上浮和下沉操作,我们比较的是pq中值作为
作为下标的元素数组中的值
例:pq[1]=6 pq[2]=3 pq[3]=8 pq[4]=1 pq[5]=4
ele: {1,"k"} {3,"f"} {4,"n"} {6,"c"} {8,"h"} 此时的 c f h k n (最小堆顺序)
这时如果我们想插入一个{10,"b"} 这时整个树结构发生变化
pq[1]=10 pq[2]=3 pq[3]=6 pq[4]=1 pq[5]=4 pq[6]=8 (可以看出pq就是用来维护优先队列,让ele中元素保持不变)
ele: {1,"k"} {3,"f"} {4,"n"} {6,"c"} {8,"h"} {10,"b"} b c f h k n
但是 如果我们想要改变a{3,"a"}使整数3; 相关的字符串变为“a” ,首先将ele[3]=a,然后维护pq中的值,这个时候维护pq值出现了新的问题,我们不知道
pq中哪个位置中的值为3,只能从头进行遍历,找到之后再次进行上浮和下沉操作
为了能够快速找到pq中元素值所对应的下标,我们需要额外设置一个数组 qp 它的作用是存储与对象内存相关的整数在pq数组中的下标
pq[1]= 10 pq[2]=3 pq[3]=6 pq[4]=1 pq[5]=4 pq[6]=8
ele: {1,"k"} {3,"f"} {4,"n"} {6,"c"} {8,"h"} {10,"b"}
qp[1]=4 qp[3]=2 q[4]=5 q[6]=3 q[8]=6 qp[10]= 1
在上述的基础模型上,假设我们需要将与整数3相关的字符串修改为 a(原来是f),那么我们只需将 ele[3] = "a",然后通过qp[3]中的值2就可以知道数组
pq[2]中的值3对应的下标为2,然后我们对pq[2]进行上浮和下沉操作,那么如果我们要交换qp[1] 和 qp[2]的值,在交换pq数组中的两个元素的值时,
我们也需要交换qp对应两个元素的值
pq[1]= 10 pq[2]=3 pq[3]=6 pq[4]=1 pq[5]=4 pq[6]=8 pq[1]= 10 pq[2]=3 pq[3]=6 pq[4]=1 pq[5]=4 pq[6]=8
ele: {1,"k"} {3,"f"} {4,"n"} {6,"c"} {8,"h"} {10,"b"} ————> ele: {1,"k"} {3,"a"} {4,"n"} {6,"c"} {8,"h"} {10,"b"}
qp[1]=4 qp[3]=2 q[4]=5 q[6]=3 q[8]=6 qp[10]= 1 qp[1]=4 qp[3]=2 q[4]=5 q[6]=3 q[8]=6 qp[10]= 1
pq[1]= 3 pq[2]=10 pq[3]=6 pq[4]=1 pq[5]=4 pq[6]=8
ele: {1,"k"} {3,"a"} {4,"n"} {6,"c"} {8,"h"} {10,"b"} 注意pq[1] pq[2] qp[3] qp[10] 都发生了变化
qp[1]=4 qp[3]=1 q[4]=5 q[6]=3 q[8]=6 qp[10]= 2
接下来代码实现
*/
public class IndexMinPQ<Key extends Comparable<Key>> {
private int maxN; // pq的最大元素数量
private int n;// PQ中元素的数量
private int[] pq; //索引二叉堆,由1开始
private int[] qp; //逆序: qp[pq[i]] = pq[qp[i]]=i qp的下标值ele的整数,qp的值为pq的下标,用来快速找到pq[]
private Key[] keys; //有优先级之分的元素
public IndexMinPQ(int maxN) {
if (maxN < 0) throw new IllegalArgumentException(); //maxN不能小于0,抛出异常
this.maxN = maxN;
n = 0;
keys = (Key[]) new Comparable[maxN + 1]; //key[0] 无意义,从k[1]---k[n]
pq = new int[maxN + 1]; //pq[0] 无意义
qp = new int[maxN + 1]; //qp[0] 无意义
for (int i = 0; i <= maxN; i++)
qp[i] = -1; //将qp[i] 全部初始化
}
public boolean isEmpty() {
return n == 0;
}
public boolean contains(int i) {
if (i < 0 || i >= maxN) throw new IllegalArgumentException();
return qp[i] != -1;
}
public int size() {
return n;
}
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);
}
public int minIndex() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return pq[1];
}
public Key minKey() {
if (n == 0) throw new NoSuchElementException("Priority queue underflow");
return keys[pq[1]];
}
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;
}
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];
}
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]);
}
@Deprecated
public void change(int i, Key key) {
changeKey(i, key);
}
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]);
}
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]);
}
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;
}
private boolean greater(int i, int j) { //i>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;
}
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;
}
}
public Iterator<Integer> iterator() {
return new IndexMinPQ.HeapIterator();
}
private class HeapIterator implements Iterator<Integer> {
// create a new pq
private edu.princeton.cs.algs4.IndexMinPQ<Key> copy;
// add all elements to copy of heap
// takes linear time since already in heap order so no keys move
public HeapIterator() {
copy = new edu.princeton.cs.algs4.IndexMinPQ<Key>(pq.length - 1);
for (int i = 1; i <= n; i++)
copy.insert(pq[i], keys[pq[i]]);
}
public boolean hasNext() {
return !copy.isEmpty();
}
public void remove() {
throw new UnsupportedOperationException();
}
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return copy.delMin();
}
}
public static void main(String[] args) {
String[] strings = {"it", "was", "the", "best", "of", "times", "it", "was", "the", "worst"};
edu.princeton.cs.algs4.IndexMinPQ<String> pq = new edu.princeton.cs.algs4.IndexMinPQ<String>(strings.length);
for (int i = 0; i < strings.length; i++) {
pq.insert(i, strings[i]);
}
while (!pq.isEmpty()) {
int i = pq.delMin();
StdOut.println(i + " " + strings[i]);
}
StdOut.println();
for (int i = 0; i < strings.length; i++) {
pq.insert(i, strings[i]);
}
for (int i : pq) {
StdOut.println(i + " " + strings[i]);
}
while (!pq.isEmpty()) {
pq.delMin();
}
}
}