【16】数据结构之基于树的排序算法篇章

目录标题

选择排序

  • 基本思想:从待排序的序列中选出最大值或最小值,交换该元素与待排序序列的头部元素,对剩下的元素重复操作,直到完成所有元素排序.

简单选择排序

  • 第i次排序通过n-i次关键字的比较,从n-i+1个元素中选出关键字最小的元素,并和第i个元素交换.共需进行i-1次比较,直到所有元素排序完成为止.
  • 示意图
  • 代码实现与调试
python 复制代码
# 树的排序--简单选择排序
class SelectSort(object):
    def __init__(self, items):
        # 待排序的序列
        self.items = items

    def selectSort(self):
        """
        简单选择排序
        :return:
        """
        # 共n轮排序
        for i in range(len(self.items)):
            # 待交换的元素
            index = i
            # 在未排序的序列中选取最小的元素
            for j in range(i+1, len(self.items)):
                if self.items[j] < self.items[index]:
                    index = j
            # 最小的元素不是带交换的元素,则两者交换
            if index != i:
                self.items[i],self.items[index] = self.items[index],self.items[i]
             
if __name__ == "__main__":
	arr = [84, 62, 35, 77, 55, 14, 35, 98]
    select = SelectSort(arr)
    select.selectSort()
    print(arr)

树形选择排序

  • 树形选择排序也称锦标赛排序.
  • 首先所有参加比赛的选手两两分组,每组产生一个胜利者,然后这些胜利者再两两分组进行比赛,每组产生一个胜利者,之后重复执行,直到最后只有一个胜利者为止.
  • 示意图
  • 代码实现与调试
python 复制代码
class TournamentSort:
    def __init__(self, arr):
        self.original = arr.copy()
        self.n = len(arr)
        # 初始化所有属性,确保空数组时属性存在
        self.leaf_size = 0
        self.tree_size = 0
        self.tree = []

        if self.n == 0:
            return  # 空数组直接返回

        # 计算扩展到下一个2的幂次方的叶子数量
        self.leaf_size = 1 << (self.n - 1).bit_length()
        self.tree_size = 2 * self.leaf_size - 1

        # 初始化树结构并用inf填充空位
        self.tree = [float('inf')] * self.tree_size
        # 将原始数据填充到叶子节点
        self.tree[-self.leaf_size: -self.leaf_size + self.n] = self.original

        # 构建初始锦标赛树(自底向上)
        for i in range(self.tree_size - 1, 0, -1):
            if i % 2 == 1:  # 只处理左子节点,避免重复计算
                parent = (i - 1) // 2
                self.tree[parent] = min(self.tree[i], self.tree[i + 1])

    def get_min(self):
        """获取当前最小值并重构树"""
        if self.n <= 0:
            return None

        winner = self.tree[0]
        # 查找叶子层中的最小值位置
        idx = self.tree_size // 2  # 叶子层起始索引
        while idx < self.tree_size:
            if self.tree[idx] == winner:
                break
            idx += 1

        # 标记该位置为已选中
        self.tree[idx] = float('inf')
        self.n -= 1

        # 自底向上更新树结构
        while idx > 0:
            parent = (idx - 1) // 2
            sibling = idx + 1 if idx % 2 == 1 else idx - 1
            self.tree[parent] = min(self.tree[idx], self.tree[sibling])
            idx = parent

        return winner

    def sort(self):
        """执行完整排序过程"""
        sorted_arr = []
        while True:
            val = self.get_min()
            if val is None:
                break
            sorted_arr.append(val)
        return sorted_arr

    def print_tree(self):
        """可视化打印树结构(调试用)"""
        print("当前锦标赛树结构:")
        if self.tree_size == 0:
            print("空树")
            return
        level = 0
        level_size = 1
        while level_size <= self.tree_size:
            start = 2 ** level - 1
            end = start + level_size
            print(f"Level {level}: {self.tree[start:end]}")
            level += 1
            level_size *= 2


# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":
	test_cases = [
        [3, 1, 4, 1, 5, 9, 2, 6],  # 常规测试
        [9, 3, 5, 2, 8],  # 奇数元素
        [5, 3, 8, 6, 2],  # 重复值测试
        [1],  # 单个元素
        []  # 空数组
    ]

    for i, case in enumerate(test_cases):
        print(f"\n测试用例 {i + 1}: {case}")
        ts = TournamentSort(case)
        ts.print_tree()  # 调试查看初始树结构

        sorted_result = ts.sort()
        print(f"排序结果: {sorted_result}")
        print("验证结果:", "正确" if sorted_result == sorted(case) else "错误")

堆排序

  • 对树形选择排序的一种改进

堆的定义Heap

  • 堆是二叉树的一种
  • 堆是完全二叉树
  • 堆中任意结点的值总是不大于或者不小于其双亲结点的值
  • 堆分为大根堆和小跟堆

小跟堆

  • 如果根结点存在左孩子结点,则根结点的值小于或等于左孩子结点的值
  • 如果根结点存在右孩子结点,则根结点的值小于或等于右孩子结点的值
  • 根结点的左右子树也是小跟堆
  • 小跟堆示意图

大根堆

  • 如果根结点存在左孩子结点,则根结点的值大于或等于左孩子结点的值
  • 如果根结点存在右孩子结点,则根结点的值大于或等于右孩子结点的值
  • 根结点的左右子树也是大跟堆
  • 大跟堆示意图

堆的存储

  • 常用顺序结构进行存储.
  • 重建堆方法
    • 首先将完全二叉树根结点中的元素移出,
    • 从空结点的左右孩子结点中选出一个关键字较小的元素,上移到空结点中,
    • 当前那个较小关键字较小的子结点相当于空结点,
    • 重复上述移动原则
    • 直到空结点的左右孩子结点的关键字均不小于待调整元素的关键字为止
  • 建立初始堆算法思路
    • 一个任意序列可以看成对应的完全二叉树,由于叶子结点可以视为单元素的堆,然后反复利用重建堆方法,自底向上逐层把所有子树调整为堆,直到将整个完全二叉树调整为堆为止

堆的代码设计

