Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[pull] main from itcharge:main #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
pull merged 13 commits into AlgorithmAndLeetCode:main from itcharge:main
Aug 18, 2022
Merged
Changes from 1 commit
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
c0a1d18
Update 02.Array-Selection-Sort.md
itcharge Aug 17, 2022
7a2e11f
Update 03.Array-Insertion-Sort.md
itcharge Aug 17, 2022
0215d09
Update 04.Array-Shell-Sort.md
itcharge Aug 17, 2022
615cf10
Update 06.Array-Quick-Sort.md
itcharge Aug 17, 2022
308148a
Merge branch 'main' of https://github.com/itcharge/LeetCode-Py
itcharge Aug 18, 2022
8ac0095
Update 09.Array-Bucket-Sort.md
itcharge Aug 18, 2022
f08b806
Update 09.Array-Bucket-Sort.md
itcharge Aug 18, 2022
af0b03c
Update 10.Array-Radix-Sort.md
itcharge Aug 18, 2022
c33adf0
Update 07.Array-Heap-Sort.md
itcharge Aug 18, 2022
5d4d642
更新题解列表
itcharge Aug 18, 2022
01a67f9
Update 06.Array-Quick-Sort.md
itcharge Aug 18, 2022
e78071c
Update 10.Array-Radix-Sort.md
itcharge Aug 18, 2022
4f75179
更新代码模板
itcharge Aug 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update 07.Array-Heap-Sort.md
  • Loading branch information
itcharge committed Aug 18, 2022
commit c33adf059b92e931e089b93f64f2322de4b2b69a
98 changes: 60 additions & 38 deletions Contents/01.Array/02.Array-Sort/07.Array-Heap-Sort.md
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,71 +1,93 @@
## 1. 堆排序算法思想

> 堆排序(Heap sort)基本思想:
> **堆排序(Heap sort)基本思想**:
>
> 借用「堆结构」所设计的排序算法。将数组转化为大顶堆,重复从大顶堆中取出数值最大的节点,并让剩余的堆维持大顶堆性质
> 借用「堆结构」所设计的排序算法。将数组转化为大顶堆,重复从大顶堆中取出数值最大的节点,并让剩余的堆结构继续维持大顶堆性质

### 1.1 堆的定义

:符合以下两个条件之一的完全二叉树:
**堆(Heap)**:符合以下两个条件之一的完全二叉树:

- 大顶堆:根节点值 ≥ 子节点值。
- 小顶堆:根节点值 ≤ 子节点值。
- **大顶堆**:根节点值 ≥ 子节点值。
- **小顶堆**:根节点值 ≤ 子节点值。

## 2. 堆排序算法步骤
### 1.2 堆排序算法步骤

- 首先将无序序列构造成第 `1` 个大顶堆(初始堆),使得 `n` 个元素的最大值处于序列的第 `1` 个位置。
- 然后交换序列的第 `1` 个元素(最大值元素)与最后一个元素的位置
- 此后,再把序列的前 `n - 1` 个元素组成的子序列调整成一个新的大顶堆,这样又得到第 `2` 个最大值元素,把子序列的第 `1` 个元素(最大值元素)与第 `n - 1` 个元素交换位置
- 此后再把序列的前 `n - 2` 个元素调整成一个新的大顶堆,......,如此下去,直到整个序列变换成一个有序序列
1. **建立初始堆**:将无序序列构造成第 `1` 个大顶堆(初始堆),使得 `n` 个元素的最大值处于序列的第 `1` 个位置。
2. **调整堆**:交换序列的第 `1` 个元素(最大值元素)与第 `n` 个元素的位置。将序列前 `n - 1` 个元素组成的子序列调整成一个新的大顶堆,使得 `n - 1` 个元素的最大值处于序列第 `1` 个位置,从而得到第 `2` 个最大值元素
3. **调整堆**:交换子序列的第 `1` 个元素(最大值元素)与第 `n - 1` 个元素的位置。将序列前 `n - 2` 个元素组成的子序列调整成一个新的大顶堆,使得 `n - 2` 个元素的最大值处于序列第 `1` 个位置,从而得到第 `3` 个最大值元素
4. 依次类推,不断交换子序列的第 `1` 个元素(最大值元素)与当前子序列最后一个元素位置,并将其调整成新的大顶堆。直到子序列剩下一个元素时,排序结束。此时整个序列就变成了一个有序序列

