C++实现大堆根

5 篇文章 0 订阅

大堆根基本概念

  • 堆顶的元素是最大的
  • 根节点比左右子节点的数大
  • 大堆根原本是一棵完全二叉树,二叉树中没有空缺。
  • 可以使用数组来存储这棵完全二叉树
  • 树和数组下标的关系(从下标0开始):第i位置的左节点为2i+1,右节点为2i+2,父节点为(i-1)/2
  • 堆和数组的关系
    在这里插入图片描述

建立大堆根的重要函数

  • 大堆根插入函数(从后调整):
    目的:
    1.在[0,index-1]段满足大堆根的规则,再加入一个元素,则需要调整;
    2.调整原则:将index元素与其父节点比较,index元素大,则需要交换;并循环与向上的父节点比较;
	//在大堆根后面index位置的元素进行调整
    void heapInsert(vector<int>& arr, int index)
    {//将index和他的根节点比较,大于的话就交换
        while(arr[index] > arr[(index-1)/2])
        {//index=0时会终止循环
            swap(arr[index], arr[(index-1)/2]);
            index = (index - 1) / 2;
        }
    }
  • 大堆根调整函数(从前调整):
    目的:
    1.在删除元素(堆顶)时,需要将堆顶元素(下标为0的元素)与数组尾元素交换,然后再删除数组尾的元素
    2.调整原则:将index与他左/右节点最大的元素交换,并循环向下做同样的操作
    //调整index位置的元素,一般是第一个index=0
    //[0,heapSize)为大堆根段,具体理解可以结合全部代码中的堆排序函数heapSort()
    void heapAdj(vector<int>& arr, int index, int heapSize)
    {//将index与他左/右节点最大的交换
        int left = 2 * index + 1; // index的左节点
        while(left < heapSize)
        {
            //寻找左右节点的最大值下标
            int large = left + 1 < heapSize && arr[left+1] > arr[left] ? left + 1 : left;
            //左右最大值和index比较
            large = arr[large] > arr[index] ? large : index;
            if (large == index)
            {
                break;
            }
            swap(arr[large], arr[index]);
            index = large;
            left = 2 * index + 1;
        }
    }

全部代码

  • main.cpp:
#include <bits/stdc++.h>
#include "bigheap.h"
using namespace std;

int main()
{
    vector<int> v{1, 2, 5, 6};
    BigHeap bh(v);
    bh.push(10);
    bh.push(4);
    bh.push(3);
    cout << "大堆根:";
    for (auto a : bh.get())
    {
        cout << a << " ";
    }
    cout << endl;
    cout << "大堆根弹出的顺序:" << endl;
    int n = bh.size();
    for (int i = 0; i < n; i++)
    {
        cout << "top: " << bh.top() << endl;
        bh.pop();
    }
    vector<int> v1{1, 2, 45, 20, 5, 6};
    cout << "堆排序:";
    bh.heapSort(v1);
    for (auto a : v1)
    {
        cout << a << " ";
    }
    cout << "你好" << endl;
    return 0;
}
  • 运行结果
大堆根:10 6 4 1 5 2 3
大堆根弹出的顺序:
top: 10
top: 6
top: 5
top: 4
top: 3
top: 2
top: 1
堆排序:1 2 5 6 20 45 你好
  • bigheap.h:
// 大堆根
// Created by Ycq on 2022/6/8.
//

#ifndef CLIONTEST_BIGHEAP_H
#define CLIONTEST_BIGHEAP_H

#include <vector>

using namespace std;

// 建立大堆根
// 参考:https://blog.csdn.net/dream_follower/article/details/105202811
// 数组以下标0开始,第i位置的左节点为2i+1,右节点为2i+2,父节点为(i-1)/2
class BigHeap
{
private:
    vector<int> vec;
public:
    BigHeap(){}
    BigHeap(vector<int> &v) : vec(v)
    {
        heapBuild(vec);
    }
    //返回大堆数组
    vector<int> get()
    {
        return vec;
    }
    //返回大堆顶
    int top()
    {
        return vec[0];
    }
    //删除堆顶
    void pop()
    {
        swap(vec[0], vec[vec.size() - 1]); //将堆顶元素交换到数组尾部
        vec.pop_back(); //删除尾部元素
        heapAdj(vec, 0, vec.size()); //调整大堆
    }
    //添加元素
    void push(int x)
    {
        vec.push_back(x);
        heapInsert(vec, vec.size() - 1);
    }
    //vec的大小
    int size()
    {
        return vec.size();
    }
    //判空
    bool empty()
    {
        return vec.empty();
    }

