算法导论 第六章 堆排序
Q : 堆排序和插入排序的共同点 , 和归并排序的共同点 ?
空间原址性 : 任何时候都只需要常数个额外的元素空间存储
时间复杂度 : O(nlogn)
6.1 堆
堆是一个数组
堆有两个属性 , A.length表示元素个数 , A.heap-size表示有多少个堆元素存储 , 一个是容量 , 一个是存储个数
树的根节点是1
二叉堆分为两种形式 , 最大堆和最小堆
最大堆中 , 除了根节点外 , \(A[i] \le A[parent(i)]\)
最小堆中 , 除了根节点外 , \(A[i] \ge A[parent(i)]\)
考虑堆的相关操作 :
- MAX-HEAPIFY : O(lgn)
- BUILD-MAX-HEAP : O(n) , 无序输入数据中构造一个堆
- HEAPSORT : O(nlgn) , 对一个数组进行原址排序
- MAX-HEAP-INSERT , HEAP-EXTRACT-MAX , HEAP-INCREASE-KEY , HEAP-MAXIMUN : O(lgn) , 用堆实现一个优先队列
6.1练习答案
- 最少是\(\frac{1 + h-1 }{2} \times (h-1) + 1\) , 最多是\(\frac{1+h}{2} \times h\)
- 设有\(2^k \le n \lt 2^k+1\) , 取log之后显然了
- 略
- 最底层
- yes
- No
- 非常神奇的解法 . 考虑\(n/2 + 1\)节点的左儿子:
原式 \(\gt (2 \times \frac{n}{2} - 1) + 2 = n\)
因此没有左儿子 , 所以为叶子节点
这意味着堆中叶子节点的个数为\(n/2\)上取整
6.2 维护堆的性质
MAX-HEAPIFY(A,i)
l = LEFT(i)
r = RIGHT(i)
largest = i
if l <= A.heap-size and A[l] > A[i]
largest = l
if r <= A.heap-size and A[r] > A[largest]
largest = r
if largest != i
exchange A[i]] with A[largest]
MAX-HEAPIFY(A,largest)
$T(n) <= T(2n/3) + O(1) $
为什么是 \(2n/3\)呢 ?
考虑最底层一半满且选择左子树的情况 , 高度为n:
\(\frac{2^{n-1}-1}{2^n-1 - 2^{n-2}} = \frac{2\times 2^{n-2}-1}{3\times 2^{n-1}}\)
由主方法得知 , 复杂度为\(O(lgn)\)
6.3 建堆
BUILD-MAX-HEAP(A)
A.heap-size = A.length
for i = A.length/2 downto 1
MAX-HEAPIFY(A,i)
为什么该函数是正确的 ?
初始化:初始时 , 显然\(n/2+1\) , \(n/2+2\)等都是叶节点
保持 : 枚举的每个\(i\) , 都有\(2 \times i\)和\(2 \times i + 1\)已经为最大根 , 然后调用了MAX-HEAPIFT
终止 : i = 0时 , 显然有每个节点1,2,.. n都是最大堆的根
6.4 堆排序算法
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i = A.length downto 2
//此处可以输出A[1]
exchange A[1] with A[i]
A.heap-size = A.heap-size-1
MAX-HEAPIFY(A,1)
6.5 优先队列
优先队列是基于堆排序的数据结构 , 包括 :
INSERT(S,x) 插入x
MAXIMUM(S) 返回最大元素
EXTRACT-MAX(S) 删去最大的
INCREASE-KEY(S,x,k) 将x的关键字值加到k
HEAP-MAXIMUM(A)
return A[1]
HEAP-EXTRACT-MAX(A)
if A.heap-size < 1
error "heap underflow"
max = A[1]
A[1]= A.heap-size-1
MAX-HEAPIFY(A,1)
return max
HEAP-INCREASE-KEY(A,i,key)
if key < A[i]
error "new key is smaller than current key"
A[i] = key
while i > 1 and a[parent(i)] < A[i]
exchange A[i] with A[parent(i)]
i = parent(i)
本章相关代码的cpp实现 :
堆排序
#include<iostream>
using namespace std;
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
const int N = 2e5+10;
int a[N];
int n;
void max_heapify(int x) {
int largest = x;
if (x * 2 <= n && a[x * 2] > a[largest])
largest = x*2;
if (x*2 + 1 <= n && a[x*2+1] > a[largest])
largest = x*2 + 1;
if (largest != x) {
swap(a[x],a[largest]);
max_heapify(largest);
}
}
void build() {
for (int i = n/2; i>=1; i--) {
max_heapify(i);
}
}
void heapsort() {
build();
int t = n;
for (int i = 1; i<= t; i++) {
cout<<a[1]<<" ";
swap(a[1],a[n]);
n--;
max_heapify(1);
}
cout<<"\n";
}
int main() {
n =read();
for (int i = 1; i<= n; i++) {
a[i] = read();
}
heapsort();
return 0;
}
优先队列
#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
const int N = 2e5+10;
class MyQueue {
private:
int a[N]={0};
int sz=0;
void max_modify(int x) {
int ans = x;
if (x * 2 <= sz && a[x * 2] > a[ans]) ans = x* 2;
if (x*2 + 1 <=sz && a[x*2 + 1] > a[ans]) ans = x * 2 + 1;
if (ans != x) {
swap(a[ans],a[x]);
max_modify(ans);
}
}
public:
int top() {
if (sz < 1) {
cerr<<"wa"<<endl;
return -1;
}
return a[1];
}
void pop() {
swap(a[1],a[sz--]);
max_modify(1);
}
void update(int x,int k) {
if (k < a[x]) {
cerr<<"wa"<<endl;
return ;
}
a[x] = k;
while (x > 1 && a[x>>1] < a[x]) {
swap(a[x],a[x>>1]);
x= x<<1;
}
}
void push(int x) {
a[++sz] = -1e9+100;
update(sz,x);
}
int size() {
return sz;
}
};
signed main() {
MyQueue q;
q.push(10);
q.push(20);
q.push(15);
while (q.size()) {
cout<<q.top()<<" ";q.pop();
}
return 0;
}

浙公网安备 33010602011771号