可见堆排序算法主要涉及「初始堆建立方法」和「堆调整方法」
从堆排序算法步骤中可以看出:堆排序算法主要涉及「调整堆」和「建立初始堆」两个步骤

### 2.1 堆调整方法
## 2. 调整堆方法

堆调整方法就是:把移走了最大值元素以后的剩余元素组成的序列再构造为一个新的堆积。具体步骤如下:
### 2.1 调整堆方法介绍

- 从根节点开始,自上而下地调整节点的位置,使其成为堆积。即把序号为 `i` 的节点与其左子树节点(序号为 `2 * i`)、右子树节点(序号为 `2 * i + 1`)中值最大的节点交换位置。
- 因为交换了位置,使得当前节点的左右子树原有的堆积特性被破坏。于是,从当前节点的左右子树节点开始,自上而下继续进行类似的调整。
- 如此下去直到整棵完全二叉树成为一个大顶堆。
**调整堆方法**:把移走了最大值元素以后的剩余元素组成的序列再构造为一个新的堆积。具体步骤如下:

### 2.2 初始堆建立方法
1. 从根节点开始,自上而下地调整节点的位置,使其成为堆积。
1. 判断序号为 `i` 的节点与其左子树节点(序号为 `2 * i`)、右子树节点(序号为 `2 * i + 1`)中值关系。
2. 如果序号为 `i` 节点大于等于左右子节点值,则排序结束。
3. 如果序号为 `i` 节点小于左右子节点值,则将序号为 `i` 节点与左右子节点中值最大的节点交换位置。
2. 因为交换了位置,使得当前节点的左右子树原有的堆积特性被破坏。于是,从当前节点的左右子树节点开始,自上而下继续进行类似的调整。
3. 依次类推,直到整棵完全二叉树成为一个大顶堆。

- 如果原始序列对应的完全二叉树(不一定是堆)的深度为 `d`,则从 `d - 1` 层最右侧分支节点(序号为 $\lfloor n/2 \rfloor$)开始,初始时令 $i = \lfloor n/2 \rfloor,ドル调用堆调整算法。
- 每调用一次堆调整算法,执行一次 `i = i - 1`,直到 `i == 1` 时,再调用一次,就把原始序列调整为了一个初始堆。
### 2.2 调整堆方法演示

