1. 程式人生 > >Java實現堆排序和計數排序

Java實現堆排序和計數排序

ret wap static 堆排序 變更 heapsort mil 葉子節點 source

堆排序代碼:

import java.util.Arrays;

/**
 * 思路:首先要知道大頂堆和小頂堆,數組就是一個堆,每個i節點的左右孩子是2i+1和2i+2
 *         有了堆,將其堆化:從(n/2)-1個元素開始向下修復,將每個節點修復為小(大)頂堆
 *         修復完成後,數組具有小(大)頂堆的性質
 *         按序輸出:小頂堆可以對數組逆序排序,每次交換堆頂和末尾元素,對堆頂進行向下修復,這樣次小元素又到堆頂了
 * 
 * 時間復雜度:堆化:一半的元素修復,修復是單分支的,所以整體堆化為nlgn/2
 * 排序:n個元素都要取出,因此調整n次,每次調整修復同上是lgn的,整體為nlgn
 * 空間復雜度:不需要開辟輔助空間
 * 原址排序
 * 穩定性
 *
 
*/ public class HeapSort { static void sort(int []A){ // 堆排序第一步: 先對A進行堆化 makeMinHeap(A); for(int x = A.length-1;x>=0;x--){ // 堆排序第二步: 把堆頂,0號元素和最後一個元素對調 swap(A, 0, x); // 堆排序第三步:縮小堆的範圍,對堆頂元素進行向下調整 MinHeapFixDown(A, 0, x); } }
static void makeMinHeap(int[] A){ int n = A.length; for(int i = n/2-1;i>=0;i--){ MinHeapFixDown(A,i,n); } } private static void MinHeapFixDown(int[] A, int i, int n) { // 找到左右孩子 int left = 2 * i + 1; int right = 2 * i + 2 ;
// 左孩子已經越界,i就是葉子節點 if (left>=n) { return ; } // min 指向了左右孩子中較小的那個 int min = left; if (right>=n) { min = left; }else { if (A[right]<A[left]) { min = right; } } // 如果A[i]比兩個孩子都要小,不用調整 if (A[i]<=A[min]) { return ; } // 否則,找到兩個孩子中較小的,和i交換 int temp = A[i]; A[i] = A[min]; A[min] = temp; // 小孩子那個位置的值發生了變化,i變更為小孩子那個位置,遞歸調整 MinHeapFixDown(A, min, n); } private static void swap(int[] A, int p, int bigger) { int temp = A[p]; A[p] = A[bigger]; A[bigger] = temp; } public static void main(String[] args) { int arr[] = new int[10]; for(int i=0;i<10;i++){ arr[i] = (int) ((Math.random()+1)*10); } System.out.println("排序前:"+Arrays.toString(arr)); sort(arr); System.out.println("排序後:"+Arrays.toString(arr)); } }

堆排序結果:

  技術分享圖片

計數排序代碼:

import java.util.Arrays;

/**
 * 計數排序
 * 思路:開辟新的空間,空間大小為max(source)+1
 *         掃描source,將value作為輔助空間的下標,用輔助空間的該位置元素記錄value的個數
 *         如 9 7 5 3 1,helper的空間就為10
 *         依次掃描,value為9,將helper[9]++,以此類推,完成之後,再去遍歷helper
 *         如果該位(index)的值為0,說明index不曾在source中出現
 *         如果該位(index)的值為 1,說明出現了1次,為2說明出現了兩次
 * 時間復雜度:掃描一次source,掃描一次helper,復雜度為N+K
 * 空間復雜度:如果source裏面有個元素較大的,那麽開辟的輔助空間較大
 * 非原址排序
 * 穩定性:相同元素不會出現交叉,非原址都是拷來拷去
 * 如果要優化一下空間,可以求出minOf(source),那麽helper的長度為(max-min)+1,這樣就能短點
 * 計數有缺陷,數據較為密集或範圍較小時,適用。
 */
public class CountSort {

    static void sort(int []source){
        int max = source[0];
        for (int i = 1; i < source.length; i++) {
            if (source[i]>max) {
                max = source[i];
            }
        }
        int []helper = new int[max+1];
        for(int e:source){
            helper[e]++;
        }
        int current = 0;  // 數據回填的位置
        for (int i = 1; i < helper.length; i++) {
            while(helper[i]>0){
                source[current++] = i;
                helper[i]--;
            }
        }
    }
    
    // 保證排序穩定性的版本
    public static void sort2(int[] source) {
        int max = source[0];
        for (int i = 1; i < source.length; i++) {
            if (source[i]>max) {
                max = source[i];
            }
        }
        int []helper = new int[max+1];
        for (int e : source) {
          helper[e]++;
        }
        for (int i = 1; i < helper.length; i++) {
          helper[i] += helper[i - 1];
        }
        int len = source.length;
        int[] target = new int[len];
        for (int i = len - 1; i >= 0; i--) {
          target[helper[source[i]] - 1] = source[i];
          helper[source[i]]--;
        }
        System.arraycopy(target, 0, source, 0, len);
      }
    
    public static void main(String[] args) {
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }
        System.out.println("排序前:"+Arrays.toString(arr));
        sort2(arr);
        System.out.println("排序後:"+Arrays.toString(arr));
    }

}

計數排序結果:  

  技術分享圖片

Java實現堆排序和計數排序