    //在大堆根后面index位置的元素进行调整
    void heapInsert(vector<int>& arr, int index)
    {//将index和他的根节点比较,大于的话就交换
        while(arr[index] > arr[(index-1)/2])
        {//index=0时会终止循环
            swap(arr[index], arr[(index-1)/2]);
            index = (index - 1) / 2;
        }
    }
    //调整index位置的元素,一般是第一个index=0
    void heapAdj(vector<int>& arr, int index, int heapSize)
    {//将index与他左/右节点最大的交换
        int left = 2 * index + 1; // index的左节点
        while(left < heapSize)
        {
            //寻找左右节点的最大值下标
            int large = left + 1 < heapSize && arr[left+1] > arr[left] ? left + 1 : left;
            //左右最大值和index比较
            large = arr[large] > arr[index] ? large : index;
            if (large == index)
            {
                break;
            }
            swap(arr[large], arr[index]);
            index = large;
            left = 2 * index + 1;
        }
    }
    //建大堆根
    void heapBuild(vector<int>& arr)
    {
        int n = arr.size();
        if (n < 2) return;
        for (int i = 0; i < n; i++)
        {//建大堆根
            heapInsert(arr, i);
        }
    }
    //堆排序, 不要给本对象的vec排序,由于使用的是引用传参,会改变vec,而使vec不满足大堆根的规则
    void heapSort(vector<int>& arr)
    {//其实就是依次把堆顶的元素放入数组尾部,每次heapSize减1(>0),最后arr是升序
        int n = arr.size();
        heapBuild(arr);
        //将第一个(堆顶)元素和最后一个元素交换
        swap(arr[0], arr[--n]);
        while(n > 0)
        {
            heapAdj(arr, 0, n);
            swap(arr[0], arr[--n]);
        }
    }
};


#endif //CLIONTEST_BIGHEAP_H

C++中priority_queue的关键代码

make_heap(c.begin(),c.end(),comp); //是在构造函数中,使容器c中的元素生成大堆根(默认)

template <class T, class Squence = vector<T>, 
class Compare = less<typename Sequence::value_tyoe> > 
class priority_queue{ 
	... 
protected: 
	Sequence c; // 底层容器 
	Compare comp; // 元素大小比较标准 
public: 
	bool empty() const {return c.empty();} 
	size_type size() const {return c.size();} 
	const_reference top() const {return c.front()} 
	void push(const value_type& x) 
	{ 
		c.push_back(x); 
		push_heap(c.begin(), c.end(),comp); //在插入一个元素后,调整元素,使满足堆的规则
	} 
	void pop() 
	{ 
		pop_heap(c.begin(), c.end(),comp); //将堆顶元素调整到c的末尾,并使其使满足堆的规则,然后删除最后一个元素
		c.pop_back(); 
	}
};

递归版本的大堆根建立

void maxHeapify(vector<int>& a, int i, int heapSize) 
{
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a[i], a[largest]);
            maxHeapify(a, largest, heapSize);
        }
}
   
void buildMaxHeap(vector<int>& a, int heapSize) 
{
        for (int i = heapSize / 2 - 1; i >= 0; --i) { // 从最后一个父节点开始
            maxHeapify(a, i, heapSize);
} 

//堆排序时,同样可以将第一个元素与最后一个元素交换,然后调用maxHeapify(a, 0, heapSize--);
void heap_sort(vector<int>& a, int heapSize)
{
	buildMaxHeap(a, heapSize);
	for (int i = 0; i < heapSize; i++)
	{
		swap(nums[0], nums[heapSize - 1 - i]);
		maxHeapify(a, 0, heapSize - 1 - i);
	}
}

参考&感谢

主要参考这里写的
递归版本参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值