C++堆排的一种实现

思路

  • 堆排分两步,第一步是建立堆,第二步是进行堆排序。
  • 堆的一些基本概念:
    • 堆的逻辑表现方式是一颗完全二叉树。用一个数组保存元素,第一个数代表根节点,index为0。尾元素的index为 n n n,表示最后一个叶子节点。
    • 对于任何一个非根节点的元素,若其index为 i i i,可知道其父节点和左右孩子节点的index。父节点的为 ( i − 1 ) / 2 (i-1)/2 (i1)/2,左孩子的为 2 i + 1 2i + 1 2i+1,右孩子的为 2 i + 2 2i+2 2i+2
    • 因为进行下滤操作的节点必须要有孩子节点,最后一个叶子节点的父节点为 ( n − 1 ) / 2 (n-1)/2 (n1)/2
  • 建立堆的顺序是,从最后一个叶子节点的父节点开始,对前面所有的节点进行一次下滤操作。
  • 堆排序:建立一个大根堆,保证堆的首元素是最大值,每次将首元素和尾元素交换位置,再将数组的大小缩小1,然后对首元素进行下滤。循环上述过程,直到堆中只剩下一个元素。

代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void swap( int& a, int& b ) {
    int temp = a;
    a = b;
    b = temp;

    return;
}

void percolateDown( vector<int>& nums, int endIndex, int index ) {
	// 下滤操作
    int lastDad = ( endIndex - 1 ) >> 1; // 最后一个父节点

    while ( index <= lastDad ) {		// 要执行下滤的节点有孩子才执行
        int lChild = 2 * index + 1;		// 左孩子坐标
        int rChild = lChild + 1;		// 右孩子坐标
        int maxChild = 0;				// 较大的孩子坐标

        if ( rChild > endIndex )		// 如果没有右孩子,则较大孩子坐标设为左孩子坐标。循环条件保证当前节点一定有孩子。
            maxChild = lChild;
        else
            maxChild = nums[lChild] > nums[rChild] ? lChild : rChild;		// 否则选较大的孩子

        if ( nums[index] < nums[maxChild] ) {	// 如果孩子大于父亲,则孩子值和父亲值交换。
            swap( nums[index], nums[maxChild] );
            index = maxChild;	// index设为被交换的孩子的位置,继续关注这个值是不是需要继续进行向下交换。
        }
        else break;		// 如果父亲值比孩子值大,说明下滤结束了。
    }
    
    return;
}

void buildHeap( vector<int> &nums ){
	// 建立一个大根堆
    int index = ( nums.size() - 2 ) >> 1;   // 最后一个节点的父节点。

    while ( index >= 0 ) {	// 从最后一个父亲节点,从底向上知道根节点,进行下滤操作。
        percolateDown( nums, nums.size() - 1, index );
        --index;
    }

    return;
}

void heapSortCore( vector<int>& nums ) {
	// 堆排序
    int endIndex = nums.size() - 1;		// 最后一个叶子节点

    while ( endIndex > 0 ) {	// 堆排停止条件是当前堆只剩下一个元素,
        swap( nums[0], nums[endIndex] );	// 把当前堆的首元素也就是最大值交换到尾部。
        --endIndex;		// 尾部指针减一,代表堆减少一个数。也代表了endIndex后面已经是有序的了。
        percolateDown( nums, endIndex, 0 );	// 首尾交换后,现有的堆结构被破坏,进行一次下滤,调整堆。
    }
}

void heapSort( vector<int>& nums ) {
	// 堆排序,分为两步。1. 建立大根堆。 2.进行排序。
    // 建立堆
    buildHeap( nums );
    //cout << "heap established: ";
    //for (auto &n : nums)
    //    cout << n << " ";
    //cout << endl;
    //cout << " ---------------------------------- " << endl;

    // 进行排序
    heapSortCore( nums );

    return;
}

int main() {
    vector<int> input = { 2, 1, 6, 3, 9, 7, 4, 8, 5 };
    heapSort( input );

    for ( auto& n : input )
        cout << n << " ";

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值