最小堆:TopK问题

在解决TopK问题时,综合各方面考虑,最小堆方案比较好,其时间复杂度为O(n*lgK),空间复杂度为O(K)。

由于空间复杂度取决于K值,因此,可应用于大量流数据的TopK问题(无法将全部数据载入内存的情况)。

思路:

(1)构造一个大小为K的最小堆,用于存储当前TopK元素,堆顶为TopK元素中最小的元素,即第K个元素;

(2)每读入一个元素,与堆顶元素比较,若大于堆顶元素,则替换堆顶元素,并将新的堆顶元素进行排序。

代码如下:

public class TopK {

  private int k;
  private List<Integer> heap;  // 存放最小堆数据

  public TopK(int k) {
    this.k = k;
    this.heap = new ArrayList<>(k);
  }

  /**
   * 输入/读入一个元素
   */
  public void input(int num) {
    if (heap.size() < k) {  // 最小堆未满:尾部添加元素,并通过上浮法对其排序。
      heap.add(num);
      swim();
    } else if (num > heap.get(0)) {  // 最小堆已满 且 当前元素大于堆顶元素:替换堆顶元素,并通过下沉法对其排序。
      heap.set(0, num);
      sink();
    }
  }

  /**
   * 上浮法排序:将新的尾部元素进行排序
   */
  private void swim() {
    int index = heap.size() - 1;
    int parentIndex = (index - 1) / 2;
    while (index > 0 && heap.get(index) < heap.get(parentIndex)) {
      Collections.swap(heap, index, parentIndex);
      index = parentIndex;
      parentIndex = (index - 1) / 2;
    }
  }

  /**
   * 下沉法排序:将堆顶元素进行排序
   */
  private void sink() {
    int index = 0;
    while (index < heap.size()) {
      int minValue = heap.get(index), swapIndex = -1;
      int leftIndex = 2 * index + 1, rightIndex = 2 * index + 2;
      if (leftIndex < heap.size() && heap.get(leftIndex) < minValue) {
        minValue = heap.get(leftIndex);
        swapIndex = leftIndex;
      }
      if (rightIndex < heap.size() && heap.get(rightIndex) < minValue) {
        minValue = heap.get(rightIndex);
        swapIndex = rightIndex;
      }
      if (swapIndex > -1) {
        Collections.swap(heap, index, swapIndex);
        index = swapIndex;
      } else {
        break;
      }
    }
  }

  /**
   * 获取第K个值:即堆顶元素
   */
  public int getValueAtK() {
    return heap.get(0);
  }

  /**
   * 获取TopK值列表(无序)
   */
  public List<Integer> getTopK() {
    return new ArrayList<>(heap);  // 深拷贝:避免外部修改影响内部数据
  }

  /**
   * 获取TopK值列表(有序)
   */
  public List<Integer> getSortedTopK() {
    List<Integer> retList = new ArrayList<>(heap);  // 深拷贝:避免修改影响原堆数据
    Collections.sort(retList);
    Collections.reverse(retList);
    return retList;
  }
}

相关文章

《全排列(Java)》

《位运算:减法与补码》

《异或(^)的性质与应用》

《图解:常用排序算法(冒泡、选择、插入、希尔、快速、归并、堆)》

《回溯算法(试探算法)》

《动态规划:鸡蛋掉落》

《动态规划:单词拆分》

《状态机:只出现一次的数字II》

《链表:快慢指针》

### 使用最小实现 Top K 问题 在解决 Top K 问题时,可以利用最小(Min Heap)来高效筛选出数据集中最大的前 K 个元素。以下是基于最小的 Top K 算法的核心原理及其伪代码。 #### 核心原理 为了找出数据集中的前 K 大元素,可以通过维护一个大小为 K 的最小来完成此操作。当遍历整个数据集时,如果当前元素大于顶元素,则替换顶并重新调整结构;否则跳过该元素。最终得到的即为所需的前 K 大元素集合[^1]。 #### 伪代码 下面是使用最小实现 Top K 问题的具体伪代码: ```cpp // 定义函数 topK 接收数组 nums 和整数 k 参数 function topK(nums[], k): // 创建容量为k的小根(minHeap),初始为空 minHeap = new MinHeap(k) // 遍历输入的数据列表nums[] for num in nums: if minHeap.size() < k: // 如果minHeap未满则直接加入num minHeap.insert(num) else if num > minHeap.top(): // 若当前数值比顶大,则移除顶并将新值插入 minHeap.replaceTop(num) // 返回存储于minHeap内的所有元素作为结果 return minHeap.getAllElements() ``` 上述伪代码描述了一个完整的流程用于获取给定数组 `nums` 中的最大前 K 项。通过构建固定规模的最小优先队列 (Min Priority Queue 或者叫 Minimum Heap),我们可以有效地控制空间复杂度 O(K),同时保持每次更新的时间成本较低 —— 插入或者删除节点平均耗时约为 log(K)[^3]。 #### Python 实现示例 下面提供一段实际可用的Python版本代码片段展示这一方法的应用场景: ```python import heapq def find_top_k_largest(nums, k): # 初始化一个小顶 min_heap = [] for num in nums: if len(min_heap) < k: heapq.heappush(min_heap, num) elif num > min_heap[0]: heapq.heapreplace(min_heap, num) return list(min_heap) if __name__ == "__main__": test_data = [7, 10, 4, 3, 20, 15] result = find_top_k_largest(test_data, 3) print(result) # 输出可能的结果之一:[7, 10, 20] ``` 以上程序定义了一个名为 `find_top_k_largest()` 函数用来查找指定序列里的最高分前三名成员,并打印出来验证其功能正常运作。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值