## 3. 堆排序动画演示
![](http://qcdn.itcharge.cn/images/20211019172530.gif)

### 3.1 堆调整方法演示
1. 交换序列的第 `1` 个元素 `90` 与最后 `1` 个元素 `19` 的位置,此时当前节点为根节点 `19`。
2. 判断根节点 `19`与其左右子节点值,因为 `17 < 19 < 36`,所以将根节点 `19` 与左子节点 `36` 互换位置,此时当前节点为根节点 `19`。
3. 判断当前节点 `36` 与其左右子节点值,因为 `19 < 25 < 26`,所以将当前节点 `19` 与右节点 `26` 互换位置。调整堆结束。

![](http://qcdn.itcharge.cn/images/20211019172530.gif)
## 3. 建立初始堆方法

### 3.1 建立初始堆方法介绍

### 3.2 初始堆建立方法演示
1. 如果原始序列对应的完全二叉树(不一定是堆)的深度为 `d`,则从 `d - 1` 层最右侧分支节点(序号为 $\lfloor \frac{n}{2} \rfloor$)开始,初始时令 $i = \lfloor \frac{n}{2} \rfloor,ドル调用调整堆算法。
2. 每调用一次调整堆算法,执行一次 `i = i - 1`,直到 `i == 1` 时,再调用一次,就把原始序列调整为了一个初始堆。

![](http://qcdn.itcharge.cn/images/20211019172539.gif)
### 3.2 建立初始堆方法演示

### 3.3 堆排序方法演示
![](https://qcdn.itcharge.cn/images/20220818111455.gif)

1. 原始序列为 `[2, 7, 26, 25, 19, 17, 1, 90, 3, 36]`,对应完全二叉树的深度为 `3`。
2. 从第 `2` 层最右侧的分支节点,也就序号为 `5` 的节点开始,调用堆调整算法,使其与子树形成大顶堆。
3. 节点序号减 `1`,对序号为 `4` 的节点,调用堆调整算法,使其与子树形成大顶堆。
4. 节点序号减 `1`,对序号为 `3` 的节点,调用堆调整算法,使其与子树形成大顶堆。
5. 节点序号减 `1`,对序号为 `2` 的节点,调用堆调整算法,使其与子树形成大顶堆。
6. 节点序号减 `1`,对序号为 `1` 的节点,调用堆调整算法,使其与子树形成大顶堆。
7. 此时整个原始序列对应的完全二叉树就成了一个大顶堆,建立初始堆完毕。

## 4. 堆排序方法完整演示

![](http://qcdn.itcharge.cn/images/20211019172547.gif)

## 4. 堆排序算法分析
1. 原始序列为 `[2, 7, 26, 25, 19, 17, 1, 90, 3, 36]`,先根据原始序列建立一个初始堆。
2. 交换序列中第 `1` 个元素(`90`)与第 `10` 个元素(`2`)的位置。将序列前 `9` 个元素组成的子序列调整成一个大顶堆,此时堆顶变为 `36`。
3. 交换序列中第 `1` 个元素(`36`)与第 `9` 个元素(`3`)的位置。将序列前 `8` 个元素组成的子序列调整成一个大顶堆,此时堆顶变为 `26`。
4. 交换序列中第 `1` 个元素(`26`)与第 `8` 个元素(`2`)的位置。将序列前 `7` 个元素组成的子序列调整成一个大顶堆,此时堆顶变为 `25`。
5. 以此类推,不断交换子序列的第 `1` 个元素(最大值元素)与当前子序列最后一个元素位置,并将其调整成新的大顶堆。直到子序列只剩下最后一个元素 `1` 时,排序结束。此时整个序列变成了一个有序序列,即 `[1, 2, 3, 7, 17, 19, 25, 26, 36, 90]`。

## 5. 堆排序算法分析

- 堆积排序的时间主要花费在两个方面:
- 将原始序列构造为一个初始堆积。
- 排序过程中不断将移走最大值元素,然后将剩下元素重新调整为一个新堆积。
- 设原始序列所对应的完全二叉树深度为 d,算法由两个独立的循环组成:
- 在第 `1` 个循环构造初始堆积时,从 `i = d - 1` 层开始,到 `i = 1` 层为止,对每个分支节点都要调用一次 `adjust` 算法,每一次 `adjust` 算法,对于第 `i` 层一个节点到第 `d` 层上建立的子堆积,所有节点可能移动的最大距离为该子堆积根节点移动到最后一层(第 `d` 层) 的距离即 `d - i`。
- 而第 `i` 层上节点最多有 2ドル^{i-1}$ 个,所以每一次 `adjust` 算法最大移动距离为 2ドル^{i-1} * (d-i)$。因此,堆积排序算法的第 `1` 个循环所需时间应该是各层上的节点数与该层上节点可移动的最大距离之积的总和,即:$\sum_{i = d - 1}^1 2^{i-1} (d-i) = \sum_{j = 1}^{d-1} 2^{d-j-1} \times j = \sum_{j = 1}^{d-1} 2^{d-1} \times {j \over 2^j} \le n \sum_{j = 1}^{d-1} {j \over 2^j} < 2n$。这一部分时间花费为 $O(n)$。
- 在算法的第 `2` 个循环中每次调用 `adjust` 算法一次,节点移动的最大距离为这棵完全二叉树的深度 $d = \lfloor log_2(n) \rfloor + 1,ドル一共调用了 `n - 1` 次 `adjust` 算法,所以,第 `2` 个循环的时间花费为 $(n-1)(\lfloor log_2 (n)\rfloor + 1) = O(n log_2 n)$。
- 因此,堆积排序的时间复杂度为 $O(nlog_2 n)$。
- 由于在堆积排序中只需要一个记录大小的辅助空间,因此,堆积排序的空间复杂度为:$O(1)$。
- 堆排序属于 **不稳定排序算法**。堆排序也是一种不适合在链表上实现的排序。
- **时间复杂度**:$O(n \times \log_2 n)$。
- 堆积排序的时间主要花费在两个方面:「建立初始堆」和「调整堆」。
- 设原始序列所对应的完全二叉树深度为 $d,ドル算法由两个独立的循环组成:
1. 在第 1ドル$ 个循环构造初始堆积时,从 $i = d - 1$ 层开始,到 $i = 1$ 层为止,对每个分支节点都要调用一次调整堆算法,而一次调整堆算法,对于第 $i$ 层一个节点到第 $d$ 层上建立的子堆积,所有节点可能移动的最大距离为该子堆积根节点移动到最后一层(第 $d$ 层) 的距离,即 $d - i$。而第 $i$ 层上节点最多有 2ドル^{i-1}$ 个,所以每一次调用调整堆算法的最大移动距离为 2ドル^{i-1} * (d-i)$。因此,堆积排序算法的第 1ドル$ 个循环所需时间应该是各层上的节点数与该层上节点可移动的最大距离之积的总和,即:$\sum_{i = d - 1}^1 2^{i-1} (d-i) = \sum_{j = 1}^{d-1} 2^{d-j-1} \times j = \sum_{j = 1}^{d-1} 2^{d-1} \times {j \over 2^j} \le n \sum_{j = 1}^{d-1} {j \over 2^j} < 2n$。这一部分的时间花费为 $O(n)$。
2. 在第 2ドル$ 个循环中,每次调用调整堆算法一次,节点移动的最大距离为这棵完全二叉树的深度 $d = \lfloor \log_2(n) \rfloor + 1,ドル一共调用了 $n - 1$ 次调整堆算法,所以,第 2ドル$ 个循环的时间花费为 $(n-1)(\lfloor \log_2 (n)\rfloor + 1) = O(n \times \log_2 n)$。
- 因此,堆积排序的时间复杂度为 $O(n \times \log_2 n)$。
- **空间复杂度**:$O(1)$。由于在堆积排序中只需要一个记录大小的辅助空间,因此,堆积排序的空间复杂度为:$O(1)$。
- **排序稳定性**:堆排序是一种 **不稳定排序算法**。

## 5. 堆排序代码实现
## 6. 堆排序代码实现

```Python
class Solution:
# 调整为大顶堆
def heapify(self, arr: [int], index: int, end: int):
# 根节点为 index,左节点为 2 * index + 1, 右节点为 2 * index + 2
left = index * 2 + 1
right = left + 1
while left <= end:
Expand All @@ -87,7 +109,7 @@ class Solution:
# 初始化大顶堆
def buildMaxHeap(self, arr: [int]):
size = len(arr)
# (size-2) // 2 是最后一个非叶节点,叶节点不用调整
# (size - 2) // 2 是最后一个非叶节点,叶节点不用调整
for i in range((size - 2) // 2, -1, -1):
self.heapify(arr, i, size - 1)
return arr
Expand Down

AltStyle によって変換されたページ (->オリジナル) /