python 复制代码
class Heap:
    def __init__(self, items=[]):
        self.items = items.copy()
        self.build_min_heap()

    def build_min_heap(self):
        """将列表初始化为小根堆"""
        length = len(self.items)
        for i in range(length // 2 - 1, -1, -1):
            self._sift_down(i, length)

    def insert(self, key):
        """插入新元素并上浮调整"""
        self.items.append(key)
        self._sift_up(len(self.items) - 1)

    def delete(self):
        """删除堆顶元素"""
        if not self.items:
            raise IndexError("堆为空,无法删除")

        # 将最后一个元素移到堆顶
        removed = self.items[0]
        self.items[0] = self.items[-1]
        self.items.pop()

        # 下沉调整
        if self.items:
            self._sift_down(0, len(self.items))
        return removed

    def _sift_up(self, index):
        """元素上浮操作"""
        parent = (index - 1) // 2
        while parent >= 0 and self.items[index] < self.items[parent]:
            self.items[index], self.items[parent] = self.items[parent], self.items[index]
            index = parent
            parent = (index - 1) // 2

    def _sift_down(self, index, size):
        """元素下沉操作"""
        smallest = index
        left = 2 * index + 1
        right = 2 * index + 2

        if left < size and self.items[left] < self.items[smallest]:
            smallest = left
        if right < size and self.items[right] < self.items[smallest]:
            smallest = right

        if smallest != index:
            self.items[index], self.items[smallest] = self.items[smallest], self.items[index]
            self._sift_down(smallest, size)

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


# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":
    # 测试初始化与插入删除
    heap = Heap([10, 25, 15, 40, 25, 20, 30, 50, 55])
    print("初始堆:", heap)  # 输出: [10, 25, 15, 40, 25, 20, 30, 50, 55]

    heap.insert(9)
    print("插入9后:", heap)  # 输出: [9, 25, 10, 40, 55, 15, 30, 50, 25, 20]

    val = heap.delete()
    print(f"删除元素 {val} 后:", heap)  # 删除9后:[10, 25, 15, 40, 55, 20, 30, 50, 25]

    val = heap.delete()
    print(f"删除元素 {val} 后:", heap)  # 删除10后:[15, 25, 20, 40, 25, 55, 30, 50]

    # 测试空堆
    empty_heap = Heap()
    try:
        empty_heap.delete()
    except IndexError as e:
        print("空堆删除测试:", e)  # 输出:堆为空,无法删除

    # 测试单元素堆
    single_heap = Heap([5])
    single_heap.delete()
    print("单元素堆删除后:", single_heap)  # 输出:[]

堆排序的代码设计

python 复制代码
class HeapSort:
    def __init__(self, items):
        self.items = items  # 初始化待排序数组

    def heapSort(self):
        """堆排序主方法"""
        length = len(self.items)

        # 1. 构建最大堆(从最后一个非叶子节点开始调整)
        for i in range(length // 2 - 1, -1, -1):
            self._adjust_max_heap(self.items, i, length)

        # 2. 排序阶段(依次将堆顶元素移到末尾)
        for i in range(length - 1, 0, -1):
            # 交换堆顶(最大值)与当前末尾元素
            self.items[0], self.items[i] = self.items[i], self.items[0]
            # 调整剩余堆结构(堆大小逐渐减小)
            self._adjust_max_heap(self.items, 0, i)

    def _adjust_max_heap(self, arr, root, heap_size):
        """调整最大堆(递归实现)"""
        largest = root  # 初始化最大值为根节点
        left = 2 * root + 1  # 左子节点索引
        right = 2 * root + 2  # 右子节点索引

        # 比较左子节点
        if left < heap_size and arr[left] > arr[largest]:
            largest = left

        # 比较右子节点
        if right < heap_size and arr[right] > arr[largest]:
            largest = right

        # 如果最大值位置变化,交换并递归调整子树
        if largest != root:
            arr[root], arr[largest] = arr[largest], arr[root]
            self._adjust_max_heap(arr, largest, heap_size)


# ---------------------- 测试用例 ----------------------
if __name__ == "__main__":
    nums = [40, 55, 73, 12, 98, 27]
    sorter = HeapSort(nums)
    sorter.heapSort()
    print("排序结果:", nums)  # 输出: [12, 27, 40, 55, 73, 98]

排序算法综合比较

相关推荐
林泽毅几秒前
UNet脑瘤医学影像分割训练实战(PyTorch 完整代码)
深度学习·算法·机器学习
栩栩云生8 分钟前
📥 x-cmd install | Toolong - 终端日志分析的瑞士军刀
运维·python·数据分析
前端小干将9 分钟前
安装python
python
星辰大海的精灵16 分钟前
Python 中利用算法优化性能的方法
后端·python
意.远17 分钟前
PyTorch实现权重衰退:从零实现与简洁实现
人工智能·pytorch·python·深度学习·神经网络·机器学习
会飞的土拨鼠呀19 分钟前
SP B\nRebuild Priorit> 如何用python去掉\n
开发语言·windows·python
珊瑚里的鱼24 分钟前
【双指针】专题:LeetCode 202题解——快乐数
开发语言·c++·笔记·算法·leetcode·职场和发展
David Bates31 分钟前
代码随想录第18天:二叉树
python·算法·二叉树
想成为配环境大佬36 分钟前
P8739 [蓝桥杯 2020 国 C] 重复字符串
算法·蓝桥杯·贪心
王磊鑫36 分钟前
重返JAVA之路——图书管理系统
java·开发语言