堆排序
列表实现
知识点
- 完全二叉树可以使用列表形式存储
- 根节点查找左子节点: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]