当前位置: 首页 > article >正文

面试准备-排序部分:快速排序、堆排序

快速排序

快速排序是一种基于**分治思想(Divide and Conquer)**的排序算法。其核心思想是:

  1. 选择一个基准元素(pivot),通常是数组中的某个元素(如最左/最右元素、中间元素或随机选择)。
  2. 通过**分区(partition)**操作,将小于基准的元素放到基准左侧,大于基准的元素放到基准右侧。
  3. 对左右两个子数组递归地进行快速排序,直到子数组长度为1或0(即已排序)。

快排的时间复杂度取决于基准选择的好坏:

最优情况(O(n log n)):每次都能将数组均匀划分,递归深度为log n,每层的分区操作是 O(n),所以总复杂度是 O(n log n)最坏情况(O(n²)):如果每次选择的基准导致极端不均匀划分(如已排序数组选择最左/最右元素),递归深度达到 O(n),每层 O(n),总复杂度 O(n²)

平均情况(O(n log n)):随机选取基准时,通常能保证 O(n log n) 的复杂度。

快排的空间复杂度:

**原地排序版本(Hoare 版)**的快速排序只需要 O(1) 的额外空间。递归调用栈的空间复杂度为 O(log n)(理想情况)到 O(n)(最坏情况)。

改进措施:采用尾递归优化,可以减少栈的使用深度。

快速排序是不稳定的,因为交换操作可能改变相同元素的相对位置

代码:

def qsort(arr: list, l: int, r: int) -> None:
    if l >= r:  # 递归终止条件,当子数组长度 ≤ 1 时返回
        return  
    # 选择中间元素作为 pivot(基准值)
    i, j, p = l, r, arr[(l + r) >> 1]
    # 分区操作
    while i <= j:
        # 从左向右找到第一个不小于 pivot 的元素
        while arr[i] < p: 
            i += 1
        # 从右向左找到第一个不大于 pivot 的元素
        while arr[j] > p: 
            j -= 1
        # 当 i <= j 时,交换两侧的元素
        if i <= j:
            arr[i], arr[j] = arr[j], arr[i]  # 交换元素,使得左侧小于 pivot,右侧大于 pivot
            i += 1  # 左指针右移
            j -= 1  # 右指针左移
    # 递归处理左子数组
    if l < j: qsort(arr, l, j)
    # 递归处理右子数组
    if i < r: qsort(arr, i, r)

堆排序

堆排序是一种基于比较的排序算法,利用了堆这种数据结构(通常是二叉堆)来完成排序。堆是一棵完全二叉树,具有以下性质:

  • 最大堆(Max-Heap):父节点的值大于或等于子节点的值。
  • 最小堆(Min-Heap):父节点的值小于或等于子节点的值。

堆排序的步骤:

  1. 构建堆:首先将待排序的数组转换为一个堆。通常构建的是最大堆(对于升序排序),即堆顶元素是数组中最大的元素。
  2. 交换根节点与最后一个元素:将堆顶元素(最大元素)与堆的最后一个元素交换。
  3. 重新调整堆:将新的根节点调整为满足堆的性质,恢复最大堆。
  4. 重复步骤 2 和 3,直到堆的大小减少到 1。

时间复杂度:

  • 构建堆O(n)
  • 调整堆:每次调整堆的时间复杂度是 O(logn),总共需要进行 n 次调整,因此整体时间复杂度为 O(nlogn)

堆排序是 不稳定的排序,可能会改变相同元素的相对顺序。

def heapify(arr:list, n:int, i:int):
    """
    将一个子树调整为最大堆,n是堆的大小,i是当前根节点的索引
    """
    largest = i  # 初始化最大值为根节点
    left = 2 * i + 1  # 左子节点索引
    right = 2 * i + 2  # 右子节点索引
    # 如果左子节点大于根节点
    if left < n and arr[left] > arr[largest]:
        largest = left
    # 如果右子节点大于根节点
    if right < n and arr[right] > arr[largest]:
        largest = right

    # 如果根节点不是最大,交换并递归堆化
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]  # 交换
        heapify(arr, n, largest)  # 递归调整子树

def heap_sort(arr):
    """
    堆排序的主函数
    """
    n = len(arr)
    # 构建最大堆
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    # 一个个从堆顶取出元素,并重新调整堆
    for i in range(n - 1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]  # 交换堆顶元素和当前末尾元素
        heapify(arr, i, 0)  # 调整剩余部分为最大堆

http://www.kler.cn/a/542686.html

相关文章:

  • [转]Java面试近一个月的面试总结
  • qt QCommandLineOption 详解
  • 2025.2.10 每日学习记录3:技术报告只差相关工作+补实验
  • HalconDotNet 基础操作
  • 麒麟操作系统-密码拯救
  • 什么是Java虚拟机(JVM)?它的作用是什么?
  • 【matlab创新性滤波代码】平方根扩展卡尔曼滤波(SR EKF)例程,三维非线性系统的滤波,提供完整代码
  • 如果依靠IDEA来做JVM内存泄露的预防检测
  • 分享如何通过Mq、Redis、XxlJob实现算法任务的异步解耦调度
  • 前端知识速记:浏览器缓存机制 - 强缓存与协商缓存
  • 如何开发一个基于Java的商城小程序?
  • torch.no_grad()
  • 反向代理模块k
  • 三角拓扑聚合优化器TTAO-Transformer-BiLSTM多变量回归预测(Maltab)
  • 【深度学习入门实战】基于Keras的手写数字识别实战(附完整可视化分析)
  • LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
  • 数据结构-栈和队列的应用
  • 亚马逊云科技Bedrock知识库自定义语义搜索配置教程
  • SpringBoot项目练习
  • HTML 简介
  • ASP.NET Core的贫血模型与充血模型
  • JavaScript系列(69)--安全编程技术详解
  • DeepSeek模型架构及优化内容
  • Leetcode 3448. Count Substrings Divisible By Last Digit
  • 更新无忧:用 Docker 数据卷确保 Open WebUI 数据持久化
  • java项目部署到linux读取properties中文乱码