C++ Heap 堆

C++ Heap 堆

C++ STL学习——heap

堆相关算法详解与C++编程实现(Heap)

白话经典算法系列之七 堆与堆排序

STL 堆操作

  • 初始化堆

    vector<int>vectorArr = {3,4,5,6,7,1,2}; // 数组
    make_heap(vectorArr.begin(), vectorArr.end(), less<int>()); // 创建最大堆
    // make_heap(vectorArr.begin(), vectorArr.end(), greater()); // 创建最小堆
  • 判断堆

    is_heap(vectorArr.begin(), vectorArr.end()); // 布尔值
  • 插入元素

    // 先调用 push_back先元素插入vector
    vectorArr.push_back(100); // 插入到最后
    // 再调用 push_heap调整堆
    push_heap(vectorArr.begin(), vectorArr.end());
  • 弹出元素

    // 先调用pop_heap将堆顶元素放到末尾
    push_heap(vectorArr.begin(), vectorArr.end());
    // 再调用pop_back再末尾元素弹出
    vectorArr.pop_back();
  • 堆排序

    // 堆排序后不是一个真正的堆了,只是完成了排序。
    // 堆排序算法。执行此操作之后,容器vector中的元素按照从小到大的顺序排列。
    sort_heap(vectorArr.begin(), vectorArr.end());
    // 排序之后可以用pop_heap() + pop_back()按顺序取出

二叉堆的定义

  • 二叉堆是一种完全二叉树.

  • 堆分为大顶堆和小顶堆

    当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆

    当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆

  • 二叉堆满足的性质:

    • 父节点的键值总是大于或等于(小于或等于) 任何一个子节点的键值.
    • 每个节点的左子树和右子树都是一个二叉堆.

堆的存储

  • 可以用数组来存储所有的节点.

    存储结构

    在具体实现过程中,要注意数组[0]位置是否使用.

    索引0使用,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。

堆的操作

无论进行哪些操作.. 都必须保持两个性质!

  1. 堆是完全二叉树.
  2. 最大/最小堆性质

插入

  • 每次插入都是将新数据放在数组最后。 这样保证了是完全二叉树.
  • 然后再将这个元素插入到合适位置! 排入排序

删除

  • 堆的删除操作只能删除堆顶元素.
  • 为了便于重建堆. 实际操作是将最后一个元素赋值给栈顶元素.然后再将这个元素从栈顶从上向下调整.
  • 对于最小堆来说: 若这个元素比左右孩子都小. 那么无需操作了. 否则. 将左右孩子中较小的元素提取上去. 该元素下降.

堆化数组

  • 将一个数组看做是一个叶子节点已经堆化完成的堆.
  • 对最后一个叶子节点的父亲节点到索引0位置的元素进行堆化(向下调整)

堆排序

  • 每次取出堆顶元素. 即是排序结果.
  • 由于堆也是用数组模拟的,故堆化数组后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了

实现自己的一个最小堆

#pragma once
#include "myinclude.h"

class MinHeap {

private:
    vector<int> vec;
    int cnt; // 个数
    static const int StartIndex = 1;// 从1开始计数

public:
    MinHeap() {
        vec.clear();
        vec.push_back(0); // 
    }

    MinHeap(vector<int> data) {
        vec.clear();
        vec.push_back(0); // 
        int sz = data.size();
        for (int i = 0; i < data.size(); ++i) {
            vec.push_back(data[i]);
        }
        cnt = data.size();
        MinHeapMakeHeap();
    }

    // 插入元素
    void MinHeapInsert(int data){
        vec.push_back(data);
        cnt++;
        shiftUp(cnt);
    }

    // 上溯
    void shiftUp(int child) {
        int tmp = vec[child];
        int parent = child / 2;

        while (parent >= StartIndex && child != StartIndex) {

            if (vec[parent] <= tmp)
                break;
            // 
            vec[child] = vec[parent];
            child = parent;
            parent = child / 2;
        }
        vec[child] = tmp;
    }

    // 删除元素
    void MinHeapPop() {
        // int res = vec[StartIndex]; 若要返回就把这个元素返回
        vec[StartIndex] = vec[cnt]; 
        cnt--;
        shiftDown(StartIndex);  
    }

    // 向下调整
    void shiftDown(int Index) {
        int parent = Index;
        int tmp = vec[parent];
        int leftChild = parent * 2;

        while (leftChild <= cnt) {

            // 找到左右孩子中最小的数
            if (leftChild + 1 <= cnt&&vec[leftChild + 1] < vec[leftChild]) {
                leftChild++;
            }

            if (vec[leftChild] >= tmp) {
                break;
            }

            vec[parent] = vec[leftChild];
            parent = leftChild;
            leftChild = parent * 2;
        }
        vec[parent] = tmp;
    }

    // 堆化数组
    void MinHeapMakeHeap() {
        int n = cnt;
        for (int i = n / 2; i >= StartIndex; i--) {
            shiftDown(i);
        }
    }

    // 堆排序
    void MinHeapSort() {
        //
    }

    // 打印
    void MinHeapPrint() {
        for (int i = StartIndex; i <= cnt; i++) {
            cout << vec[i] << ends;
        }
        cout << endl;
    }

};

你可能感兴趣的:([数据结构与算法])