堆排序(递归)C++实现

堆排序(递归)C++实现

堆排序

使用堆的数据结构来进行排序,堆可以看成一个近似完全二叉树的顺序存储结构的数组

维护堆性质

维护堆的性质,对于给定的下标i,以i结点为根节点的堆左右子树都是最大堆,此时i对应结点的关键字可能小于其孩子节点,则违背了堆性质,通过向下调整使得这个堆满足最大堆的性质
堆调整算法选取根节点左右孩子关键字较大值与根节点交换,再递归进入被交换的左右孩子为根节点的堆,继续向下调整,直到当前的堆满足最大堆性质或者调整到叶子节点。

  • C++代码
void MaxHeap::maxHeapify(int i) {
    int leftChild = left(i);
    int rightChild = right(i);
    int largest = i;
    if (leftChild <= heapSize && sequence[leftChild] > sequence[largest]) {
        largest = leftChild;
    }
    if (rightChild <= heapSize && sequence[rightChild] > sequence[largest]) {
        largest = rightChild;
    }
    if (largest != i) {
    	// 选取左右孩子结点关键字较大的与根节点交换
        swap(sequence[i], sequence[largest]);
        // 递归进入被调整的子树对应的堆继续向下调整
        maxHeapify(largest);
    }
}
  • 伪代码
MAX-HEAPIFY(A, i)
l = LEFT(i)
r = RIGHT(i)

if l <= A.heap-size and A[l] > A[i]
    largetest = l
else largetest = i

if r <= A.heap-size and A[r] > A[largetest]
    largetest = r

if largetest != i
    exchange A[i] with A[largetest]
    MAX-HEAPIFY(A, largetest)

建堆

从倒数第二层开始向下调整,因为最后一层的堆只有一个结点必然满足堆性质,而第二层调整完毕后,保证以倒数第二层结点为根结点的堆满足堆性质,继续调整倒数第三层直到调整到第一层的根节点,即整个堆满足堆性质,建堆完成。

  • C++代码
void MaxHeap::buildMaxHeap() {
    for (int i = heapSize / 2; i > 0; i--) {
        maxHeapify(i);
    }
}
  • 伪代码
BUILD-MAX-HEAP(A)
A.heap-size = A.length
for i = [A.length / 2] downto 1
    MAX-HEAPIFY(A, i)

堆排序

根据堆性质,堆顶元素(根结点)必然为堆中最大,每次堆顶元素与堆最后一个元素交换,即每次都取出子序列的最大值,这样能够保证堆为空后,所得到的序列为非递减序列,但每次交换操作后需要维护堆性质,才能保证下一次要取出的堆顶元素满足要求

  • C++代码
void MaxHeap::heapSort() {
    buildMaxHeap();
    for (int i = heapSize; i > 1; i--) {
        swap(sequence[i], sequence[1]);
        heapSize--;
        maxHeapify(1);
    }
}
  • 伪代码
HEAPSORT(A)

BUILD-MAX-HEAP(A)

for i = A.length downto 2
    exchange A[1] with A[i]
    A.heap-size = A.heap-size - 1
    MAX-HEAPIFY(A, 1)

实现代码

/*
author : eclipse
email  : eclipsecs@qq.com
time   : Wed Jul 15 20:37:52 2020
*/
#include <bits/stdc++.h>
using namespace std;

class MaxHeap {
private:
    vector<int> sequence;
    int heapSize;
    void maxHeapify(int i);
    int parent(int i);
    int left(int i);
    int right(int i);
    void buildMaxHeap();
public:
    MaxHeap(vector<int> array);
    void heapSort();
    void traverse();
};

MaxHeap::MaxHeap(vector<int> array) {
    heapSize = array.size();
    sequence.resize(array.size() + 1);
    for (int i = 1; i < sequence.size(); i++) {
        sequence[i] = array[i - 1];
    }
}

int MaxHeap::parent(int i) {
    return i / 2;
}

int MaxHeap::left(int i) {
    return i * 2;
}

int MaxHeap::right(int i) {
    return i * 2 + 1;
}

void MaxHeap::maxHeapify(int i) {
    int leftChild = left(i);
    int rightChild = right(i);
    int largest = i;
    if (leftChild <= heapSize && sequence[leftChild] > sequence[largest]) {
        largest = leftChild;
    }
    if (rightChild <= heapSize && sequence[rightChild] > sequence[largest]) {
        largest = rightChild;
    }
    if (largest != i) {
        swap(sequence[i], sequence[largest]);
        maxHeapify(largest);
    }
}

void MaxHeap::buildMaxHeap() {
    for (int i = heapSize / 2; i > 0; i--) {
        maxHeapify(i);
    }
}

void MaxHeap::heapSort() {
    buildMaxHeap();
    for (int i = heapSize; i > 1; i--) {
        swap(sequence[i], sequence[1]);
        heapSize--;
        maxHeapify(1);
    }
}

void MaxHeap::traverse() {
    for (int i = 1; i < sequence.size(); i++) {
        printf("%d ", sequence[i]);
    }
}

int main(int argc, char const *argv[]) {
    vector<int> array;
    int N;
    scanf("%d", &N);
    array.resize(N);
    for (vector<int>::iterator it = array.begin(); it != array.end(); it++) {
        scanf("%d", it);
    }
    MaxHeap *maxHeap = new MaxHeap(array);
    maxHeap->heapSort();
    maxHeap->traverse();
    return 0;
}

测试数据

10
2 0 2 0 0 7 1 5 21 34

输出结果

0 0 0 1 2 2 5 7 21 34

鸣谢

算法导论

最后

  • 上述伪代码来自算法导论
  • 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值