算法第二章

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();
        }

    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章