---
# System prepended metadata

title: 堆積排序(Heap Sort)
tags: [' LeetCode', 演算法]

---

---
tags: 演算法, LeetCode
disqus: HackMD
---

# 堆積排序(Heap Sort)

在提到堆積排序前我們先講講樹的資料結構，因為在堆積排序中會使用到某一種樹。

## 樹-資料結構

那樹是甚麼

* 抽象資料類型
* 被用來模擬分層資料結構
* 無循環`graph`

在樹當中只會有一個`root value`，也就是樹根，看到下面那張圖`7,5,6,9`都有`Subtree`(子樹)，每個圈圈我們稱它為`node`(節點)，箭頭稱為`edge`，以圖片藍色圈起來的子樹為例，`7`就是`2, 10, 6`的`parent node`，而`2, 10, 6`就是`7`的`children node`。

![](https://i.imgur.com/jFi8U83.jpg)

### 二元樹(Binary Tree)

在樹的資料結構中有許多不同的樹，最常見的樹為二元樹。

二元樹中也有不同種類

![](https://i.imgur.com/8WIe4sn.png)
(Types of Binary Tree || Designed by [Anand K Parmar](https://medium.com/@anandkparmar))

#### 1. Full Binary Tree

`Full Binary Tree` 每個節點都有 0 或 2 個子節點

![](https://i.imgur.com/wUO8dtZ.png)


#### 2. Complete Binary Tree

`Complete Binary Tree` 除了最後一層外，所有層節點都是滿的，在最後一層中，節點盡可能位於左側

![](https://i.imgur.com/wdPAjQe.png)


#### 3. Degenerate(or Pathological) Binary Tree

`Degenerate(or Pathological) Binary Tree` 每個父節點只會有一個子節點

#### 4. Perfect Binary Tree

`Perfect Binary Tree` 內部節點都有兩個子節點，並且左右都處於相同深度

#### 5. Balanced Binary Tree

`Balanced Binary Tree` 每個節點的左右子樹的高度最多相差1的二元樹

### Max Heap

`max heap` 就是這次排序中會使用到的資料結構

* 是一個 `Complete Binary Tree`
* `SubTree` 的 `root` 一定會是最大

![](https://i.imgur.com/FiiYsVp.png)
(圖片來自[Binary heap](https://en.wikipedia.org/wiki/Binary_heap))

## 堆積排序介紹

先將數組轉換成`Complete Binary Tree`資料結構，再換成`Max Heap`，這時`root`值將會是最大值，將`root`值移出去，再繼續將樹轉換成`Max Heap`，依此類推，這就是堆積排序。

![](https://i.imgur.com/3dMu9NP.gif)
([圖片來源](https://commons.wikimedia.org/wiki/File:Heapsort-example.gif))

## 虛擬碼

首先是建立`Max Heap` 的`pseudo code`

> heapSize 和 arr 為全域變數，因為在多個function中使用

虛擬碼中，`heapSize` 的值為數組 `arr` 的長度减 `1`。然後，有一個迴圈從 `heapSize / 2` 開始，遍歷所有的非葉子節點，至第 `0` 個節點。

每個非葉子節點會被傳遞給 `MAX-HEAPIFY` 函數。`MAX-HEAPIFY` 函數的作用是使其成為`max heap`，使得父節點的鍵值都大於等於其子節點的鍵值。

經過這段虛擬碼的執行後，數組 `arr` 會被轉化成一個`max heap`。

```pseudoCode=
BUILD-MAX-HEAP():
    heapSize = arr.length - 1
    for i from (heapSize / 2) to 0:
        arr = MAX-HEAPIFY(i)
```

這個虛擬碼做的事是在給定的節點`i`的子樹中，找到最大的節點並使之成為`i`的父節點。

> 最多走 $log2^n$層，這邊的`n`為`node`數量，假設有`1024`個`node`代表樹為`10`層

以下是該虛擬碼的詳細說明：

1. `2，3`行計算`i`節點的左右子節點在陣列中的位置。

3. `4，5`行
    如果左子節點存在且大於根節點，那麼將左子節點設為最大值。

3. `8，9`行。
    如果右子節點存在且大於當前的最大值，那麼將右子節點設為最大值。

1. `10~12`行。如果最大值不是根節點，則交換根節點和最大值並遞迴調用`MAX-HEAPIFY(largest`)保持`max heap`的性質。

這個 `MAX-HEAPIFY` 作用是將某一個節點下面的子樹調整成最大堆，並且讓根節點是最大值。

```pseudoCode=
// BigO O(logn)
MAX-HEAPIFY(i):
    l = 2 * i + 1
    r = 2 * i + 2
    if l <= heapSize and arr[l] > arr[i]:	
        largest = l
    else:
        largest = i
    if r <= heapSize and arr[r] > arr[largest]:	
        largest = r
    if largest is not i:	
        swap arr[i] with arr[largest]	
        MAX-HEAPIFY(largest)
```

我們舉例`arr = [8, 6, 2, 4, 5, 3, 7]`，`heapSize` 就會等於 `6`，接著下來跑迴圈先給予`i=3`但`arr[3]`底下沒有節點，所以舉`i=2`為例子，丟進`MAX-HEAPIFY`，接下來往下看

假設`i` 為 `2`，這時候 `l = 5`, `r = 6`，將虛擬碼帶入數字

![](https://i.imgur.com/b5BAlwB.png)


這邊可以看到找到最大值的`index`等於`6`，所以我們跟`i`位置的值交換位置，那他會從`largest`的位置再往下查看是否為最大值，如果不是就會再持續交換

![](https://i.imgur.com/1CTnwjt.jpg)

再來才是本篇重點`Heap sort`的`pseudo code`，一開始創建`max heap`後將最大值與陣列最後一個值互換，這樣就能確保最後一個值為最大值，依照此步驟，依此類推，就可以完成陣列的排序。

```pseudoCode=
HEAP-SORT():
    BUILD-MAX-HEAP(arr)
    for i from arr.length - 1 to 0:
        exchangeArr[0] with arr[i]
        heapSize = heapSize - 1
        MAX-HEAPIFY(0)
```

如下圖步驟循環

1. 將資料建立成一個`max heap`。
1. 將根節點`(root)`與最後一個節點交換，並將最後一個節點刪除。
1. 重新調整`heap`，使其成為一個`max heap`。
1. 重複步驟 `2` 和 `3`，直到`heap`中只剩下一個節點。

![](https://i.imgur.com/Don7rWc.jpg)

## 複雜度

### 時間複雜度為 
$O(n log n)$

### 空間複雜度為 
$O(1)$

## `Heap Sort`程式碼

```javascript=
class HeapSort{
    constructor(arr){
        this.arr = arr;
        this.heapSize = arr.length;
    }

    buildMaxHeap(){
        this.heapSize = this.heapSize - 1;
        for(let i = Math.floor(this.heapSize/2); i >= 0; i--){
            this.maxHeapify(i);
        }
    }

    maxHeapify(i){
        let left = 2*i + 1;
        let right = 2*i + 2;
        let largest;

        if(left <= this.heapSize && this.arr[left] > this.arr[i]){
            largest = left;
        } else {
            largest = i;
        }

        if(right <= this.heapSize && this.arr[right] > this.arr[largest]){
            largest = right;
        }

        if(largest != i){
            let temp = this.arr[i];
            this.arr[i] = this.arr[largest];
            this.arr[largest] = temp;
            this.maxHeapify(largest);
        }
    }

    sort(){
        this.buildMaxHeap();
        for(let i = this.arr.length-1; i >= 0; i--){
            let temp = this.arr[0];
            this.arr[0] = this.arr[i];
            this.arr[i] = temp;
            this.heapSize--;
            this.maxHeapify(0);
        }
    }
}

let arr = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7];
let heap = new HeapSort(arr);
heap.sort();

console.log(heap.arr); // [1, 2,  3,  4,  7, 8, 9, 10, 14, 16]
```

## LeetCode實戰

[912. Sort an Array](https://leetcode.com/problems/sort-an-array)

給你一個整數陣列`nums`，請將陣列以升序排列並返回。您必須在`O(nlog(n))`時間複雜度內解決此問題，並使用較小的空間複雜度。

![](https://i.imgur.com/eGtDdkZ.jpg)


[☑解法詳解](https://leetcode.com/problems/sort-an-array/solutions/3059335/javascript-heap-sort-solution-best-77/)

### code:tada:

```javascript=
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    let heapSize;

    function maxHeapify(i){
        let left = 2*i + 1;
        let right = 2*i + 2;
        let largest;

        if(left <= heapSize && nums[left] > nums[i]){
            largest = left;
        } else {
            largest = i;
        }

        if(right <= heapSize && nums[right] > nums[largest]){
            largest = right;
        }

        if(largest != i){
            let temp = nums[i];
            nums[i] = nums[largest];
            nums[largest] = temp;
            maxHeapify(largest);
        }
    }

    function buildMaxHeap(){
        heapSize = nums.length - 1;
        for(let i = Math.floor(heapSize/2); i >= 0; i--){
            maxHeapify(i);
        }
    }

    buildMaxHeap();
    for(let i = nums.length-1; i >= 0; i--){
        let temp = nums[0];
        nums[0] = nums[i];
        nums[i] = temp;
        heapSize--;
        maxHeapify(0);
    }
    return nums
};
```

### Submissions

![](https://i.imgur.com/IQgDYbN.jpg)


## 來源

[資料結構與演算法 (JavaScript)](https://www.udemy.com/course/algorithm-data-structure/)

> [name=@joe94113] [time=Tue, JAN 15, 2023 10:00 PM] [color=#907bf7]
