最小堆,堆排序(c++代码)

//所谓的最大堆,最小堆其实是一颗完全二叉树.并以数组来存储树的节点元素.这里的示例从数组的0下标开始存储树的节点.
//最小堆的父节点<=子节点,最大堆的父节点>=子节点.
//最大堆常用于实现优先队列等,最小堆常用于实现定时器(例如libevent)等.
//以下代码实现了最小堆和堆排序的算法,作为备忘笔记.使用的时候可以封装成模板类,这里只是算法的示例代码.
//从大到小排序使用最小堆,从小到大排序使用最大堆.
//要改成最大堆只需要改几个(<=)运算符.
//如有错误,欢迎指正.

#include <stdlib.h>
#include <stdio.h>
#include <vector>
#define HEAP_PARENT(i)		((i-1)/2)
#define HEAP_LEFTCHILD(i)	(2*i+1)
#define HEAP_RIGHTCHILD(i)	(2*i+2)

using namespace std;

//节点
struct HeapNode{
	HeapNode(int k) : key(k){}
    int key;
};

//
struct Heap{
    Heap():size_(0) {}
    vector<HeapNode*>	vec_;
    int                 size_;
};

//下移pos位置节点.
//如果pos位置的节点 > 其子节点,则将pos位置元素和其子节点中较小的节点交换.并重复此过程,直到不能再下移.
void shiftDown( Heap & h , int pos ) {
    int last_index = h.size_ - 1;
    while (1){
        //叶节点,下移结束
        bool has_child = HEAP_LEFTCHILD(pos) <= last_index ? true : false;
        if ( !has_child ) break;
        //找到父子三个节点中最小节点的位置
        int min_index;
        min_index = h.vec_[pos]->key <= h.vec_[HEAP_LEFTCHILD(pos)]->key ? pos : HEAP_LEFTCHILD(pos);
        bool has_right_child = HEAP_RIGHTCHILD(pos) <= last_index ? true : false;
        if (has_right_child){
            min_index = h.vec_[min_index]->key <= h.vec_[HEAP_RIGHTCHILD(pos)]->key ? min_index : HEAP_RIGHTCHILD(pos);
        }
        //如果父节点不是最小则和最小的子节点交换位置
        if ( pos == min_index) break;
        //交换节点数据,更新pos,进行下一轮下移.
        HeapNode * temp = h.vec_[ pos ];
        h.vec_[ pos ] = h.vec_[ min_index ];
        h.vec_[ min_index ] = temp;
        pos = min_index;
    }
}

//上移pos位置节点.
//如果pos位置的节点小于父节点,则和父节点交换.并重复此过程,直到不能再上移.
void shiftUp( Heap & h , int pos ){
    while ( pos > 0) {
        int parent = HEAP_PARENT(pos);
        if ( h.vec_[parent]->key <= h.vec_[pos]->key) break;
        //
        HeapNode * temp = h.vec_[pos];
        h.vec_[pos] = h.vec_[parent];
        h.vec_[parent] = temp;
        pos = parent;
    }
}

//将新节点加入到最后,并且上移该节点到适当位置.
void push( Heap & h , HeapNode * newNode ){
    int pos = h.size_;
    h.vec_.push_back(newNode);
    h.size_++;
    shiftUp(h , pos);
}

//将第一个节点与最后一个节点交换,再删除最后一个节点,从而弹出树顶端的节点.
//将交换后的第一个节点下移到适当位置.
HeapNode * pop(Heap & h){
    if ( h.size_ == 0 ) return NULL;
    HeapNode * res = h.vec_[0];
    h.vec_[0] = h.vec_[ h.size_ - 1 ];
    h.vec_.pop_back();
    h.size_--;
    shiftDown( h , 0 );
    return res;
}

//建堆
//从最后一个非叶子节点开始下移.直到根节点下移.将无序的数组创建为一个最小堆.
void makeHeap(Heap & h){
    for (int i = h.size_/2 - 1 ; i >= 0 ; i--){
        shiftDown(h , i);
    }
}

//堆排序
//由于最小堆的树顶元素为最小值,将树顶元素与最后一个元素交换,并且减小堆的大小.然后维护堆的性质.
//等到树中只剩下一个元素的时候,存储堆的数组就是一个有序的序列.
void heapSort(Heap & h){
    for( int i = h.size_ - 1 ; i>=1 ; i-- ){
        //
        HeapNode * temp = h.vec_[i];
        h.vec_[i] = h.vec_[0];
        h.vec_[0] = temp;
        h.size_--;
        //
        shiftDown(h , 0);
    }
}

void print_vec( Heap & h ){
    for (int i = 0 ; i < h.vec_.size() ; i++){
        printf("%d " , h.vec_[i]->key);
    }
}

#if 0
int main(){
    Heap h;
    HeapNode * node;
    printf("\n=====push , pop=======\n");
    push(h , new HeapNode(100) );
    push(h , new HeapNode(88) );
    push(h , new HeapNode(89) );
    push(h , new HeapNode(111) );
    push(h , new HeapNode(60) );
    while( (node = pop(h)) != NULL ){
        printf( "%d " , node->key );
    }
    printf("\n=====make heap , pop=======\n");
    h.vec_.push_back( new HeapNode(100) );
    h.vec_.push_back( new HeapNode(88) );
    h.vec_.push_back( new HeapNode(89) );
    h.vec_.push_back( new HeapNode(111) );
    h.vec_.push_back( new HeapNode(60) );
    h.size_ = 5;
    makeHeap(h);
    while( (node = pop(h)) != NULL ){
        printf( "%d " , node->key );
    }
    printf("\n=====make heap , heap sort=======\n");
    h.vec_.push_back( new HeapNode(100) );
    h.vec_.push_back( new HeapNode(88) );
    h.vec_.push_back( new HeapNode(89) );
    h.vec_.push_back( new HeapNode(111) );
    h.vec_.push_back( new HeapNode(60) );
    h.size_ = 5;
    makeHeap(h);
    heapSort(h);
    print_vec(h);

}
#endif

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值