zoukankan      html  css  js  c++  java
  • 【排序】堆排序

    什么是堆

    堆是一棵完全二叉树,可以用数组来存储。比如一个数组[3, 8, 15, 31, 24],具体为一个堆,它的逻辑结构如下所示:(图来自https://www.cnblogs.com/jingmoxukong/p/4303826.html#堆的概念,侵删)

    #### 最大堆和最小堆
    • 最大堆:根结点的值是所有堆结点值中最大者,且以每个节点为根的所有子堆都为最大堆。
    • 最小堆:根结点的值是所有堆结点值中最小者,且以每个节点为根的所有子堆都为最小堆。

    可以从图中发现最大堆/最小堆中,一个节点值的大小与它所在的深度无关,只与该节点的父节点有关。更简单说,对于最大堆,从某一个节点往下直到叶子节点,途中获得的值都在减少,比如[7, 6, 4].

    堆的性质

    由于堆在逻辑结构上实际为一棵完全二叉树,所以一个节点下标为p的两个子节点分别为:

    1. 2 * p + 1
    2. 2 * p + 2

    如何将堆调整为最大堆

    1. 上面已经提到最大堆是长什么样子的了,下面给出调整最大堆的过程图:

    2. 用文字描述一下调整过程:

      记堆的数据存放在一个数组nums中,N为数组的长度
      1. 获取树中最后一个不是叶子节点的那个节点下标p
      2. 开始循环
          while p != 0:
              # PercDown是一个函数,用于调整以nums[p]为根的子堆到最大堆
              PercDown(nums, p, N)
              p--
      
    3. 那么又有一个问题,PercDown(nums, p, N)函数怎么工作的:

    PercDown函数的代码

    void PercDown( int A[], int p, int N )
    { 
        /* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */
        int Parent, Child;
        int X;
    
        /* 取出根结点存放的值 */
        X = A[p]; 
        for (Parent = p; Parent*2+1 < N; Parent = Child) {
            Child = Parent * 2 + 1;     // 左节点下标
            if( (Child!=N-1) && (A[Child]<A[Child+1]) )
                Child++;  /* Child指向左右子结点的较大者 */
            if( X >= A[Child] ) break; /* 找到了合适位置 */
            else  /* 下滤X */
                A[Parent] = A[Child];
        }
        A[Parent] = X;
    }
    

    利用最大堆排序

    那么已经调整好最大堆,怎么排序呢?聪明的办法是将nums[0]和nums最后一个数交换,然后将最大的那个数排除在nums外(可以用减少个数之类的办法),在利用PercDown调整以nums[0]为根的子堆将剩余的数重新调整为最大堆,还是上个图:

    完整代码

    #include <iostream>
    using namespace std;
    
    void Swap( int *a, int *b )
    {
         int t = *a; 
         *a = *b; 
         *b = t;
    }
     
    void PercDown( int nums[], int p, int N )
    { 
        /* 将N个元素的数组中以nums[p]为根的子堆调整为最大堆 */
        int Parent, Child;
        int X;
    
        /* 取出根结点存放的值 */
        X = nums[p]; 
        for (Parent = p; Parent*2+1 < N; Parent = Child) {
            Child = Parent * 2 + 1;
            if ((Child!=N-1) && (nums[Child] < nums[Child+1]) )
                Child++;  /* Child指向左右子结点的较大者 */
    
            /* 找到了合适位置退出 */
            if (X >= nums[Child]) 
                break; 
            /* 下滤X */
            else  
                nums[Parent] = nums[Child];
        }
        nums[Parent] = X;
    }
    
    void HeapSort(int nums[], int N) 
    { 
        /* 堆排序入口 */
        int i;
        
        /* 建立最大堆 */
        for (i = N/2-1; i >= 0; i--)
            PercDown(nums, i, N);
        
        for (i=N-1; i>0; i--) 
        {
            /* 删除最大堆顶 */
            Swap(&nums[0], &nums[i]); 
            PercDown(nums, 0, i );
        }
    }
    
    int main()
    {
        int nums[] = {4,3,1,2};
        HeapSort(nums, 4);
        for(int i : nums)
            cout << i << ' ';
    }
    
  • 相关阅读:
    centos7物理机a start job is running for dev-mapper-centosx2dhome.device
    jenkins pipeline流水线
    nginx 加载慢 负载均衡不均衡
    山田预发环境发布脚本
    prometheus 监控容器
    maven私服安装使用
    日志清理
    ERROR 1046 (3D000) at line 1: No database selected
    网络工程学习经典书籍推荐
    每日一句
  • 原文地址:https://www.cnblogs.com/shayue/p/10426127.html
Copyright © 2011-2022 走看看