【数据结构】堆的实现(包括:默认成员函数,插元素push,删元素pop,访问根节点top,判空,大小)

原创 2016年05月30日 17:18:49

在数据结构里,堆是一类很重要的结构。堆结构是一组数组对象,我们可以把它当作是一颗完全二叉树。


最大堆:堆里每一个父亲节点大于它的子女节点。

最小堆:堆里每一个父亲节点小于它的子女节点。

如图就是一个最大堆:

wKioL1cbOTSjDiLRAAAZq4jMjWY012.png

实现代码时我的测试序列是:int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };

我们把它的图画出来,便于分析。

wKioL1cbVunRJvBdAABYpvWkEaw905.png

我们来实现如何将一个数组中的序列转变为最大堆。

若我们知道最大堆的代码后,只需将代码稍微修改一下就可以变成最小堆的代码。或者,我们可以用仿函数来提高代码的复用性。

实现代码如下:

建立头文件heap.hpp

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

#include<assert.h>
#include<vector>

template <class T>
class Heap
{
public:
    Heap()
        :_a(NULL)
    {}


    //构造堆:先把各个元素接收到,再根据堆的特点将元素调整
    Heap(const T* array, size_t size)
    {
        _a.reserve(size);
        for (size_t i = 0; i < size; i++)
        {
            _a.push_back(array[i]);
        }

        //建堆
        int Size = size;
        for (int j = (_a.size() - 2) / 2; j>=0; j --)
        {
            _AdjustDown(j, Size);
        }
    }


    //拷贝构造
    Heap(const vector<T>& vec)
        :_a(NULL)
    {
        _a.reserve(vec.size());
        for (size_t i = 0; i < size; i++)
        {
            _a.push_back(vec[i]);
        }
    }

    //插入一个元素x:先插入到顺序表中,再根据具体元素大小向上调整确定插入元素的位置
    void Push(const T& x)
    {
        _a.push_back(x);
        _AdjustUp(_a.size() - 1);
    }


    //删除根节点
    void Pop()
    {
        size_t size = _a.size();
        assert(size > 0);//防御式编程,确定是否可以删除元素
        swap(_a[0], _a[size - 1]);//若直接删除堆的根节点,则会使堆结构紊乱
        _a.pop_back();//将根节点与堆的最后一个节点交换位置,此时再对元素删除,以及将其调整于合适位置
        size = _a.size();
        _AdjustDown(0,size);
    }


    //访问堆的根节点
    T& GetTop()
    {
        size_t size = _a.size();
        assert(size > 0);
        return _a[0];
    }


    //将根节点向下调整
    void _AdjustDown(size_t parent,size_t size)
    {
        size_t child = 2 * parent + 1;
        while (child<size)
        {
            if (child+1 < size && _a[child] < _a[child + 1])
            {
                child++;
            }
            if (_a[child] > _a[parent])
            {
                swap(_a[child], _a[parent]);
                parent = child;
                child = 2 * parent + 1;
            }
            else
            {
                break;
            }
        }        
    }


    //向上调整
    void _AdjustUp(int child)
    {
        //无论插节点后为左子树还是右子树,都可用(child-2)/2计算出此时父节点的下标
        int parent = (child - 1) / 2;
        int size = _a.size();//size用int,若用size_t循环条件且为>=0则死循环
        while (child>0)//当child=0,说明此时已经到根节点位置,无需继续上调
        {
            //向上调整时,无需看左右节点哪个值大,只需要看是否父节点<根节点
            if (_a[child]>_a[parent])
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child-1)/2;
            }
            else
            {
                break;
            }
        }
    }


    bool Empty()
    {
        size_t size = _a.size();
        assert(size >= 0);
        return size == 0;
    }


    size_t Size()
    {
        size_t size = _a.size();
        assert(size >= 0);
        return size;
    }
    
    
    void PrintHeap()
    {
        cout << "堆的序列为:" << endl;
        for (int i = 0; i < Size(); i++)
        {
            cout << _a[i] << "  ";
        }
        cout << endl;
    }
private:
    vector<T> _a;
};


建立源文件heap.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include "heap.hpp"

