算法题:寻找二维数组top k问题

算法题目

m 个排序好的数组(从小到大排序),组成的一个二维数组,请你找到最小的第 k 个数。Python 实现,例如数组 \[1, 3, 5, 7, 8, 9, 2, 4, 6],k=5

题目分析

题意:①全局无序,②子数组有序

给定 m 个各自升序排列的一维数组构成二维数组,全局所有元素中找出第 k 小的数。

示例:[[1,3,5],[7,8,9],[2,4,6]],所有元素排序:[1,2,3,4,5,6,7,8,9]k=5 → 结果为 5

介绍三种解法:(从暴力到最优)

方法 1:暴力合并排序(最简单,适合数据量小)

  1. 把所有数组摊平成一维,整体排序取下标 k-1,一行就能写完。
  2. 缺点:时间复杂度 (O(Nlog N))N为总元素数,大数据低效
python 复制代码
def kth_smallest_brute(matrix, k):
    arr = []
    for row in matrix:
        arr.extend(row)
    arr.sort()
    return arr[k-1]

# 测试
if __name__ == "__main__":
    data = [[1, 3, 5], [7, 8, 9], [2, 4, 6]]
    print(kth_smallest_brute(data, 5))  # 输出 5

方法 2:最小堆(优先队列,经典多路归并)

核心思路:多路有序数组归并,用小顶堆每次弹出最小值,弹出第 k 次即为答案。

  1. 先把每个数组第一个元素入堆,记录:(值, 数组索引, 元素在数组内下标)
  2. 循环弹出堆顶最小元素,计数 + 1;若计数 ==k 直接返回
  3. 如果弹出元素所在数组还有下一个元素,继续入堆
  4. 复杂度:(O(klog m)),m 为数组个数,k 不大时效率极高
  5. 说明:需要用到Python自带的包 heapq,有取巧嫌疑。
python 复制代码
import heapq

def kth_smallest_heap(matrix, k):
    heap = []
    m = len(matrix)
    for i in range(m):  # 初始化堆:每个数组第一个元素入堆
        val = matrix[i][0]
        heapq.heappush(heap, (val, i, 0))
    
    cnt = 0
    while heap:
        val, arr_idx, elem_idx = heapq.heappop(heap)
        cnt += 1
        if cnt == k:
            return val
        if elem_idx + 1 < len(matrix[arr_idx]):  # 当前数组还有下一个元素则入堆
            next_val = matrix[arr_idx][elem_idx + 1]
            heapq.heappush(heap, (next_val, arr_idx, elem_idx + 1))

if __name__ == "__main__":  
    test_arr = [[1, 3, 5], [7, 8, 9], [2, 4, 6]]
    print(kth_smallest_heap(test_arr, 5))  # 5

方法 3:二分查找最优解法(推荐大数据量)

思路:利用值域二分(自己想到的方法)

  1. 最小值 left = 所有数组首元素最小值最大值 right = 所有数组尾元素最大值
  2. mid = (left+right)//2,统计二维数组中≤mid 的元素总数 count
  3. count < k:说明答案在右半区间 left=mid+1
  4. count ≥k:答案在左半区间 right=mid
  5. 最终 left=right 就是第 k 小数
  6. 复杂度(O(m log S))S 为数值值域范围,性能上限最高
python 复制代码
import bisect

def kth_smallest_binary(matrix, k):
    # 确定二分上下界
    left = min(row[0] for row in matrix)
    right = max(row[-1] for row in matrix)

    def count_less_or_equal(x):
        """统计所有数组中 <= x 的元素个数(每行有序,二分加速)"""
        total = 0
        for row in matrix:
            total += bisect.bisect_right(row, x)   # bisect_right 返回插入点,即小于等于x的数量
        return total

    while left < right:
        mid = (left + right) // 2
        cnt = count_less_or_equal(mid)
        if cnt < k:
            left = mid + 1
        else:
            right = mid
    return left

if __name__ == "__main__":
    arr = [[1, 3, 5], [7, 8, 9], [2, 4, 6]]
    print(kth_smallest_binary(arr, 5))  # 5

总结

  1. 数据很小 → 暴力一行版;
  2. 面试常规、多路归并考点 → 堆解法;
  3. 海量数据、追求极致效率 → 值域二分法。
相关推荐
1000世界小札36 分钟前
《大话数据结构》第9章精读:归并排序与快速排序完整 C++ 实现
数据结构·c++·算法
2601_956121973 小时前
背包基础篇(01、完全、分组、多重、混合)
c++·算法·动态规划
kobe_OKOK_4 小时前
DRF接口幂等操作
python·django
廿士4 小时前
python脚本使用相关
python
兴通物联科技4 小时前
SMT PCB 微小 DataMatrix 码扫不动问题分析 兴通 XT8601B 600 万像素工业读码器落地实践
大数据·人工智能·单片机·嵌入式硬件·算法·计算机视觉
青 春 记 忆4 小时前
零基础入门python19:Flask账本第一步——应用工厂、蓝图和健康检查
python·flask·后端开发
朦胧之4 小时前
Python 后端核心知识
python
denggun123455 小时前
yield
前端·数据库·python
zx_741484816 小时前
【Python 入门】面向对象基础:类、对象、成员变量与构造方法
开发语言·python