简单易懂堆排序

# 什么是堆?

  1.堆是一个完全二叉树

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


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


   

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

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


      因此, 实现堆排序需要解决两个问题: 1. 将一个无序序列构建为一个堆        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] 即为此序列的最大元素

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

 

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

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

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

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

              同样调整堆

           继续输出

          调整

            交换输出

         调整

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

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

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


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


  # 代码

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);  
        }  
    }  


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼编码是一种可变长度编码,它通过对字符进行不等长编码来压缩数据。对于出现频率较高的字符,它们的编码比较短,而出现频率较低的字符则编码比较长。这种编码方法可以有效地减少数据的存储空间。下面是一个简单易懂的哈夫曼编码的示例代码: ```python import heapq from collections import defaultdict # 哈夫曼树节点类 class Node: def __init__(self, freq, char=None): self.freq = freq self.char = char self.left = None self.right = None # 定义小于号操作符,用于堆排序 def __lt__(self, other): return self.freq < other.freq # 构建哈夫曼树 def build_huffman_tree(freq_dict): heap = [] for char, freq in freq_dict.items(): heapq.heappush(heap, Node(freq, char)) while len(heap) > 1: node1 = heapq.heappop(heap) node2 = heapq.heappop(heap) new_node = Node(node1.freq + node2.freq) new_node.left, new_node.right = node1, node2 heapq.heappush(heap, new_node) return heap # 递归遍历哈夫曼树,并生成编码表 def traverse_huffman_tree(node, code_table, code=''): if node.char: code_table[node.char] = code else: traverse_huffman_tree(node.left, code_table, code + '0') traverse_huffman_tree(node.right, code_table, code + '1') # 哈夫曼编码主函数 def huffman_encode(string): freq_dict = defaultdict(int) for char in string: freq_dict[char] += 1 root = build_huffman_tree(freq_dict) code_table = {} traverse_huffman_tree(root, code_table) encoded_string = '' for char in string: encoded_string += code_table[char] return encoded_string, code_table # 哈夫曼解码主函数 def huffman_decode(encoded_string, code_table): decoded_string = '' current_code = '' for bit in encoded_string: current_code += bit for char, code in code_table.items(): if code == current_code: decoded_string += char current_code = '' break return decoded_string ``` 以上代码中,`build_huffman_tree`函数用于构建哈夫曼树;`traverse_huffman_tree`函数用于遍历哈夫曼树,并生成编码表;`huffman_encode`函数用于对输入字符串进行哈夫曼编码;`huffman_decode`函数用于对编码后的字符串进行解码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值