返回

解码面试题:发掘最小k个数的奥秘

前端

探索「最小 k 个数」问题:排序法 vs. 快速选择法

什么是「最小 k 个数」问题?

在数据分析和机器学习领域,经常需要找出数据集中最小的 k 个数字。这个问题称为「最小 k 个数」问题。例如,对于数组 [4, 5, 1, 6, 2, 7, 3, 8],当 k = 3 时,最小的 3 个数字是 1、2 和 3。

解决「最小 k 个数」问题的两种常见方法

解决这一问题的两种主要方法是:

  • 排序法: 对数组进行排序,然后选择前 k 个最小的数字。这种方法简单易懂,但时间复杂度较高。
  • 快速选择法: 一种快速排序的变体,通过递归地将数组划分为较小和较大的子集来找到第 k 个最小的数字。

排序法

def find_smallest_k_numbers_by_sorting(nums, k):
  """
  使用排序法找出数组中不去重的最小的k个数

  :param nums: 输入数组
  :type nums: list
  :param k: 要找的最小k个数
  :type k: int
  :return: 最小的k个数
  :rtype: list
  """

  # 对数组进行排序
  nums.sort()

  # 返回前k个最小的数
  return nums[:k]

快速选择法

def find_smallest_k_numbers_by_quick_select(nums, k):
  """
  使用快速选择法找出数组中不去重的最小的k个数

  :param nums: 输入数组
  :type nums: list
  :param k: 要找的最小k个数
  :type k: int
  :return: 最小的k个数
  :rtype: list
  """

  # 递归终止条件
  if k == 1:
    return [nums[0]]

  # 随机选取一个枢纽元素
  pivot = random.choice(nums)

  # 将数组元素划分为两部分:小于枢纽元素的元素和大于枢纽元素的元素
  left, right = [], []
  for num in nums:
    if num < pivot:
      left.append(num)
    elif num > pivot:
      right.append(num)

  # 如果左侧元素的个数等于k,则左侧元素就是我们要找的最小k个数
  if len(left) == k:
    return left

  # 如果左侧元素的个数小于k,则最小k个数在右侧元素中
  elif len(left) < k:
    return find_smallest_k_numbers_by_quick_select(right, k - len(left))

  # 如果左侧元素的个数大于k,则最小k个数在左侧元素中
  else:
    return find_smallest_k_numbers_by_quick_select(left, k)

应用场景

「最小 k 个数」问题在数据挖掘、机器学习和网络安全等领域都有着广泛的应用,例如:

  • 查找数据集中的异常值或热点。
  • 优化机器学习模型的性能。
  • 检测网络流量中的可疑活动。

总结

「最小 k 个数」问题是一个重要的问题,在数据分析和机器学习中有着广泛的应用。排序法和快速选择法是解决这一问题的两种常见方法。根据数据量和所需的时间复杂度,您可以选择最合适的方法。

常见问题解答

  1. 哪种方法更适合解决「最小 k 个数」问题?
    这取决于数据量和所需的时间复杂度。对于小数据集,排序法可能更简单。对于大数据集,快速选择法通常更有效率。

  2. 快速选择法的平均时间复杂度是多少?
    O(n)。

  3. 快速选择法的最坏情况时间复杂度是多少?
    O(n^2)。

  4. 「最小 k 个数」问题有什么实际应用?
    在数据挖掘、机器学习和网络安全等领域有着广泛的应用。

  5. 如何判断一个解决方案是否找到了最小的 k 个数?
    可以通过将解决方案与已排序的数组的前 k 个元素进行比较来验证。