Python三种方法实现topk问题(源码)

news/2024/7/7 22:49:01
# topK问题  数组中有n个元素求前k个最大的数
# 1. 快排或小顶堆排n个数 返回前k个数  --- 时复为O(n+nlog_2n+k)
# 2. 第一次优化:首先根据n数组建立一个大顶堆 每次获取arr[0](并将其移除) 原地移除的方法是将arr[0]与arr[-1]对调 后在arr[0:-1)时向下调整法 反复上述步骤 直至k次 则获得了前k个最大的数  ------时复为O(n + klog_2n) 前一个n是建堆的时复,后面是进行了k次向下调整法,这样则当n很大时 log_2n趋于稳定 此时为线性复杂度O(n)
# 3. 第二次优化:当n很大时,上述时间复杂度还是很高, 还可以如何做? 若是求前k个最大的数,则建元素个数为k的小堆,每加入一个元素与堆顶元素比较,若比堆顶大则将堆顶元素替换,再做向下调整法,直至遍历完数组,那么小堆中维护的就是数组中前k个大的数。时复最差为O(k+(n-k)log_2n)前面是k个堆的排序 后面是最糟糕的情况 即后面每次都要调整。

第一次优化代码
# 向下调整算法
def ajustDown(arr, end, parent_index):
    """
    :param arr: 需要调整的arr数组
    :param end: 需要调整的arr数组的末尾索引+1
    :param parent_index: 从该节点开始调整
    :return: 原地更改数组 无返回值
    """
    # 针对arr只有一个元素的情况 不需处理
    if parent_index < 0:
        return
    # 先认为当前父节点为最小
    smallest_index = parent_index
    # 获取左右孩子下标
    leftchild_index = 2 * parent_index + 1
    rightchild_index = 2 * parent_index + 2
    # 找出父 左 右 孩子最小的index
    if leftchild_index < end and arr[leftchild_index] < arr[parent_index]:
        smallest_index = leftchild_index
    if rightchild_index < end and arr[rightchild_index] < arr[smallest_index]:
        smallest_index = rightchild_index
    # 如果更改了下标 则进行交换 并递归更新
    if not (smallest_index == parent_index):
        arr[smallest_index], arr[parent_index] = arr[parent_index], arr[smallest_index]
        ajustDown(arr, end, smallest_index)

if __name__ == '__main__':
    # 测试用例
    # ---------- 对[整个数组 因此end=len(arr)]直接建堆 --------------
    arr = [2,11,8,5,4,9,35,36,1,6]
    for i in range(len(arr), -1, -1):
        parent_index = (i - 1) // 2
        ajustDown(arr, len(arr), parent_index)
    print(arr)
    # ------------ 对数组进行堆排序 ---------------
    # 堆排序分为两步 先对数组建堆 将头(最小元素)尾对调 对头进行向下调整法(这时不可以包括尾) 复原堆的位置
    # 这里建的小顶堆 因此是升序排序
    arr2 = [1, 2, 8, 5, 4, 9, 35, 36, 11, 6]
    # 建堆
    for i in range(len(arr2), -1, -1):
        parent_index = (i - 1) // 2
        ajustDown(arr2, len(arr2), parent_index)
    print(arr2)
    # 对调 及 对头 进行向下调整法
    for tail in range(len(arr2)-1, 0, -1):
        arr2[0], arr2[tail] = arr2[tail], arr2[0]
        ajustDown(arr2, tail, 0)
    print(arr2)

# 执行结果
# [1, 2, 8, 5, 4, 9, 35, 36, 11, 6]
# [1, 2, 8, 5, 4, 9, 35, 36, 11, 6]
# [36, 35, 11, 9, 8, 6, 5, 4, 2, 1]

第二次优化与第一次类似 只是换成大顶堆去实现

第三次优化如下:

# 向下调整算法
def ajustDown(arr, end, parent_index):
    """
    :param arr: 需要调整的arr数组
    :param end: 需要调整的arr数组的末尾索引+1
    :param parent_index: 从该节点开始调整
    :return: 原地更改数组 无返回值
    """
    # 针对arr只有一个元素的情况 不需处理
    if parent_index < 0:
        return
    # 先认为当前父节点为最小
    smallest_index = parent_index
    # 获取左右孩子下标
    leftchild_index = 2 * parent_index + 1
    rightchild_index = 2 * parent_index + 2
    # 找出父 左 右 孩子最小的index
    if leftchild_index < end and arr[leftchild_index] < arr[parent_index]:
        smallest_index = leftchild_index
    if rightchild_index < end and arr[rightchild_index] < arr[smallest_index]:
        smallest_index = rightchild_index
    # 如果更改了下标 则进行交换 并递归更新
    if not (smallest_index == parent_index):
        arr[smallest_index], arr[parent_index] = arr[parent_index], arr[smallest_index]
        ajustDown(arr, end, smallest_index)