void Test()
{
    int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
    Heap<int> h1(a, sizeof(a) / sizeof(a[0]));
    Heap<int> h2(h1);
    cout<<h1.GetTop()<<endl;
    cout << h1.Size() << endl;

    h1.Push(20);
    cout << h1.GetTop() << endl;

    h1.Pop();
    cout << h1.Size() << endl;

}


int main()
{
    Test();
    system("pause");
    return 0;
}


关于size(),GetTop()等函数我们可以通过测试函数Test()写出适当的测试用例来测试。


本文出自 “Han Jing's Blog” 博客,请务必保留此出处http://10740184.blog.51cto.com/10730184/1767076

堆的动态创建与根节点删除

堆的介绍与调整 关于堆的介绍以及对于给定的完全二叉树,调整为大根堆或者小根堆,可以参考博文http://blog.csdn.net/pngynghay/article/details/2205273...
  • IT_PCode
  • IT_PCode
  • 2014年03月25日 21:50
  • 1475

大根堆的插入、删除、修改优先级实现

大根堆其实就是一个一维数组, 不过数组中数据的排列是基于完全二叉树模型的。根节点的数据大于等于左孩子和右孩子节点的数据,然后每个子树都和前面一样。它的主要应用是在优先级队列中, 因为大根堆或小根堆总是...
  • haoyuedangkong_fei
  • haoyuedangkong_fei
  • 2016年05月26日 14:22
  • 1284

数据结构之栈的push与pop操作(顺序存储结构的c实现)

枝( stack )是限定仅在襄尾进行捕入和删除撮伟的雄性袋。
  • keepupblw
  • keepupblw
  • 2014年05月14日 15:26
  • 1001

JAVA实现用两个栈来实现一个队列,完成队列的Push和Pop操作(《剑指offer》)

最近在刷《剑指offer》里的编程题,但是网上关于《剑指offer》的解答多半是C或者C++的,而且官方(作者)也是在用C++进行讲解,这里自己用java写了一些题目的解答代码(当然也有部分是借鉴了网...
  • qq_15062527
  • qq_15062527
  • 2015年10月01日 11:54
  • 5059

堆的实现(堆的建立及push、pop元素)

堆数据结构是一种数组对象,它可以被视为一棵完全二叉树结构。堆结构的二叉树存储:大堆:每个父节点的都大于孩子节点;小堆:每个父节点的都小于孩子节点。建堆:由于堆被视为完全二叉树,故在h-1层找到第一个(...
  • Scenlyf
  • Scenlyf
  • 2016年06月11日 18:53
  • 285

实现一个返回最小值的栈

一、 实现一个栈,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值的操作) 的时间复杂度为O(1) (1)栈的基本操作入栈(push);出栈(pop);查找栈顶(top);栈的元素个数(...
  • gogogo_sky
  • gogogo_sky
  • 2017年04月15日 23:01
  • 522

【Java】设计一个栈,并且push,pop和min三个方法的时间复杂度必须为O(1)

设计一个栈,除pop与push方法,还支持min方法,可返回栈元素中的最小值。 push,pop和min三个方法的时间复杂度必须为O(1) 要保证min的时间复杂度为O(1), 那就不能每次通过遍...
  • michellechouu
  • michellechouu
  • 2015年07月10日 13:29
  • 1226

《算法导论》笔记 第6章 6.1堆

堆是一棵完全二叉树,树的根为 A[1]
  • cyendra
  • cyendra
  • 2014年04月06日 21:41
  • 707

用两个栈来实现一个队列,完成队列的Push和Pop操作

题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 这个用python不知道怎么做,就用java吧,参考大神思路 用两个栈实现一个队列的功能?要...
  • XiaoXIANGZI222
  • XiaoXIANGZI222
  • 2017年02月17日 15:16
  • 598

【算法总结-top K】堆--查找最小(大)的k个元素

top K问题是一个经典的问题。     该问题描述为:输入n个整数,输出其中最小的k个元素,例如,输入  1,2,3,4,5,6,7,8 那么最小的4个元素就是1,2,3,4.     ...
  • Aiphis
  • Aiphis
  • 2015年08月11日 16:27
  • 619
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【数据结构】堆的实现(包括:默认成员函数,插元素push,删元素pop,访问根节点top,判空,大小)
举报原因:
原因补充:

(最多只允许输入30个字)