简单易懂堆排序

# 什么是堆?

  1.堆是一个完全二叉树

  2.任意非叶子节点的值大于等于(大顶堆)或小于等于(小顶堆)其左右孩子的值.


   这就说明根元素的值是堆中最大或最小的.


    简单易懂堆排序_第1张图片

      如果我们输出堆顶元素, 然后对剩下的元素重新调整为一个堆, 如此直到输出所有元素, 便可完成排序了.

      但是有个问题, 初始给定的数据不是一个堆啊, 所以我们要先将其初始化为堆.


      因此, 实现堆排序需要解决两个问题: 1. 将一个无序序列构建为一个堆        2.输出堆顶元素后, 调整堆.


# 通常的结构

简单易懂堆排序_第2张图片

   物理结构为一个数组, 逻辑结构为完全二叉树.

   用 n 表示最大下标则:

   array[0] 为堆顶

   array[i] 的左孩子为 array[2*i+1], 右孩子为 array[2*i+2]          i >= 0 && i <= (n-1)/2


# 堆排序过程

   1. 怎么构建一个堆呢?

       也就是说我们要将一个最大/最小的元素放到堆顶

       那就在堆顶 array[0], 左子树 array[1], 右子树 array[2] 中"选择"一个最大的放到 array[0]

       那左子树 array[1] 的最大元素呢? 我想聪明的你应该想到了, 继续同样的操作就行了.

       这里的"选择"就是一个调整堆的过程. 所以现在我们将问题转化为调整堆了.

   2. 调整堆

       我们给定序列 8, 9, 6, 7, 9, 7

       创建的大顶堆如下, 这时 array[0] 即为此序列的最大元素

简单易懂堆排序_第3张图片

       输出堆顶元素(和序列最后一个元素交换位置)

  简单易懂堆排序_第4张图片

     这时就破坏了大顶堆, 我们先调整(6, 9, 7), 选择一个最大的与根交换

     由于交换又破坏了左子树, 那就继续调整, 直到所以遭破坏的树调整完毕即可

     有可能破坏的树只有交换后的子树.

简单易懂堆排序_第5张图片

           输出堆顶元素(交换), 此时堆顶元素为序列中次大的元素

简单易懂堆排序_第6张图片

              同样调整堆

简单易懂堆排序_第7张图片

           继续输出

简单易懂堆排序_第8张图片

          调整

简单易懂堆排序_第9张图片

            交换输出

简单易懂堆排序_第10张图片

         调整

简单易懂堆排序_第11张图片

           交换输出, 只剩下一个元素, 此时排序完成

简单易懂堆排序_第12张图片

          如此, 我们可以看出大顶堆完成是从小到大的排序

          稍微想一下, 小顶堆则完成的是从大到小的排序


      此时我们再看创建大顶堆的过程, 就比较清楚了. 依次队非叶子节点调整

简单易懂堆排序_第13张图片

  # 代码

static void adjust_heap(int *keys, int first, int last) {
    int root, child;
    
    // child 开始为左孩子
    for (root = first; (child = root * 2 + 1) <= last; root = child) {
        // 在左右孩子中选择一个最大的, 此时最大的元素下标为 child
        if (child + 1 < last && keys[child+1] > keys[child]) {
            child = child + 1;
        }
        // 如果孩子中最大的比父节点大, 则与父节点交换, 并以此孩子节点为根继续调整 root = child
        if (keys[child] > keys[root]) {
            swap(&keys[child], &keys[root]);
        // 如果在没交换的情况下, 父节点就是最大的, 则无需再继续调整
        } else {
            break;
        }
    }
}
      
    void heap_sort(int *keys, int len) {  
        int i;  
      
        // 使len为最大索引  
        len -= 1;  
      
        // 构造大顶堆, 此时保证 keys[0] 为最大元素  
        for (i = (len-1)/ 2; i >= 0; --i)  
            adjust_heap(keys, i, len);  
          
        for (i = len; i > 0; ) {  
            // 将 keys[0] 放到尾部,  
            swap(&keys[i], &keys[0]);  
      
            // 移除最大元素, 调整堆   
            adjust_heap(keys, 0, --i);  
        }  
    }  


你可能感兴趣的:(简单易懂堆排序)