堆排序(C++)

实现

  1. 相关知识
  • 堆:堆是一棵完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值;
    • 最大堆(大根堆):根节点最大的堆;
    • 最小堆(小根堆):根节点最小的堆;
  • 如果将一维数组看作是一棵顺序存储的完全二叉树,则具有以下关系:
    • 对于任意一个父节点,假设其下标为x,则左子(如果有)下标为 (2x + 1), 右子(如果有)下标为(2x + 2)。
  • 堆排序:一种选择排序。将具有 n 个元素的数组,调整成堆(最大堆或最小堆),取出堆顶元素(放置到数组头或数组尾),然后将剩下 n-1 个元素重新调整为堆,再次取出堆顶元素, 依次类推,最后得到一个排序数组。
  1. 思路
  • 本文例子是建最大堆,升序排列。
    • 思路如下:(数组为 a, 元素数为 n)
      • 将数组 a 调整为最大堆, 则 a[0] 为堆顶元素(最大值);
      • 将堆顶元素 a[0] 放到数组尾( a[0] 与 a[n -1] 交换 );
      • 将前 n-1 个元素 重新调整为堆(其实此时只有 a[0] 可能不满足最大堆);
      • 将堆顶元素 a[0] 放到数组次尾( a[0] 与 a[n -2] 交换 );
      • 以此类推,最后数组升序排列完成;
    • 总结,实际仅需要解决两个问题:
      • 1 如何将数组代表的完全二叉树调整为堆?
      • 2 如何将只有根节点不满足堆的一组元素调整为堆?
  1. 代码
/*
调整为最大堆( 解决问题2)
a为数组,n为元素数,将其看作一颗完全二叉树,s为一颗子树根节点的下标,
对s子树来说,仅s节点可能不满足最大堆,将其调整为最大堆
思路:假设 child = [ s 左右子较大值下标], temp = a[s],   
     (1)若 a[s] >= a[child],则无需调整,直接返回;
     (2)若 a[s] < a[child] :
        则用 a[child] 填充 s 节点;
        s = child, child = [ child 左右子较大值下标],循环(1)(2)
     (3)将 temp 填充到 child 节点。     
*/
void adjust_heap(int a[], int n, int s){
  int temp = a[s];
  int child = 2*s + 1; // 默认child指向s节点左子
  while(child < n){
    // s节点右子存在,且右子更大, 则child指向s节点右子
    if ((child + 1 < n -1) && (a[child] < a[child + 1])){
      child +=1;
    }

    // child节点值填充到s节点
    if (a[s] < a[child]) {
      a[s] = a[child];
      
      s = child;
      child = 2 * s + 1;
    }else {
      break;
    }
    a[s] = temp;
  }
}

/*
建最大堆( 解决问题1)
a为数组,n为元素数,将其看作一颗完全二叉树
思路:假设 last_f 为此二叉树中最后一个元素的父节点下标,   
   则 a[0] ~ a[last_f] 均为具有子的父节点,
     (1)将以 a[last_f] 为根节点的子树调整为最大堆(此时可认为子节点满足最大堆,只是last_f不满足);
     (2)将以 a[last_f - 1] 为根节点的子树调整为最大堆;
     (3)依次倒序调整,直到a[0]调整完毕,数组就变成了最大堆存储。
*/
void build_heap(int a[], int n) {
  if (n < 2){
    return;
  }

  int last_f = 0; // 最后一个有子节点的父节点
  if (n % 2 == 0){
    last_f = (n - 1) /2; // n为偶数,最后一个节点为左子节点
  } else {
    last_f = (n - 3) /2; // n为奇数,最后一个节点为右子节点
  }

  for (int i = last_f; i >=0; --i){
    adjust_heap(a, n, last_f);
  }
}

void heap_sort(int a[], int n) {
  // 建最大堆
  build_heap(a, n);
  for (int i = n - 1; i >=0; --i) {
    // 堆顶节点(a[0])与i节点交换值
    int temp = a[i];
    a[i] = a[0];
    a[0] = temp;

    // 将前i个节点重新调整为最大堆
    adjust_heap(a, i, 0);

    print(a, n);
  }
}

测试

  1. 代码
#include <iostream>
using namespace std;

void print(int a[], int num) {
  for (int i = 0; i < num; ++i) {
    cout << a[i] << " ";
  }
  cout << endl;
}

int main() {
  int a[] = {7, 6, 5, 4, 3, 2, 1};
  int n = sizeof(a) / sizeof(a[0]);
  print(a, n);
  heap_sort(a, n);
  print(a, n);
  cin.get();
  return 0;
}
  1. 结果
7 6 5 4 3 2 1
6 4 5 1 3 2 7
5 4 2 1 3 6 7
4 3 2 1 5 6 7
3 1 2 4 5 6 7
2 1 3 4 5 6 7
1 2 3 4 5 6 7
1 2 3 4 5 6 7
1 2 3 4 5 6 7
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页