【算法与数据结构】堆排序是什么鬼?
排序算法相必大家都見過很多種,例如快速排序、歸并排序、冒泡排序等等。今天,我們就來簡(jiǎn)單講講堆排序。
在上一篇中,我們講解了二叉堆,今天的堆排序算法主要就是依賴于二叉堆來完成的,不清楚二叉堆是什么鬼的,可以看下:
【算法與數(shù)據(jù)結(jié)構(gòu)】二叉堆是什么鬼?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用輔助數(shù)組來實(shí)現(xiàn)堆排序算法
?
假如給你一個(gè)二叉堆,根據(jù)二叉堆的特性,你會(huì)怎么使用二叉堆來實(shí)現(xiàn)堆排序呢?
我們都知道,二叉堆有一個(gè)很特殊的節(jié)點(diǎn) —-?堆頂,堆頂要嘛是所有節(jié)點(diǎn)的最大元素,要嘛是最小元素,這主要取決于這個(gè)二叉堆是最小堆還是最大堆。
今天,我們暫且選擇以最小堆來作為例子。
基于堆頂這個(gè)特點(diǎn),我們就可以來實(shí)現(xiàn)我們的堆排序了。
大家看下面一個(gè)例子:
對(duì)于一個(gè)如圖有10個(gè)節(jié)點(diǎn)元素的二叉堆:
我們把堆頂這個(gè)節(jié)點(diǎn)刪除,然后把刪除的節(jié)點(diǎn)放在一個(gè)輔助數(shù)組help里(黑色的節(jié)點(diǎn)表示已經(jīng)被刪除沒用的點(diǎn))。
顯然,這個(gè)被刪除的節(jié)點(diǎn),是堆中值最小的節(jié)點(diǎn)。接下來,我們繼續(xù)刪除二叉堆的堆頂,然后把刪除的元素還是存放在help數(shù)組里。
顯然,第二次刪除的節(jié)點(diǎn),是原始二叉堆中的第二小節(jié)點(diǎn)。繼續(xù)重復(fù)刪除堆頂。
繼續(xù)連續(xù)6次刪除堆頂,把刪除的節(jié)點(diǎn)一次放入help數(shù)組。
二叉堆中只剩最后一個(gè)節(jié)點(diǎn)了,這個(gè)節(jié)點(diǎn)同時(shí)也是原始二叉堆中的最大節(jié)點(diǎn),把這個(gè)節(jié)點(diǎn)繼續(xù)刪除了,還是放入help數(shù)組里。
此時(shí),二叉堆的元素被刪除光了,觀察一下help數(shù)組。這是一個(gè)有序的數(shù)組,實(shí)際上,通過從二叉堆的堆頂逐個(gè)取出最小值,存放在另一個(gè)輔助的數(shù)組里,當(dāng)二叉堆被取光之時(shí),我們就完成了一次堆排序了。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 其實(shí)無需輔助數(shù)組
?
在上面的堆排序過程中,我們使用了一個(gè)輔助數(shù)組help。可事實(shí)上,我們真的需要輔助數(shù)組嗎?
上篇講二叉堆的時(shí)候,我們說過。二叉堆在實(shí)現(xiàn)的時(shí)候,是采取數(shù)組的形式來存儲(chǔ)的。
從二叉堆中刪除一個(gè)元素,為了充分利用空間,其實(shí)我們是可以把刪除的元素直接存放在二叉堆的最后一個(gè)元素那里的。例如:
刪除堆頂,把刪除的元素放在最后一個(gè)元素。
節(jié)點(diǎn),把刪除的元素放在最后第二個(gè)位置
繼續(xù)刪除
以此類推….
這樣,對(duì)于一個(gè)含有n個(gè)元素的二叉堆,經(jīng)過n-1(不用刪除n次)次刪除之后,這個(gè)數(shù)組就是一個(gè)有序數(shù)組了。
所以,給你一個(gè)無序的數(shù)組,我們需要把這個(gè)數(shù)組構(gòu)建成二叉堆,然后在通過堆頂逐個(gè)刪除的方式來實(shí)現(xiàn)堆排序。
其實(shí),也不算是刪除了,相當(dāng)于是把堆頂?shù)脑嘏c堆尾部在交換位置,然后在通過下沉的方式,把二叉樹恢復(fù)成二叉堆。
代碼如下:
?
對(duì)于堆的時(shí)間復(fù)雜度,我就直接給出了,有興趣的可以自己推理下,還是不難的。堆的時(shí)間復(fù)雜度是 O (nlogn)。空間復(fù)雜度是 O(1)。
這里可能大家會(huì)問,堆排序的時(shí)間復(fù)雜度是O (nlogn),像快速排序,歸并排序的時(shí)間復(fù)雜度也是 O(nlogn),那我在使用的時(shí)候該如何選擇呢?
這里說明一下:快速排序是平均復(fù)雜度 O(logn),實(shí)際上,快速排序的最壞時(shí)間復(fù)雜度是O(n^2。),而像歸并排序,堆排序,都穩(wěn)定在O(nlogn)
我給出一個(gè)問題,例如給你一個(gè)擁有n個(gè)元素的無序數(shù)組,要你找出第 k 個(gè)大的數(shù),那么你會(huì)選擇哪種排序呢?
顯然在這個(gè)問題中,選用堆排序是最好的,我們不用把數(shù)組全部排序,只需要排序到前k個(gè)數(shù)就可以了。至于代碼如何實(shí)現(xiàn),這個(gè)我就不給代碼了,大家可以動(dòng)手敲一敲。
public class HeapSort {/**下沉操作,執(zhí)行刪除操作相當(dāng)于把最后一個(gè)元素賦給根元素之后,然后對(duì)根元素執(zhí)行下沉操作*@param arr*@param parent 要下沉元素的下標(biāo)*@param length 數(shù)組長(zhǎng)度*/public static int[] downAdjust(int[] arr, int parent, int length) {//臨時(shí)保證要下沉的元素int temp = arr[parent];//定位左孩子節(jié)點(diǎn)位置int child = 2 * parent + 1;//開始下沉while (child < length) {//如果右孩子節(jié)點(diǎn)比左孩子小,則定位到右孩子if (child + 1 < length && arr[child] > arr[child + 1]) {child++;}//如果父節(jié)點(diǎn)比孩子節(jié)點(diǎn)小或等于,則下沉結(jié)束if (temp <= arr[child]) break;//單向賦值arr[parent] = arr[child];parent = child;child = 2 * parent + 1;}arr[parent] = temp;return arr;}//堆排序public static int[] heapSort(int[] arr, int length) {//構(gòu)建二叉堆for (int i = (length - 2) / 2; i >= 0; i--) {arr = downAdjust(arr, i, length);}//進(jìn)行堆排序for (int i = length - 1; i >= 1; i--) {//把堆頂?shù)脑嘏c最后一個(gè)元素交換int temp = arr[i];arr[i] = arr[0];arr[0] = temp;//下沉調(diào)整arr = downAdjust(arr, 0, i);}return arr;}//測(cè)試public static void main(String[] args) {int[] arr = new int[]{1, 3, 5, 2, 0, 10, 6};System.out.println(Arrays.toString(arr));arr = heapSort(arr, arr.length);System.out.println(Arrays.toString(arr));} }參考鏈接:https://mp.weixin.qq.com/s?__biz=MzUxNzg0MDc1Mg==&mid=2247484222&idx=1&sn=2a3d3f0e95df9d1fdcc2e21123721e66&chksm=f9934921cee4c037fe37eb9a27454f095376117985187867b09bd95b564e4941956ead73ed93&scene=21#wechat_redirect
總結(jié)
以上是生活随笔為你收集整理的【算法与数据结构】堆排序是什么鬼?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉堆是什么?
- 下一篇: 快速使用Tensorflow读取7万数据