堆排序相對來說比較複雜。這裏使用了兩種是實現方案,方案1,heapSort1()需要藉助於二叉堆類,這個算是一個入門;有助於理解堆排序。 方案2,heapSort2()將數組看成是一棵二叉樹,首先要對樹轉換,使其變成大頂堆。然後再進行排序。 這裏需要注意 1.數組索引從0開始。所以計算元素索引的時候要轉換,而且要注意越界問題。 2.大頂堆和堆排序都只用了下沉操作; 2.1在構建大頂堆的時候,要從最後一棵子樹開始構建子二叉堆。 2.2在構建二叉堆的時候,如果樹的高度大於1的時候,要比較樹的子節點,以便讓元素放在合適的位置上。 假設高度爲h的子樹均已完成二叉堆化,那麼對於高度爲h+1的子樹,把其根節點沿着最大子節點的分枝做調整,最多需要h步完成二叉堆化。 還是親自寫一下,然後在本子上自己畫一下,會比較深刻。否則很難很好的理解這個過程。 /** * @auther 王輝 * @create 2020-04-15 20:47 * @Description */ import java.lang.reflect.Array; import java.util.Arrays; /** * 描述: * 堆排序 * 首先構建一個二叉堆。 * 二叉堆是完全二元樹(二叉樹)或者是近似完全二元樹(二叉樹)。 * <p> * 二叉樹的一些性質: * 位置爲K的節點的父節點爲K/2,而它的子節點的位置爲2k(左節點)和2k+1(右節點) * <p> * 二叉堆有兩種:最大堆和最小堆。 * 最大堆:父結點的鍵值總是大於或等於任何一個子節點的鍵值;----升序排序的時候用大頂堆 * 最小堆:父結點的鍵值總是小於或等於任何一個子節點的鍵值。----降序排序的時候用小頂堆 * <p> * 此處實現的是大頂堆 * * @author wanghui email:[email protected] * @create 2020-04-15 上午10:54 */ public class MaxPQ<Key extends Comparable<Key>> { /** * 通過數組實現二叉樹 */ private Key[] pq; /** * 存儲在pq中的元素個數。0沒有使用。 */ private int N = 0; public MaxPQ(int maxN) { this.pq = (Key[]) new Comparable[maxN + 1]; } /** * 判斷二叉樹是否爲空 * * @return */ public boolean isEmpty() { return N == 0; } /** * 後去二叉樹大小 * * @return */ public int size() { return N; } /** * 插入二叉樹,從尾部插入,需要根據插入元素跟父節點元素的大小,做上浮操作。 * 當v大於父節點時,上浮 * 插入元素比較次數不超過lgN+1 * * @param v */ public void insert(Key v) { pq[++N] = v; swim(N); } /** * 獲取並移除最大元素 * 移除最大元素後,將二叉樹最後一個節點的元素設置爲樹的根節點;此時,樹的根節點小於其子節點,所以要做下沉操作 * 刪除最大元素操作不超過2lgN次比較 * * @return */ public Key delMax() { if (N == 1) { Key max = pq[1]; pq[N--] = null; return max; } else if (N > 1) { Key max = pq[1]; exch(1, N--); pq[N + 1] = null; sink(1); return max; } else { return null; } } /** * 元素大小比較 * * @param i * @param j * @return */ private boolean less(int i, int j) { return pq[i].compareTo(pq[j]) < 0; } /** * 元素位置交換 * * @param i * @param j */ private void exch(int i, int j) { Key t = pq[i]; pq[i] = pq[j]; pq[j] = t; } /** * 上浮操作,自下而上的調整元素順序,使其符合二叉堆的條件 * * @param k */ private void swim(int k) { while (k > 1 && less(k / 2, k)) { exch(k / 2, k); k /= 2; } } /** * 下沉操作,自上而下的調整元素順序,使其符合二叉堆的條件 * * @param k */ 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; } } /** * 堆排序實現1 * 1、調用insert(),將數組變成二叉堆(堆有序操作) * 2、下沉排序 * * @param a */ public void heapSort1(Comparable[] a) { int temp = a.length; MaxPQ aa = new MaxPQ(temp); for (int i = 0; i < temp; i++) { aa.insert(a[i]); } System.out.println("堆有序:" + Arrays.toString(aa.pq)); for (int j = temp - 1; j >= 0; j--) { a[j] = aa.delMax(); } System.out.println("排序後:" + Arrays.toString(a)); } /** * 堆排序實現2 * 1、構建二叉堆,從右至左的通過下沉操作構建二叉堆 * * @param a */ public void heapSort2(Comparable[] a) { int n = a.length; //step1、子二叉堆下沉操作實現大頂堆 for (int k = n / 2; k >= 1; k--) { sink(a, k, n); } System.out.println("堆有序:" + Arrays.toString(a)); //step2、堆排序 while (n > 1) { exch(a, 1, n--); sink(a, 1, n); } System.out.println("排序後:" + Arrays.toString(a)); } /** * 子二叉堆下沉操作,交換時,要跟子節點中小的一方交換 * 選出的子二叉樹在變換成子二叉堆的時候, * 子節點大的跟父節點比較,如果大於父節點,則替換 * @param a 數組a內部實現下沉 * @param i 下沉開始節點 * @param j 下沉結束節點 */ private void sink(Comparable[] a, int i, int j) { int v = i - 1; Comparable rootNode = a[v]; for (int k = 2 * v + 1; k < j; k = 2 * k + 1) { //右子節點大於左子節點 if(k+1 < j && a[k].compareTo(a[k+1])<0) { k++; } //父節點小於子節點中最大的 if(rootNode.compareTo(a[k]) < 0) { a[v] = a[k]; //將索引指向下一個子二叉樹的父節點 v = k; }else { break; } } a[v] = rootNode; } /** * 元素交換 * * @param a 數組a內部實現元素交換 * @param i 需要交換元素1 * @param j 需要交換元素1 */ private void exch(Comparable[] a, int i, int j) { Comparable temp = a[i - 1]; a[i - 1] = a[j - 1]; a[j - 1] = temp; } public static void main(String[] args) { String[] a = {"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"}; System.out.println("排序前:" + Arrays.toString(a)); int temp = a.length; new MaxPQ(temp).heapSort2(a); } }
常用的排序算法實現(2)-堆排序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.