zoukankan      html  css  js  c++  java
  • 数据结构之堆

    原帖转自:http://www.cnblogs.com/Jason-Damon/archive/2012/04/18/2454649.html  

     对于堆的数据结构的介绍,在网上搜了下,具体讲的不是很多。发现比较好的一篇介绍堆的博客是http://dongxicheng.org/structure/heap/。在此感谢他。

        通过对上面那篇博客的学习,然后自己也去翻了下《算法导论》里面关于堆排序(heapsort)的介绍。这样就对堆有了更加深刻的认识,在此,我结合自己的一点点理解,主要还是基于上面那篇博客的内容(主要也是《算法导论》里的内容),也把他里面程序的一些错误改正,我把这篇博客写了出来,以加深对堆的认识,并供自己日后温习。后面我还要练习poj上几天关于堆的题目,链接会在后面给出。

    1.堆

        堆数据结构是一种数组对象,它可以被视为一科完全二叉树结构。它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶堆和小顶堆)。它常用于管理算法执行过程中的信息,应用场景包括堆排序优先队列等。

    2. 堆的基本操作

        堆是一棵完全二叉树,高度为O(lg n),其基本操作至多与树的高度成正比。在介绍堆的基本操作之前,先介绍几个基本术语:

     

        A:用于表示堆的数组,下标从1开始,一直到n

     

      PARENT(t):节点t的父节点,即floor(t/2)

     

      RIGHT(t):节点t的左孩子节点,即:2*t

     

        LEFT(t):节点t的右孩子节点,即:2*t+1

     

        HEAP_SIZE(A):堆A当前的元素数目

     3.保持堆的性质 Heapify(A,n,t)

     

         该操作主要用于维持堆的基本性质。假定以RIGHT(t)和LEFT(t)为根的子树都已经是堆,然后调整以t为根的子树,使之成为堆。

     

        

    复制代码
    void Heapify(int A[],int i)
    {
        int l=LEFT(i);
        int r=RIGHT(i);
        int largest;
        if(l<=HEAP_SIZE(A)) largest=A[l]>A[i]?l:i;
            if(r<=HEAP_SIZE(A)) largest=A[r]>A[largest]?r:largest;  //从i,2*i,2*i+1中找出最大的一个
            if(largest!=i)    //i不是最大的
        {
            swap(A[i],A[largest]);
            Heapify(A,largest);  //交换后,子树有可能违反最大堆性质
        }
    }
    复制代码

     


    4.建堆 BuildHeap(A,n)

        操作主要是将数组A转化成一个大顶堆。思想是,先找到堆的最后一个非叶子节点(即为第n/2个节点),然后从该节点开始,从后往前逐个调整每个子树,使之称为堆,最终整个数组便是一个堆。子数组A[(n/2)+1..n]中的元素都是树中的叶子,因此都可以看作是只含有一个元素的堆。具体的过程我觉得看《算法导论》里面的图的话理解应该很简单,我找不到那图。

    void BuildHeap(int A[],)  
     {    
                int i;
                for(i = HEAP_SIZE(A)/2; i>=1; i--)    
                   Heapify(A, i); 
      } 

     

    5.堆排序算法   

         先用BuildHeapo将数组A[1..n]构造成一个最大堆。因为数组中最大元素在根A[1],则可以通过把它与A[n]交换来达到最终正确的位置。

     

    复制代码
    void HeapSort(int A[])
    {
        BuildHeap(A);
        for(i=HEAP_SIZE(A),i>1; i--)
        {
            swap(A[1],A[i]);
            HEAP_SIZE(A)=HEAP_SIZE(A)-1;
            Heapify(A,1);  //交换后新的根元素可能委培了最大堆的性质
        }
    }
    复制代码

     

    6.优先队列  priority queue

        优先队列是一种用来维护由一组元素构成的集合S的数据机构。相信大家对它都有所了解。虽然说c++里面有了priority_queue,但我们还是要了解它的一些基本构成及实现的代码。

    GETMAX:

    该操作主要是获取堆中最大的元素,同时保持堆的基本性质。堆的最大元素即为第一个元素,将其保存下来,同时将最后一个元素放到A[1]位置,之后从上往下调整A,使之成为一个堆。

    复制代码
    void GetMaximum(int A[])   {
         int max = A[1];
         A[1] = A[n];
         HEAP_SIZE--;
         Heapify(A, n, 1);
         return max;
       } 
    复制代码

    INSERT:

    向堆中添加一个元素t,同时保持堆的性质。算法思想是,将t放到A的最后,然后从该元素开始,自下向上调整,直至A成为一个大顶堆。

    复制代码
    void Insert(int A[], int i)   {  //i为插入的值
        int n=++HEAP_SIZE(A);     
        A[n] = -99999;//小无穷
         int p = n;
         while(p >1 && A[PARENT(p)] < i)  {
           A[p] = A[PARENT(p)];
           p = PARENT(p);
          }
          A[p]=i;
    }
    复制代码

        总结。

         堆的最常见应用是堆排序,时间复杂度为O(N lg N)。如果是从小到大排序,用小顶堆;从大到小排序,用大顶堆。虽然堆排序是一个很漂亮的算法,但实际中,快排的一个好的实现往往优于堆排序。尽管这样,对数据结构还是有着很大的用处,比如说优先队列。

        例子:  在O(n lg k)时间内,将k个排序表合并成一个排序表,n为所有有序表中元素个数。

    【解析】取前100 万个整数,构造成了一棵数组方式存储的具有小顶堆,然后接着依次取下一个整数,如果它大于最小元素亦即堆顶元素,则将其赋予堆顶元素,然后用Heapify调整整个堆,如此下去,则最后留在堆中的100万个整数即为所求 100万个数字。该方法可大大节约内存。

       例子:一个文件中包含了1亿个随机整数,如何快速的找到最大(小)的100万个数字?(时间复杂度:O(n lg k))

    堆是一种非常基础但很实用的数据结构,很多复杂算法或者数据结构的基础就是堆,因而,了解和掌握堆这种数据结构显得尤为重要。

     


    二.基于堆的K路合并问题.

    题:请给出一下时间为O(n*lgk),用来将 k 个已排序链表合并为一个排序链表的算法.此处,n 次所有输入链表中元素的总数.

    答:新建一个链表,再申请一个大小为 k 的数组A,首先把 k 个已排序链表的第一个元素压入 A 中,将 A 建成一个最小堆,花费O(k) 的时间.然后将堆 A 的第一个元素 min(也就是最小的那个)放入链表中.再将min->nextNode 放在min的位置.再花O(lgk)调用heapify 方法将 A 重新建成一个最小堆.然后又将第一个元素 min 放入链表......重复进行就可将 k 个已排序链表合并.(当最后剩余不到 k 个节点时情况会有点变化,但很容易解决).显然,这样处理的时间复杂为 O(n*lgk);

  • 相关阅读:
    MDI窗体容器
    记事本制作、流的初步引用、窗口的三种显示类型
    ListVies控件的应用
    变量常量
    百度地图自定义离线地图
    通过配置文件方式修改 axios 请求地址
    百度地图坐标偏差
    Vue 全局指令限制输入框输入
    axios post请求发送数组
    WebSocket
  • 原文地址:https://www.cnblogs.com/catkins/p/5270605.html
Copyright © 2011-2022 走看看