算法导论 第六章 堆排序

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练习答案

  1. 最少是\(\frac{1 + h-1 }{2} \times (h-1) + 1\) , 最多是\(\frac{1+h}{2} \times h\)
  2. 设有\(2^k \le n \lt 2^k+1\) , 取log之后显然了
  3. 最底层
  4. yes
  5. No
  6. 非常神奇的解法 . 考虑\(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;
}
posted @ 2025-05-10 20:35  Guaninf  阅读(5)  评论(0)    收藏  举报