堆排序(Python实现)

堆排序

列表实现

知识点

  • 完全二叉树可以使用列表形式存储
  • 根节点查找左子节点:i_left = 2 ** i_root + 1
  • 根节点查找右子节点:i_right = 2 ** i_root + 2
  • 子节点查找父节点:i_root = int(i_child // 2)
import math
import random
#
# li = [1,
#       2, 10,
#       0, 3, 19, 8,
#       90, 91, 93, 95, 99, 100, 101, 102,
#       103,
#       ]


def print_tree(list_tree):
    """
    打印树结构
    sum_n = 2 ** n -1
    """
    tree = list_tree[::]
    size = len(tree)
    n = math.log2(size + 1)
    # print(n)
    n = math.ceil(n)
    # print("层", n)
    current_n = 1
    for i, v in enumerate(tree):
        if i != 0:
            print(f"{v:-6d}", end="")
        if i + 1 == (2 ** current_n - 1) and i != 0:
            print("\n" + "   " * (n - current_n), end="")
            current_n += 1
        elif i == 0:
            print("   " * (n - current_n + 1) + f"{v:-6d}", end="")
            current_n += 1

        if i == 0:
            print("\n" + "   " * (n - current_n + 1), end="")
    print()


def min_down(li, high, lower):
    """
    li列表
    将值小的根节点调整到叶子节点
    high根节点列表下标,为了能够对不同子树进行操作
    lower最末尾子节点的列表下标
    """
    # size = len(li)
    root = li[high]
    left, right = None, None
    left_i = 2 * high + 1
    right_i = 2 * high + 2
    if left_i <= lower:
        left = li[left_i]
    if right_i <= lower:
        right = li[right_i]

    if left is not None and right is not None:
        if root < left or root < right:
            if left < right:
                # 交换 根 与 右子树
                li[high] = right
                li[right_i] = root
                min_down(li, right_i, lower)
            else:
                # 交换 根 与 左子树
                li[high] = left
                li[left_i] = root
                min_down(li, left_i, lower)

    elif left is not None and left > root:
        # 交换 根 与 左子树
        li[high] = left
        li[left_i] = root
        min_down(li, left_i, lower)

    elif right is not None and right > root:
        # 交换 根 与 右子树
        li[high] = right
        li[right_i] = root
        min_down(li, right_i, lower)


def to_heap(tree):
    """
    tree 列表
    从列表生成从大到小的二叉堆
    """
    size = len(tree)
    n = math.log2(size + 1)
    # print(n)
    n = math.ceil(n)
    # print(f"层数:{n}")
    for i in range(len(tree) - 1, -1, -1):
        # print(i)
        min_down(tree, i, len(tree) - 1)
        # print_tree(li)


def heap_sort(li: list):
	# 堆排序
    to_heap(li) # 将列表变成一个堆
    # print_tree(li)
    size = len(li)
    # 将更节点即最大值,依次放到列表后面
    for i in range(size - 1, 0, -1):
        lower = i
        # 交换根节点与末尾节点值
        li[0], li[lower] = li[lower], li[0]
        # 重新调整,保持堆结构
        min_down(li, 0, lower - 1)
        # print_tree(li)


if __name__ == "__main__":
	# 生成随机列表
	size = random.randint(10, 20)
    li = [random.randint(0, 999) for i in range(size)]
    print("======= 排序前 ========")
    print(li)
    print_tree(li)
    print("======= 排序后 ========")
    heap_sort(li)
    print(li)
    print_tree(li)
======= 排序前 ========
[554, 70, 923, 622, 430, 967, 146, 42, 871, 398]
               554
             70   923
         622   430   967   146
       42   871   398
======= 排序后 ========
[42, 70, 146, 398, 430, 554, 622, 871, 923, 967]
                42
             70   146
         398   430   554   622
      871   923   967

自定义完全二叉树类实现堆排序

其实没必要使用指针形式实现完全二叉树然后在进行堆排序,不过还是随便写写

import math
import random


class BinaryTreeNode:
	# 二叉树节点
    def __init__(self, key: int, value=None, root=None, left=None, right=None):
        self.key = key
        self.value = value
        self.root = root
        self.left = left
        self.right = right

    def __str__(self):
        return f"{self.key}: {self.value}"


class CompleteBinaryTree:
    """
    完全二叉树
    """

    def __init__(self, root: BinaryTreeNode = None):
        self.root = root

    def get_last_node(self):
        if not self.root:
            return None
        queue = [self.root]
        node = None
        while queue:
            node = queue.pop(0)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return node

    def add(self, node):
        if not self.root:
            self.root = node
            # print(f"root add: {node.key}")
            return

        queue = [self.root]
        while queue:
            current_node = queue.pop(0)
            if not current_node.left and not current_node.right:
                current_node.left = node
                node.root = current_node

                # print(f"left add: {node.key} root: {current_node.key}")
                return

            elif current_node.left and not current_node.right:
                current_node.right = node
                node.root = current_node

                # print(f"right add: {node.key} root: {current_node.key}")
                return

            if current_node.left:
                queue.append(current_node.left)
            if current_node.right:
                queue.append(current_node.right)

    def get_high(self):
        if not self.root:
            return -1
        high = 0
        root = self.root
        if root.left:
            high += 1
        return high

    def to_list(self):
        if not self.root:
            return []
        queue = [self.root]
        li = []
        while queue:
            node = queue.pop(0)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
            li.append(node.key)
        return li

    @classmethod
    def min_dow(cls, root: BinaryTreeNode = None):
        if not root:
            return

        if root.left and root.right:
            if root.key < root.left.key or root.key < root.right.key:
                if root.left.key < root.right.key:
                    root.right.key, root.key = root.key, root.right.key
                    root.right.value, root.value = root.value, root.right.value

                    cls.min_dow(root.right)
                else:
                    root.left.key, root.key = root.key, root.left.key
                    root.left.value, root.value = root.value, root.left.value

                    cls.min_dow(root.left)

        elif root.left and root.key < root.left.key:
            root.left.key, root.key = root.key, root.left.key
            root.left.value, root.value = root.value, root.left.value

            cls.min_dow(root.right)

        elif root.right and root.key < root.right:
            root.right.key, root.key = root.key, root.right.key
            root.right.value, root.value = root.value, root.right.value

            cls.min_dow(root.right)

    def to_heap(self):
        """
        变成一个堆
        """
        if not self.root:
            return
        queue = [self.root]
        nodes = []
        while queue:
            node = queue.pop(0)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
            nodes.append(node)

        for node in nodes[::-1]:
            self.min_dow(node)

    def heap_pop(self):
        if not self.root:
            return None
        if not self.root.left and not self.root.right:
            root = self.root
            self.root = None
            return root

        node = self.get_last_node()
        # print("last_node:", node.key)
        # 交换根与最后一个节点值
        self.root.key, node.key = node.key, self.root.key
        self.root.value, node.value = node.value, self.root.value

        # 删除最后一个节点
        if node.root.left is node:
            node.root.left = None
            # print("===")
        elif node.root.right is node:
            node.root.right = None
            # print("---")
        self.min_dow(self.root)
        return node

    def print(self):
        """
        分层打印树
        """
        if not self.root:
            print(None)
        # high = self.get_high()
        layer = 0
        num = 0
        queue = [self.root]
        while queue:
            node = queue.pop(0)

            print(node.key, end=" ")
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

            num += 1
            if num == 2 ** layer:
                num = 0
                layer += 1
                print()

        print()

    def __str__(self):
        return str(self.to_list())

    def __len__(self):
        return len(self.to_list())


if __name__ == "__main__":
    size = random.randint(1, 20)
    li = [random.randint(0, 999) for i in range(size)]

    cbt = CompleteBinaryTree()
    for i in li:
        cbt.add(BinaryTreeNode(i))

    print(cbt)
    cbt.print()

    cbt.to_heap()
    print("=" * 20)
    cbt.print()
    new_list = []
    for i in cbt.to_list():
        node = cbt.heap_pop()
        if node:
            new_list.append(node.key)
    new_list = new_list[::-1]
    print("=" * 20)
    print(new_list)
    print(cbt)


# 排序前
[255, 967, 192, 432, 573, 463, 168, 991, 509, 270, 800, 122, 9, 747, 308, 860, 872, 521]
255 
967 192 
432 573 463 168 
991 509 270 800 122 9 747 308 
860 872 521 
====================
# 堆结构
991 
967 747 
872 800 463 308 
860 521 270 573 122 9 168 192 
255 432 509 
====================
排序后
[9, 122, 168, 192, 255, 270, 308, 432, 463, 509, 521, 573, 747, 800, 860, 872, 967, 991]
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值