【qbxt!预习】二叉堆

qxbt的老师发消息来说让自己预习,本来想中考完之后认真学(颓)习(废)  没办法

0. 数据结构图文解析系列

1. 二叉堆的定义

二叉堆是一种特殊的堆,二叉堆是完全二叉树或近似完全二叉树。二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。
当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。

2. 二叉堆的存储

二叉堆一般使用数组来表示。请回忆一下二叉树的性质,其中有一条性质:

性质五:如果对一棵有n个节点的完全二叉树的节点按层序编号(从第一层开始到最下一层,每一层从左到右编号,从1开始编号),对任一节点i有:

  1. 如果i=1 ,则节点为根节点,没有双亲。
  2. 如果2 * i > n ,则节点i没有左孩子 ;否则其左孩子节点为2*i . (n为节点总数)
  3. 如果2 * i+1>n ,则节点i没有右孩子;否则其右孩子节点为2*1+1.

简单来说:

  1. 如果根节点在数组中的位置是1,第n个位置的子节点分别在2n 与 2n+1,第n个位置的双亲节点分别在?i /2?。因此,第1个位置的子节点在2和3.
  2. 如果根节点在数组中的位置是0,第n个位置的子节点分别在2n+1与2n+2,第n个位置的双亲节点分别在?(i-1) /2?。因此,第0个位置的子节点在1和2.

得益于数组的随机存储能力,我们能够很快确定堆中节点的父节点与子节点。

下面以大顶堆展示一下堆的数组存储。

在本文中,我们以大顶堆为例进行堆的讲解。本文大顶堆的根节点位置为0.

3. 二叉堆的具体实现

在二叉堆上可以进行插入节点、删除节点、取出堆顶元素等操作。

3.1 二叉堆的抽象数据类型

 1 /*大顶堆类定义*/
 2 template <typename T>
 3 class MaxHeap
 4 {
 5 public:
 6     bool insert(T val);     //往二叉堆中插入元素
 7     bool remove(T data);    //移除元素
 8     void print();           //打印堆
 9     T getTop();             //获取堆顶元素
10     bool createMaxHeap(T a[], int size);//根据指定的数组来创建一个最大堆
11
12     MaxHeap(int cap = 10);
13     ~MaxHeap();
14
15 private:
16     int capacity;   //容量,也即是数组的大小
17     int size;       //堆大小,也即是数组中有效元素的个数
18     T * heap;       //底层的数组
19 private:
20     void filterUp(int index); //从index所在节点,往根节点调整堆
21     void filterDown(int begin ,int end ); //从begin所在节点开始,向end方向调整堆
22 };
  1. 注意capacity与size的区别。capacity指的是数组的固有大小。size值数组中有效元素的个数,有效元素为组成堆的元素。
  2. heap为数组。

3.2 二叉堆的插入

在数组的最末尾插入新节点,然后自下而上地调整子节点与父节点的位置:比较当前结点与父节点的大小,若不满足大顶堆的性质,则交换两节点,从而使当前子树满足二叉堆的性质。时间复杂度为O(logn)。
当我们在上图的堆中插入元素12:

调整过程:

  1. 节点12添加在数组尾部,位置为11;
  2. 节点12的双亲位置为?11/2? = 5,即节点6;节点12比节点6大,与节点6交换位置。交换后节点12的位置为5.
  3. 节点12的双亲位置为? 5 /2? = 2,即节点9;节点12比节点9大,与节点9交换位置。交换后节点12的位置为2.
  4. 节点12的双亲位置为?2/2? = 1,即节点11;节点12比节点11大,与节点11交换位置。交换后节点12的位置为1.
  5. 12已经到达根节点,调整过程结束。

    /*从下到上调整堆*/
    /*插入元素时候使用*/
    template <typename T>
    void MaxHeap<T>::filterUp(int index)
    {
        T value = heap[index];  //插入节点的值,图中的12
    
        while (index > 0) //如果还未到达根节点,继续调整
        {
            int indexParent = (index -1)/ 2;  //求其双亲节点
            if (value< heap[indexParent])
                break;
            else
            {
                heap[index] = heap[indexParent];
                index = indexParent;
            }
        }
        heap[index] = value;    //12插入最后的位置
    };

    在真正编程的时候,为了效率我们不必进行节点的交换,直接用父节点的值覆盖子节点。最后把新节点插入它最后的位置即可。

    基于这个调整函数,我们的插入函数为:

    /*插入元素*/
    template <typename T>
    bool MaxHeap<T>::insert(T val)
    {
        if (size == capacity) //如果数组已满,则返回false
            return false;
        heap[size] = val;
        filterUp(size);
        size++;
        return true;
    };

    3.3 二叉堆的删除

    堆的删除是这样一个过程:用数组最末尾节点覆盖被删节点,再从该节点从上到下调整二叉堆。我们删除根节点12:

    可能有人疑惑,删除后数组最末尾不是多了一个6吗?
    的确,但我们把数组中有效元素的个数减少了一,最末尾的6并不是堆的组成元素。

    这个从上到下的调整过程为:

    /*从上到下调整堆*/
    /*删除元素时候使用*/
    template<typename T>
    void MaxHeap<T>::filterDown(int current,int end)
    {
    
        int child = current * 2 + 1; //当前结点的左孩子
    
        T value = heap[current];    //保存当前结点的值
    
        while (child <= end)
        {
            if (child < end && heap[child] < heap[child+1])//选出两个孩子中较大的孩子
                child++;
            if (value>heap[child])  //无须调整;调整结束
                break;
            else
            {
                heap[current] = heap[child];    //孩子节点覆盖当前结点
                current = child;                //向下移动
                child = child * 2 + 1;
            }
        }
        heap[current] = value;
    };

    基于调整函数的删除函数:

    /*删除元素*/
    template<typename T>
    bool MaxHeap<T>::remove(T data)
    {
        if (size == 0) //如果堆是空的
            return false;
        int index;
        for (index = 0; index < size; index++)  //获取值在数组中的索引
        {
            if (heap[index] == data)
                break;
        }
        if (index == size)            //数组中没有该值
            return false; 
    
        heap[index] = heap[size - 1]; //使用最后一个节点来代替当前结点,然后再向下调整当前结点。
    
        filterDown(index,size--);  
    
        return true;
    };

    3.4 其余操作

    其余操作很简单,不在这里啰嗦。

    /*打印大顶堆*/
    template <typename T>
    void MaxHeap<T>::print()
    {
        for (int i = 0; i < size; i++)
            cout << heap[i] << " ";
    };
    /*获取堆顶元素*/
    template <typename T>
    T MaxHeap<T>::getTop()
    {
        if (size != 0)
            return heap[0];
    };
    
    /*根据指定的数组来创建一个最大堆*/
    template<typename T>
    bool MaxHeap<T>::createMapHeap(T a[], int size)
    {
        if (size > capacity)    //  堆的容量不足以创建
            return false;
        for (int i = 0; i < size; i++)
        {
            insert(a[i]);
        }
        return true;
    };

    4. 二叉堆代码测试

int _tmain(int argc, _TCHAR* argv[])
{
    MaxHeap<int> heap(11);
    //逐个元素构建大顶堆
    for (int i = 0; i < 10; i++)
    {
        heap.insert(i);
    }
    heap.print();
    cout << endl;
    heap.remove(8);
    heap.print();
    cout << endl;

    //根据指定的数组创建大顶堆
    MaxHeap<int> heap2(11);
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    heap2.createMaxHeap(a, 10);
    heap2.print();
    getchar();
    return 0;
}

输出结果:


9 8 5 6 7 1 4 0 3 2
9 7 5 6 2 1 4 0 3
10 9 6 7 8 2 5 1 4 3
 

原文地址:https://www.cnblogs.com/_Kurisu/p/9251157.html

时间: 2024-07-31 08:59:59

【qbxt!预习】二叉堆的相关文章

二叉堆(binary heap)

堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因而实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权.堆即为解决此类问题设计的一种数据结构. 本文地址:http://www.cnblogs.com/archimedes/p/binary-heap.html,转载请注明源地址. 逻辑定义 n个

