手把手教学堆排序
堆排序的一般步骤
- 用数组表示二叉树(完全二叉树,也就是插入的顺序必须是从左到右从上到下),同时进行建堆
- 堆顶的元素与堆最后一个元素交换,断开最后一个的链接,维护剩下的n-1个堆
- 再对剩下的n-1个元素进行步骤2重复到堆里面只有一个元素,或者没有元素为止
Tip 数组表示二叉树,下标为i的父节点是
(i-1)/2
下标为i的左子节点是i*2+1
下标为i的右子节点是i*2+2
建堆的时间复杂度是
O(logN)
所以整个排序的时间复杂度是O(NlogN)
手动模拟建堆的过程
c++代码实现
#include <bits/stdc++.h>
using namespace std;
/*
堆的排序有两个步骤
1. 维护堆(建堆) 时间复杂度是 O(logN)
2. 排序时间复杂度是 O(N)
二叉树性质: 要满足完全二叉树的特点,插入元素要从上至下,从左至右
我们这里用数组表示二叉树
用数组表示二叉树的话
下标为 i 的父节点就是 (i-1)/2
下标为 i 的左子节点是 i*2+1
下标为 i 的右子节点是 i*2+2
*/
void heapify(vector<int>& Tree, int n,
int i) { // Tree就是二叉树 n就是二叉树元素的个数 i父节点的下标
int largest = i; // 父节点应该是最大值的下标
int child_left = i * 2 + 1;
int child_right = i * 2 + 2;
if (child_left < n && Tree[child_left] > Tree[largest]) {
swap(child_left, largest);
}
if (child_right < n && Tree[child_right] > Tree[largest]) {
swap(child_right, largest);
}
if (largest != i) { // 需要进行维护
swap(Tree[i], Tree[largest]);
heapify(Tree, n, largest);
}
}
void heapsort(vector<int>& Tree) {
// 第一步建堆
int n = Tree.size();
// 从最下面进行开始建堆
for (int i = (n - 1) / 2; i >= 0; i--) {
heapify(Tree, n, i);
}
cout << "建堆" << endl;
for (auto val : Tree) {
cout << val << " ";
}
cout << "建堆结束" << endl;
for (int i = n - 1; i >= 0; i--) {
// 拿出最后一个数字
swap(Tree[0], Tree[i]);
heapify(Tree, i, 0);
}
cout << "堆排序的结果" << endl;
for (auto val : Tree) {
cout << val << " ";
}
cout << endl;
}
int main() {
vector<int> data = {5, 100, 31, 15, -35, 71, 63, 113};
heapsort(data);
}