zoukankan      html  css  js  c++  java
  • Binary Heap(二叉堆)

      这篇的主题主要是Heapsort(堆排序),下一篇ADT数据结构随笔再谈谈 - 优先队列(堆)。

      首先,我们先来了解一点与堆相关的东西。堆可以实现优先队列(Priority Queue),看到队列,我们马上就想到了队列的一个特点 - 先进先出(FIFO - first in first out),但优先队列有些不同的地方,优先队列是一种具有优先级先出的数据结构。

      堆的结构:

    typedef int ElemType;
    
    typedef struct
    {
        ElemType * arr;
        int size;
    }Heap;

      和数组栈的结构类似。

      初始化堆:

    Heap * InitHeap(ElemType * nums, int ArraySize)
    {
        Heap * heap;
    
        heap = (Heap *)malloc(sizeof(Heap));
        heap -> arr = (ElemType *)malloc(sizeof(ElemType));
        for(heap -> size = 0; heap -> size < ArraySize; heap -> size ++)
            heap -> arr[heap -> size] = nums[heap -> size];
        
        return heap;
    }

      找节点的关系:

    /*
     *    节点i的双亲
     */
    static int HeapParent(int i)
    {
        return i/2;
    }
    
    /*
     *    节点i的左孩子
     */
    static int HeapLeft(int i)
    {
        return 2*i + 1;
    }
    
    /*
     *    节点i的右孩子
     */
    static int HeapRight(int i)
    {
        return 2*(i + 1);
    }

      这里要保持的是一个大根堆:

    /*
     *    维护的最大堆(或最小堆)的性质
     *    参数说明:
     *      1.接收一个已存在的堆
     *      2.节点位置
     *        无返回值
     */
    void Max_Heapify(Heap * heap, int i)
    {
        int _L = HeapLeft(i);
        int _R = HeapRight(i);
        int largest;
        int temp;
    
        if(_L < heap -> size && heap -> arr[_L] > heap -> arr[i])
            largest = _L;
        else
            largest = i;
        if(_R < heap -> size && heap -> arr[_R] > heap -> arr[largest])
            largest = _R;
        if(largest != i)
        {
            temp = heap -> arr[i];
            heap -> arr[i] = heap -> arr[largest];
            heap -> arr[largest] = temp;
            Max_Heapify(heap, largest);
        }
    }

      建大根堆:

    static void Build_Max_Heap(Heap * heap)
    {
        int i;
    
        for(i = heap -> size/2; i >= 0; i--)
            Max_Heapify(heap, i);
    }

      排序:

    void HeapSort(Heap * heap)
    {
        int i;
        int temp;
    
        //维护堆,把初始建立的混乱的堆变为大根堆,以便进行排序
        Build_Max_Heap(heap);
        for(i = heap -> size - 1; i >= 0; i--)
        {
            temp = heap -> arr[0];
            heap -> arr[0] = heap -> arr[i];
            heap -> arr[i] = temp;
            -- heap -> size;        //每次把具有最小值的节点交换到根节点上,则把需要维护的堆的大小减小
            Max_Heapify(heap, 0);   //交换根节点后的堆的性质变了,需要不断维护
        }
    }

      至此,用二叉堆对一个数组的排序就完成了。

      然后,测试一下:

    int main()
    {
        Heap * heap;
        ElemType nums[] = {5, 13, 2, 25, 7, 17, 20, 8, 4}, * arr = nums;
        int arrSize = sizeof(nums)/sizeof(int);
        int i;
    
        heap = InitHeap(arr, arrSize);
    
        HeapSort(heap);
    
        for(i = 0; i < arrSize; i++)
            printf("%d ", heap -> arr[i]);
        printf("
    ");
    
        return 0;
    }

      运行结果:

    2 4 5 7 8 13 17 20 25

      完整的堆排序代码:

    /**
     *  堆排序(Heap Sort)
     */
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef int ElemType;
    
    typedef struct
    {
        ElemType * arr;
        int size;
    }Heap;
    
    Heap * InitHeap();
    static int HeapParent();
    static int HeapLeft();
    static int HeapRight();
    void Max_Heapify();
    static void Build_Max_Heap();
    void HeapSort();
    
    /*
     *    初始化堆
     *
     *    参数说明:
     *        1.传入数组
     *        2.数组大小
     *
     *    返回值:堆
     */
    Heap * InitHeap(ElemType * nums, int ArraySize)
    {
        Heap * heap;
    
        heap = (Heap *)malloc(sizeof(Heap));
        heap -> arr = (ElemType *)malloc(sizeof(ElemType));
        for(heap -> size = 0; heap -> size < ArraySize; heap -> size ++)
            heap -> arr[heap -> size] = nums[heap -> size];
    
        return heap;
    }
    
    /*
     *    某节点i的双亲
     */
    static int HeapParent(int i)
    {
        return i/2;
    }
    
    /*
     *    某节点i的左孩子
     */
    static int HeapLeft(int i)
    {
        return 2*i + 1;
    }
    
    /*
     *    某节点i的右孩子
     */
    static int HeapRight(int i)
    {
        return 2*(i + 1);
    }
    
    /*
     *    维护堆
     */
    void Max_Heapify(Heap * heap, int i)
    {
        int _L = HeapLeft(i);
        int _R = HeapRight(i);
        int largest;
        int temp;
    
        if(_L < heap -> size && heap -> arr[_L] > heap -> arr[i])
            largest = _L;
        else
            largest = i;
        if(_R < heap -> size && heap -> arr[_R] > heap -> arr[largest])
            largest = _R;
        if(largest != i)
        {
            temp = heap -> arr[i];
            heap -> arr[i] = heap -> arr[largest];
            heap -> arr[largest] = temp;
            Max_Heapify(heap, largest);
        }
    }
    
    /*
     *    建堆
     */
    static void Build_Max_Heap(Heap * heap)
    {
        int i;
    
        for(i = heap -> size/2; i >= 0; i--)
            Max_Heapify(heap, i);
    }
    
    /*
     *    排序
     */
    void HeapSort(Heap * heap)
    {
        int i;
        int temp;
    
        Build_Max_Heap(heap);            //维护堆,把初始建立的混乱的堆变为大根堆,以便进行排序
        for(i = heap -> size - 1; i >= 0; i--)
        {
            temp = heap -> arr[0];
            heap -> arr[0] = heap -> arr[i];
            heap -> arr[i] = temp;
            -- heap -> size;            //每次把具有最小值的节点交换到根节点上,则把需要维护的堆的大小减小
            Max_Heapify(heap, 0);        //交换根节点后的堆的性质变了,需要不断维护
        }
    }
    
    int main(void)
    {
        Heap * heap;
        ElemType nums[] = {5, 13, 2, 25, 7, 17, 20, 8, 4}, * arr = nums;
        int arrSize = sizeof(nums)/sizeof(int);
        int i;
    
        heap = InitHeap(arr, arrSize);
    
        HeapSort(heap);
    
        for(i = 0; i < arrSize; i++)
            printf("%d ", heap -> arr[i]);
        printf("
    ");
    
        return 0;
    }
    

      这里的堆显然是一个二叉堆,因此可以把它想象成二叉树的样子来理解,关于整个堆排序的过程,它的时间复杂度为O(nlgn),因为调用Build_Max_Heap()的代价为O(n),而n - 1次调用Max_Heapify()的代价为O(lgn)。

      但至于为什么Build_Max_Heap()函数的时间复杂的只有O(n)...看起来应该是O(nlgn),我也还没完全理解这里的时间复杂度的计算...留个坑慢慢理解...懂了之后再补充。

      前面提到了堆的结构型和数组栈相似,实际上,堆与队列和栈有很大的关系,我们知道队列是FIFO,栈是FILO,从上面堆的维护操作等就可以看出,堆还有着具有优先级先出的特性(注意与栈并不同),优先队列是基于最大/小堆实现的,虽然名字中有个队列,但区别还是很大的。

  • 相关阅读:
    ELK--filebeat命令行参数解释
    ELK--filebeat详解
    centOS7 修改DNS
    nginx-日志统计
    ceph 安装ceph问题汇总
    正则 挖网站表格复习
    c#反射优化 表达式树
    combotree 满足条件的节点不可选中
    NHibernate获取实体配置信息(表名,列名等等)
    jqgrid 单元格放超链接文本
  • 原文地址:https://www.cnblogs.com/darkchii/p/7591093.html
Copyright © 2011-2022 走看看