算法—二叉堆

实现栈或是队列与实现优先队列的最大不同在于对性能的要求.对于栈和队列,我们的实现能够在常数时间内完成所有操作:而对于优先队列,插入元素和删除最大元素这两个操作之一在最坏情况下需要线性时间来完成.我们接下来要讨论的基于数据结构堆的实现能够保证这两种操作都能更快地执行. 1.堆的定义 数据结构二叉堆能够很好地实现优先队列的基本操作.在二叉堆的数组中,每个元素都要保证大于等于另两个特定位置的元素.相应地,这些位置的元素又至少要大于等于数组中的另两个元素,以此类推.如果我们将所有元素画成一棵二叉树,将每

二叉堆 - 最小堆

二叉堆:一般我们拿来用的就是最大堆和最小堆. 最小堆:每个节点的值比它的左右子节点的值要大. 代码实现如下:参考Mark Allen Weiss<数据结构和算法分析>(第二版) 1 #include <cstdio> 2 #include <cstdlib> 3 4 #define MIN (1<<(sizeof(int)*8-1)) 5 6 typedef int Item; 7 typedef struct HeapStruct* heap; 8 9 s

Bestcoder4——Happy Three Friends(二叉堆)

本文出自:http://blog.csdn.net/svitter Happy Three Friends Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 0    Accepted Submission(s): 0 Problem Description Dong-hao , Grandpa Shawn , Beautful-leg

堆排序:什么是堆?什么是最大堆?二叉堆是什么?堆排序算法是怎么样的?PHP如何实现堆排序?

本文标签:  堆排序 php php算法 堆排序算法 二叉堆 数据结构 REST   服务器 什么是堆 这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构. 堆可以视为一棵完全的二叉树,完全二叉树的一个"优秀"的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素. 数组与堆之间的关系 二叉堆一般分为两种:最大堆和最小堆. 什么是最大堆 堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆 因此,最大堆中的

poj 3253 初涉二叉堆 模板题

这道题很久以前就做过了 当时是百度学习了优先队列 后来发现其实还有个用sort的办法 就是默认sort排序后 a[i]+=a[i-1] 然后sort(a+i,a+i+n) (大概可以这样...答案忘了...) 嗯...其实标准解法是二叉堆.. 主函数里面的while里面wa了好多次.. 每次都选最小的那俩相加 再放回去 #include<stdio.h> #include<string.h> #include<algorithm> using namespace std

在A*寻路中使用二叉堆

接上篇:A*寻路初探 GameDev.net 在A*寻路中使用二叉堆 作者:Patrick Lester(2003年4月11日更新) 译者:Panic 2005年3月28日 译者序:     这一篇文章,是"A* Pathfinding for Beginners.",也就是我翻译的另一篇文章<A*寻路初探>的补充,在这篇文章里,作者再一次展现了他阐述复杂话题的非凡能力,用通俗易懂的语句清晰的解释了容易让人迷惑的问题.还是那句话,如果你看了这篇文章仍然无法领会作者的意图,那

堆、二叉堆、堆排序

堆.二叉堆.堆排序 堆的概念: n个元素序列 { k1, k2, k3, k4, k5, k6 -. kn } 当且仅当满足以下关系时才会被称为堆: ki <= k2i,ki <= k2i+1 或者 ki >= k2i,ki >= k2i+1 (i = 1,2,3,4 .. n/2) 如果数组的下表是从0开始,那么需要满足 ki <= k2i+1,ki <= k2i+2 或者 ki >= k2i+1,ki >= k2i+2 (i = 0,1,2,3 .. n

AC日记——二叉堆练习3 codevs 3110

3110 二叉堆练习3 时间限制: 3 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 给定N(N≤500,000)和N个整数(较有序),将其排序后输出. 输入描述 Input Description N和N个整数 输出描述 Output Description N个整数(升序) 样例输入 Sample Input 5 12 11 10 8 9 样例输出 Sample Output 8 9 10 11 12 数据范围及提示 Data Si