堆排序代码实现c++

基本概念:
1、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。
2、一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
满二叉树由于其满的特点,无法插入和移除,业务上一般极少使用。

堆定义:
1、堆是一颗完全二叉树;
2、堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其子结点的值。
3、堆中每个结点的子树都是堆树。

堆是一颗完全二叉树,但是二叉树不一定是一个堆。因为堆是具备排序特性。
堆不是一个完全有序的数组,但是他可以保证根节点是最大或者最小的节点。用来持续pop最大值或者最小值,比如众多寻路算法中不断取得权值最小的节点,一般就用最小堆来实现。

常规的树一般基于链表实现。
堆,由于其非叶子节点为满,叶子节点都集中在左侧。如下图
所以可以基于数组实现。
基于数组的堆实现,可以非常方便的获取某一个节点的父节点和子节点,所以节点里面无需存储父节点指针和子节点指针,这也是有别于链表实现的地方。
在这里插入图片描述
插入逻辑:尾部插入,尾部上升。
在数组的末尾添加元素(节点),表示其处于最右侧的叶子节点,然后依次与其父节点((index-1)/2)比较,不符合大小限定则交换,直至上升至根节点,即可完成插入。
删除逻辑:头部删除,交换尾部至头部,头部下降。
只可删除根节点(也就是数组的第一个,为该堆的最大(最小)值),首先取出根节点,待返回用,然后将数组最末尾节点也就是最右侧叶子几点交换至根节点,最后将该根节点与其两个孩子节点比较,交换至较小的叶子节点中。轮询或递归至叶子节点即可。

代码实现

    struct Node{
        int value_;
        Node(int v){
            value_ = v;
        }
    };
    class sort_tree{ //min
        public:
            sort_tree(){}
            bool push(int value);
            shared_ptr<Node> pop();
            bool empty(){return root.empty();}
            void print();
        private:
            void up();
            void down();
        
        vector<shared_ptr<Node> > root;
    };
void sort_tree::up()
    {
        int cur = root.size() -1;
        while(cur >= 0){
            int parent = (cur-1)/2;
            if(parent < 0)break;
            if(root[parent]->value_ > root[cur]->value_){
                swap(root[parent], root[cur]);
                cur = parent;
                continue;
            }
            else{
                break;
            }
        }
    }
    void sort_tree::down()
    {
        int cur = 0;
        while(cur < root.size())
        {
            int left = cur*2+1;
            int right = (cur+1)*2;
            int min_child_idx = cur;
            if(right < root.size()){
                if(root[left]->value_ < root[right]->value_){
                    min_child_idx = left;
                }else{
                    min_child_idx = right;
                }
            }else if(left < root.size()){
                min_child_idx = left;
            }
            if(min_child_idx != cur){
                if(root[cur]->value_ > root[min_child_idx]->value_){
                    swap(root[cur], root[min_child_idx]);
                    cur = min_child_idx;
                    continue;
                }else{
                    break;
                }
            }
            else{
                break;
            }
        }
    }
    bool sort_tree::push(int value)
    {
        shared_ptr<Node> n = make_shared<Node>(value);
        if(!n)
            return false;
        root.push_back(n);
        up();
        return true;
    }
    
    shared_ptr<Node> sort_tree::pop()
    {
        if(root.empty())return nullptr;
        shared_ptr<Node> n = root[0];
        root[0] = root[root.size()-1];
        root.pop_back();
        down();
        return n;
    }

    void sort_tree::print(){
        if(root.empty())return;
        for(auto n : root){
            printf("%d ", n->value_);
        }
        printf("\n");
    }
    
    void test(){
        vector<int> v{10, 2, 3, 45,1, 11, 13};
        sort_tree st;
        for(auto value: v){
            st.push(value);
        }
        st.print();
        while(!st.empty()){
            shared_ptr<Node> n = st.pop();
            if(n){
                printf("%d\n", n->value_);
            }
            
        }
    }
运行结果:
./main 
1 2 3 45 10 11 13 
1
2
3
10
11
13
45

相比于快排等排序手法,对于固定个数的元素,平均时间开销上是不如的,但是比较稳定,插入最坏为O(nlog2n),而移除最小固定为O(nlog2n)。
空间效率为O(1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值