Java之堆排序,java堆排序

來源:互聯網
上載者:User

Java之堆排序,java堆排序

堆是資料結構中的一種重要結構,瞭解“堆”的概念和操作,可以協助我們快速地掌握堆排序。

堆的概念

堆是一種特殊的完全二叉樹(complete binary tree)。如果一棵完全二叉樹的所有節點的值都不小於其子節點,稱之為大根堆(或大頂堆);所有節點的值都不大於其子節點,稱之為小根堆(或小頂堆)。

在數組(在0號下標儲存根節點)中,容易得到下面的式子(這兩個式子很重要):

1.下標為i的節點,父節點座標為(i-1)/2;

2.下標為i的節點,左子節點座標為2*i+1,右子節點為2*i+2。

堆的建立和維護

堆可以支援多種操作,但現在我們關心的只有兩個問題:

1.給定一個無序數組,如何建立為堆?

2.刪除堆頂元素後,如何調整數組成為新堆?

先看第二個問題。假定我們已經有一個現成的大根堆。現在我們刪除了根項目,但並沒有移動別的元素。想想發生了什麼:根項目空了,但其它元素還保持著堆的性質。我們可以把最後一個元素(代號A)移動到根項目的位置。如果不是特殊情況,則堆的性質被破壞。但這僅僅是由於A小於其某個子項目。於是,我們可以把A和這個子項目調換位置。如果A大於其所有子項目,則堆調整好了;否則,重複上述過程,A元素在樹形結構中不斷“下沉”,直到合適的位置,數組重新恢複堆的性質。上述過程一般稱為“篩選”,方向顯然是自上而下。

刪除一個元素是如此,插入一個新元素也是如此。不同的是,我們把新元素放在末尾,然後和其父節點做比較,即自下而上篩選。

那麼,第一個問題怎麼解決呢?

我看過的資料結構的書很多都是從第一個非葉子結點向下篩選,直到根項目篩選完畢。這個方法叫“篩選法”,需要迴圈篩選n/2個元素。

但我們還可以借鑒“無中生有”的思路。我們可以視第一個元素為一個堆,然後不斷向其中添加新元素。這個方法叫做“插入法”,需要迴圈插入(n-1)個元素。

由於篩選法和插入法的方式不同,所以,相同的資料,它們建立的堆一般不同。

大致瞭解堆之後,堆排序就是水到渠成的事情了。

演算法概述/思路

我們需要一個升序的序列,怎麼辦呢?我們可以建立一個最小堆,然後每次輸出根項目。但是,這個方法需要額外的空間(否則將造成大量的元素移動,其複雜度會飆升到O(n^2))。如果我們需要就地排序(即不允許有O(n)空間複雜度),怎麼辦?
有辦法。我們可以建立最大堆,然後我們倒著輸出,在最後一個位置輸出最大值,次末位置輸出次大值……由於每次輸出的最大元素會騰出第一個空間,因此,我們恰好可以放置這樣的元素而不需要額外空間。很漂亮的想法,是不是?

 1 public class HeapSort  2 { 3     public static void main(String[] args)  4     { 5         int[] arr = { 50, 10, 90, 30, 70, 40, 80, 60, 20 }; 6         System.out.println("排序之前:");  7         for (int i = 0; i < arr.length; i++)  8             System.out.print(arr[i] + " ");  9         10         // 堆排序11         heapSort(arr); 12         System.out.println(); 13         System.out.println("排序之後:"); 14         for (int i = 0; i < arr.length; i++) 15             System.out.print(arr[i] + " ");16     }17     18     /** 19    * 堆排序 20    */21     private static void heapSort(int[] arr) 22     {23         // 將待排序的序列構建成一個大頂堆 24         for (int i = arr.length / 2; i >= 0; i--)25             heapAdjust(arr, i, arr.length);26         27         // 逐步將每個最大值的根節點與末尾元素交換,並且再調整二叉樹,使其成為大頂堆 28         for (int i = arr.length - 1; i > 0; i--)29         {30             swap(arr, 0, i); // 將堆頂記錄和當前未經排序子序列的最後一個記錄交換 31             heapAdjust(arr, 0, i); // 交換之後,需要重新檢查堆是否符合大頂堆,不符合則要調整32         }33     }34     /** 35    * 構建堆的過程 36    * @param arr 需要排序的數組 37    * @param i 需要構建堆的根節點的序號 38    * @param n 數組的長度 39    */40     private static void heapAdjust(int[] arr, int i, int n) 41     {42         int child;43         int father;44         for (father = arr[i]; leftChild(i) < n; i = child)45         {46             child = leftChild(i);47             // 如果左子樹小於右子樹,則需要比較右子樹和父節點 48             if (child != n - 1 && arr[child] < arr[child + 1])49                 child++; // 序號增1,指向右子樹50             // 如果父節點小於孩子結點,則需要交換 51             if (father < arr[child]) 52                 arr[i] = arr[child];53             else54                 break; // 大頂堆結構未被破壞,不需要調整55         }56         arr[i] = father;57     }58     59     // 擷取到左孩子結點 60     private static int leftChild(int i) 61     {62         return 2 * i + 1;63     }64     65     // 交換元素位置 66     private static void swap(int[] arr, int index1, int index2) 67     {68         int tmp = arr[index1];69         arr[index1] = arr[index2];70         arr[index2] = tmp;71     }72 }

 

作者:耑新新,發佈於  部落格園

轉載請註明出處,歡迎郵件交流:zhuanxinxin@foxmail.com

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.