if __name__ == '__main__':
    # 给定n个元素的arr个数组,求前k个最大的数,利用小顶堆--始终维护遍历到的最大的k个数,则将数组遍历完毕,就得到整个数组的前k个最大的数
    arr = [1,5,1,3,6,9,4,5,6,8,1,2,5,63,2,46,52,1,6,25,3,56,265,235,1,5,36,523]
    k = 5
    # 给数组arr前k个数进行建小顶堆
    for i in range(k-1, -1, -1):
        parent_index = (i - 1) // 2
        ajustDown(arr, k, parent_index)
    # 从数组的索引k进行遍历 遇到大于堆顶 则跟堆顶替换 并用向下调整法 始终维护遍历到当前位置的前k个最大的数
    for i in range(k,len(arr)):
        if arr[i] > arr[0]:
            arr[i], arr[0] = arr[0], arr[i]
            ajustDown(arr, k, 0)
    # 此时数组前k个数 为最大的数 输出结果
    print(arr[:k])
# 执行结果 [56, 63, 523, 265, 235]


http://lihuaxi.xjx100.cn/news/1886853.html

相关文章

openEuler 20.03 (LTS-SP2) aarch64 cephadm 部署ceph18.2.0【5】 添加osd存储节点

接上篇 openEuler 20.03 (LTS-SP2) aarch64 cephadm 部署ceph18.2.0【1】离线部署 准备基础环境-CSDN博客 openEuler 20.03 (LTS-SP2) aarch64 cephadm 部署ceph18.2.0【2】离线部署 podman配置registries 部署registry私服 准备离线镜像-CSDN博客 openEuler 20.03 (LTS-SP2…

mumu模拟器,adb devices 忽然就不显示设备解决方法

依次执行以下 adb kill-server adb start-server adb devices

HTML行内元素与块级元素的区别

目录 行内元素&#x1f338;常见的行内元素&#x1f338;行内元素&#xff08;内联元素&#xff09;的特性 块级元素&#x1f338;常见的块级元素&#x1f338;块级元素的特性 相互转换(display)&#x1f338;行内块状元素的特性 行内元素 &#x1f338;常见的行内元素 <s…

[原创] FPGA的JTAG烧录不稳定或烧录失败原因分析

一、电路故障背景 打板回来常会出现烧录不良&#xff0c;调试是一个技术活&#xff0c;如果烧录不过关&#xff0c;一切白搭。 二、常见JTAG故障原因如下&#xff1a; 1、ESD防护器件焊接不良&#xff1b; 电路板给生产部分焊接&#xff0c;发现元器件虚焊&#xff0c;特别是…

FIO QD参数与Linux IO路径的关联

在Linux中&#xff0c;当使用fio工具测试存储设备的性能时&#xff0c;QD&#xff08;Queue Depth&#xff09;参数对应的是I/O调度器&#xff08;I/O Scheduler&#xff09;和块设备层中的请求队列。 I/O 调度器&#xff1a; I/O 调度器是内核的一部分&#xff0c;负责管理来自…

C语言中的柔性数组

uint8_t data[0];代码的含义老虎开始对这个数组不太了解&#xff0c;查阅后得知这是个柔性数组。 C语言中的柔性数组&#xff08;Flexible Array Member&#xff09;是一种特殊的数组&#xff0c;它被定义在结构体的最后一个元素中&#xff0c;其大小未知&#xff0c;也就是所…

Redis 专栏、JVM 专栏文章导读

深入理解 Redis 专栏文章 Redis深入理解-Socket连接建立流程以及文件事件处理机制 Redis深入理解-内核请求处理流程、数据传输协议 Redis深入理解-三次握手、槽位机制 Redis深入理解-主从架构下内核数据结构、主从同步以及主节点选举 基于社区电商的Redis缓存架构-缓存数据库双…

Vue 双向绑定:让数据与视图互动的魔法!(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…