統計可重復列表中的TOP N

文章目錄

      • 方案1:HashMap統計 + 全排序
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
      • 方案2:HashMap統計 + 最小堆(優先隊列)
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
      • 方案3:Java Stream API
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
      • 完整示例代碼
      • 關鍵點總結
      • 方案4:并行流處理(Parallel Stream)
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
      • 方案5:桶排序(Bucket Sort)
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
      • 方案6:快速選擇(Quickselect)算法
        • 實現步驟:
        • 代碼實現(部分):
        • 優缺點:
      • 方案7:Guava庫的MultiSet(第三方依賴)
        • 實現步驟:
        • 代碼實現:
        • 優缺點:
    • 二、方案對比總表
    • 三、總結建議

這種統計top值的情況場景使用的不少,面試過程中也有聊到過這類問題,在這詳細介紹一下思路和方案

在Java中統計列表中出現次數最多的前N個對象,常見的實現方案及其優缺點如下:


方案1:HashMap統計 + 全排序

實現步驟:
  1. 使用HashMap統計每個元素的頻率。
  2. 將統計結果轉為列表,按頻率降序排序。
  3. 取前N個元素。
代碼實現:
public static List<Map.Entry<String, Integer>> topNWithSort(List<String> list, int n) {// 統計頻率Map<String, Integer> freqMap = new HashMap<>();for (String item : list) {freqMap.put(item, freqMap.getOrDefault(item, 0) + 1);}// 轉換為列表并排序List<Map.Entry<String, Integer>> entries = new ArrayList<>(freqMap.entrySet());entries.sort((a, b) -> b.getValue().compareTo(a.getValue()));// 取前N個return entries.subList(0, Math.min(n, entries.size()));
}
優缺點:
  • 優點:實現簡單,代碼直觀。
  • 缺點:全排序時間復雜度為 (O(m \log m))((m) 為不同元素的數量),當 (m) 較大時效率低。

方案2:HashMap統計 + 最小堆(優先隊列)

實現步驟:
  1. 使用HashMap統計頻率。
  2. 使用大小為N的最小堆,遍歷頻率表,維護堆頂為當前最小的頻率。
  3. 將堆中元素逆序輸出。
代碼實現:
public static List<Map.Entry<String, Integer>> topNWithHeap(List<String> list, int n) {// 統計頻率Map<String, Integer> freqMap = new HashMap<>();for (String item : list) {freqMap.put(item, freqMap.getOrDefault(item, 0) + 1);}// 初始化最小堆(按頻率升序)PriorityQueue<Map.Entry<String, Integer>> heap = new PriorityQueue<>((a, b) -> a.getValue() - b.getValue());// 遍歷頻率表,維護堆的大小為Nfor (Map.Entry<String, Integer> entry : freqMap.entrySet()) {if (heap.size() < n) {heap.offer(entry);} else if (entry.getValue() > heap.peek().getValue()) {heap.poll();heap.offer(entry);}}// 將堆轉換為列表并逆序List<Map.Entry<String, Integer>> result = new ArrayList<>(heap);result.sort((a, b) -> b.getValue().compareTo(a.getValue()));return result;
}
優缺點:
  • 優點:時間復雜度為 (O(m \log n)),適合大數據量且 (n \ll m) 的場景。
  • 缺點:需要手動維護堆,代碼稍復雜。

方案3:Java Stream API

實現步驟:
  1. 使用StreamgroupingBycounting統計頻率。
  2. 按頻率降序排序后取前N個。
代碼實現:
public static List<Map.Entry<String, Long>> topNWithStream(List<String> list, int n) {return list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).limit(n).collect(Collectors.toList());
}
優缺點:
  • 優點:代碼簡潔,函數式編程風格。
  • 缺點:隱藏實現細節,可能對內存和性能控制不足。


完整示例代碼

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;public class TopNFrequency {public static void main(String[] args) {List<String> list = Arrays.asList("apple", "banana", "apple", "orange", "banana", "apple");int n = 2;// 方法1:全排序System.out.println("HashMap + Sorting: " + topNWithSort(list, n));// 方法2:最小堆System.out.println("HashMap + Heap: " + topNWithHeap(list, n));// 方法3:Stream APISystem.out.println("Stream API: " + topNWithStream(list, n));}// 方法1:全排序public static List<Map.Entry<String, Integer>> topNWithSort(List<String> list, int n) {Map<String, Integer> freqMap = new HashMap<>();for (String item : list) {freqMap.put(item, freqMap.getOrDefault(item, 0) + 1);}List<Map.Entry<String, Integer>> entries = new ArrayList<>(freqMap.entrySet());entries.sort((a, b) -> b.getValue().compareTo(a.getValue()));return entries.subList(0, Math.min(n, entries.size()));}// 方法2:最小堆public static List<Map.Entry<String, Integer>> topNWithHeap(List<String> list, int n) {Map<String, Integer> freqMap = new HashMap<>();for (String item : list) {freqMap.put(item, freqMap.getOrDefault(item, 0) + 1);}PriorityQueue<Map.Entry<String, Integer>> heap = new PriorityQueue<>((a, b) -> a.getValue() - b.getValue());for (Map.Entry<String, Integer> entry : freqMap.entrySet()) {if (heap.size() < n) {heap.offer(entry);} else if (entry.getValue() > heap.peek().getValue()) {heap.poll();heap.offer(entry);}}List<Map.Entry<String, Integer>> result = new ArrayList<>(heap);result.sort((a, b) -> b.getValue().compareTo(a.getValue()));return result;}// 方法3:Stream APIpublic static List<Map.Entry<String, Long>> topNWithStream(List<String> list, int n) {return list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).limit(n).collect(Collectors.toList());}
}

關鍵點總結

  • 全排序適合數據量小的場景,代碼簡單但效率低。
  • 最小堆適合大數據量,時間復雜度更優。
  • Stream API以簡潔性取勝,但需注意類型轉換和性能。

方案4:并行流處理(Parallel Stream)

實現步驟:
  1. 使用并行流加速統計和排序。
  2. 利用ConcurrentHashMap保證線程安全。
代碼實現:
public static List<Map.Entry<String, Long>> topNParallelStream(List<String> list, int n) {return list.parallelStream().collect(Collectors.groupingByConcurrent(Function.identity(), Collectors.counting())).entrySet().parallelStream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).limit(n).collect(Collectors.toList());
}
優缺點:
  • 優點:利用多核并行處理,適合超大數據量。
  • 缺點:線程安全控制復雜,可能因數據傾斜導致性能提升有限。

方案5:桶排序(Bucket Sort)

實現步驟:
  1. 統計頻率,記錄最大頻率。
  2. 創建頻率桶,索引為頻率,值為元素列表。
  3. 從高到低遍歷桶,收集前N個元素。
代碼實現:
public static List<Map.Entry<String, Integer>> topNBucketSort(List<String> list, int n) {Map<String, Integer> freqMap = new HashMap<>();int maxFreq = 0;for (String item : list) {int freq = freqMap.getOrDefault(item, 0) + 1;freqMap.put(item, freq);maxFreq = Math.max(maxFreq, freq);}// 創建桶(索引為頻率)List<List<String>> buckets = new ArrayList<>(maxFreq + 1);for (int i = 0; i <= maxFreq; i++) {buckets.add(new ArrayList<>());}freqMap.forEach((k, v) -> buckets.get(v).add(k));// 從高到低收集結果List<Map.Entry<String, Integer>> result = new ArrayList<>();for (int i = maxFreq; i >= 0 && result.size() < n; i--) {for (String item : buckets.get(i)) {result.add(new AbstractMap.SimpleEntry<>(item, i));if (result.size() == n) break;}}return result;
}
優缺點:
  • 優點:時間復雜度 (O(m + k))((k)為最大頻率),適合頻率分布集中的場景。
  • 缺點:空間復雜度 (O(k)),若最大頻率極高則浪費內存。

方案6:快速選擇(Quickselect)算法

實現步驟:
  1. 統計頻率,將Entry存入列表。
  2. 使用快速選擇算法找到第N大的頻率分界點。
  3. 對前N個元素進行排序。
代碼實現(部分):
public static List<Map.Entry<String, Integer>> topNQuickSelect(List<String> list, int n) {Map<String, Integer> freqMap = new HashMap<>();for (String item : list) {freqMap.put(item, freqMap.getOrDefault(item, 0) + 1);}List<Map.Entry<String, Integer>> entries = new ArrayList<>(freqMap.entrySet());quickSelect(entries, n);return entries.subList(0, n).stream().sorted((a, b) -> b.getValue().compareTo(a.getValue())).collect(Collectors.toList());
}private static void quickSelect(List<Map.Entry<String, Integer>> list, int n) {int left = 0, right = list.size() - 1;while (left <= right) {int pivotIndex = partition(list, left, right);if (pivotIndex == n) break;else if (pivotIndex < n) left = pivotIndex + 1;else right = pivotIndex - 1;}
}private static int partition(List<Map.Entry<String, Integer>> list, int low, int high) {int pivotValue = list.get(high).getValue();int i = low;for (int j = low; j < high; j++) {if (list.get(j).getValue() > pivotValue) {Collections.swap(list, i, j);i++;}}Collections.swap(list, i, high);return i;
}
優缺點:
  • 優點:平均時間復雜度 (O(m)),適合對性能要求極高的場景。
  • 缺點:實現復雜,需處理大量邊界條件。

方案7:Guava庫的MultiSet(第三方依賴)

實現步驟:
  1. 使用Guava的Multiset統計頻率。
  2. 按頻率排序后取前N個。
代碼實現:
public static List<Multiset.Entry<String>> topNGuava(List<String> list, int n) {Multiset<String> multiset = HashMultiset.create(list);return multiset.entrySet().stream().sorted((a, b) -> b.getCount() - a.getCount()).limit(n).collect(Collectors.toList());
}
優缺點:
  • 優點:代碼極簡,依賴Guava工具類。
  • 缺點:需引入第三方庫,不適合純JDK環境。

二、方案對比總表

方案時間復雜度空間復雜度適用場景
全排序(O(m \log m))(O(m))數據量小,代碼簡單
最小堆(O(m \log n))(O(n))大數據量且 (n \ll m)
Stream API(O(m \log m))(O(m))快速開發,代碼簡潔
并行流(O(m \log m / p))(O(m))多核環境,超大數據量
桶排序(O(m + k))(O(k))頻率集中且最大值已知
快速選擇(O(m))(平均)(O(m))高性能需求,允許復雜實現
Guava MultiSet(O(m \log m))(O(m))允許第三方依賴

三、總結建議

  1. 小數據量:優先使用 Stream API全排序,代碼簡潔。
  2. 大數據量:選擇 最小堆并行流,平衡性能與內存。
  3. 已知頻率分布:嘗試 桶排序 優化時間和空間。
  4. 極高性能需求:考慮 快速選擇(需自行處理實現復雜度)。
  5. 允許第三方庫Guava 可大幅簡化代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74040.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74040.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74040.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JVM 知識點梳理

JDK 、JRE、JVM JDK&#xff08; Java Development Kit &#xff09; Java開發工具包 JRE 開發命令工具&#xff08;運行java.exe、編譯javac.exe、javaw.exe&#xff09; JRE&#xff08; Java Runtime Environment &#xff09;Java運行環境 JVM Java核心類庫&#xff08;l…

淘寶歷史價格數據獲取指南:API 與爬蟲方案的合法性與效率對比

引言 在淘寶平臺的購物生態中&#xff0c;消費者希望通過了解商品歷史價格來判斷當前價格是否實惠&#xff0c;商家也需要借助歷史價格數據制定合理的營銷策略、分析市場趨勢。獲取淘寶商品歷史價格數據主要有 API 和爬蟲兩種方案&#xff0c;它們在合法性與效率上存在顯著差異…

DeepSeek-R1論文深度解析:純強化學習如何引爆LLM推理革命?

技術突破&#xff1a;從“無監督”到“自主進化”的跨越 paper &#xff1a;https://arxiv.org/pdf/2501.12948目錄 技術突破&#xff1a;從“無監督”到“自主進化”的跨越1 DeepSeek-R1-Zero&#xff1a; RLnoSFT1.1 R1-Zero&#xff1a; GRPO&#xff08;Group Relative Po…

表格標題豎直

使用文本方式使表格怎么豎列 思路&#xff1a;表格豎直書寫&#xff0c;里面的內容水平書寫 使用到的是css中的文本效果&#xff1a; writing-mode&#xff1a;書寫方式horizontal-tb&#xff1a;水平vertical-rl&#xff1a;豎直<style>table {writing-mode: vertical…

AI+視頻賦能智慧農業:EasyCVR打造全域可視化農場監管平臺

隨著科技的飛速發展&#xff0c;傳統農業正加速向智慧農業轉型&#xff0c;農場管理也迎來了前所未有的變革機遇。在這一進程中&#xff0c;如何有效整合先進的信息技術&#xff0c;實現農場的精準化、智能化管理&#xff0c;成為了擺在農場主和農業管理者面前的關鍵課題。 基于…

HarmonyOS鴻蒙開發 BuilderParam在父組件的Builder的點擊事件報錯:Error message:is not callable

HarmonyOS鴻蒙開發 BuilderParam在父組件的Builder的點擊事件報錯&#xff1a;Error message:is not callable 最近在鴻蒙開發過程中&#xff0c;UI做好了&#xff0c;根據列表item進行點擊跳轉&#xff0c;報錯了 報錯信息如下 Error message:is not callable Stacktrace:at…

簡化神經元模型6 -- Hindmarsh-Rose Model

Hindmarsh-Rose 模型 目錄 0. 寫在前面 1. Hindmarsh-Rose 模型的定義 2. Hindmarsh-Rose 模型簇發放的動力學機制 3. Hindmarsh-Rose 模型的其他發放模式 4. 分析過程所用到的一系列 BrainPy 代碼 0. 寫在前面 前面介紹了: Hodgkin-Huxley Model 簡化神經元模型1 – LIF M…

第六屆電氣、電子信息與通信工程國際學術會議 (EEICE 2025)

重要信息 官網&#xff1a;www.eeice.net&#xff08;點擊了解參會投稿等&#xff09; 時間&#xff1a;2025年4月18-20日 地點&#xff1a;中國-深圳技術大學 簡介 第六屆電氣、電子信息與通信工程 (EEICE 2025&#xff09;將于2025年4月18-20日在中國深圳召開。 EEICE 20…

計算機操作系統(三) 操作系統的特性、運行環境與核心功能(附帶圖譜更好對比理解))

計算機操作系統&#xff08;三&#xff09; 操作系統的特性、運行環境與核心功能 前言一、操作系統的基本特性1.1 并發1.2 共享1.3 虛擬1.4 異步 二、操作系統的運行環境2.1 硬件支持2.2 操作系統內核2.3 處理機的雙重工作模式2.4 中斷與異常 三、操作系統的主要功能3.1 處理機…

Linux(Ubuntu)系統安裝Docker與Docker Compose完整指南

本文是為需要在Ubuntu系統部署容器服務的開發者準備的詳細教程。我們將分兩個主要部分講解&#xff1a;Docker引擎的標準安裝流程和Docker Compose的配置方法。所有操作均在終端執行&#xff0c;建議使用Ubuntu 18.04及以上版本。 一、Docker引擎安裝全流程 &#xff08;總耗時…

批量將 PPT 轉換為PDF/XPS/JPG圖片等其它格式

PPT 文檔經常有轉換為其它格式的需求&#xff0c;比如將 PPT 轉換為 PDF、將 PPT 轉換為圖片、生成 PPT 預覽圖等&#xff0c;這在某些場景下非常的有用&#xff0c;今天給大家介紹的就是如何批量將 PDF 轉換為 PDF、JPG、Tiff 等多種格式的操作。 在工作中我們經常需要接觸 PP…

c庫、POSIX庫、C++庫、boost庫之間的區別和聯系

文章目錄 一、區別1. 定義和來源2. 功能范圍3. 可移植性4. 語言支持5. 維護和更新 二、聯系1. 相互補充2. 部分功能重疊3. 共同促進編程發展4. 代碼兼容性 三、總結 一、區別 1. 定義和來源 C 庫函數&#xff1a;由 ANSI C 和 ISO C 標準定義&#xff0c;是 C 語言編程的基礎…

響應壓縮導致的接口請求response沒有響應體問題排查

目錄 一、背景二、排查過程三、解決方法四、學習與思考-響應壓縮&#xff08;一&#xff09;可能原因&#xff08;二&#xff09;深入排查&#xff08;三&#xff09;注意 一、背景 接口發布到測試環境&#xff0c;測試同學說沒有數據 二、排查過程 1、本地用相同的參數、相…

JVM中的運行時常量池詳解

運行時常量池&#xff08;Runtime Constant Pool&#xff09;是每一個類或接口的常量池&#xff08;Constant_Pool&#xff09;的運行時表示形式&#xff0c;它包括了若干種不同的常量&#xff1a;從編譯期可知的數值字面量到必須運行期解析后才能獲得的方法或字段引用。運行時…

C# MethodBase 類使用詳解

總目錄 前言 在C#編程中&#xff0c;反射&#xff08;Reflection&#xff09;是一種強大的機制&#xff0c;允許我們在運行時檢查和操作類型的成員。MethodBase 類是.NET框架中 System.Reflection 命名空間下的一個抽象類&#xff0c;它是所有方法( MethodInfo 和 Constructor…

【css酷炫效果】純CSS實現3D翻轉卡片動畫

【css酷炫效果】純CSS實現3D翻轉卡片動畫 緣創作背景html結構css樣式完整代碼效果圖 想直接拿走的老板&#xff0c;鏈接放在這里&#xff1a;https://download.csdn.net/download/u011561335/90490472 緣 創作隨緣&#xff0c;不定時更新。 創作背景 剛看到csdn出活動了&am…

Flask多參數模版使用

需要建立目錄templates&#xff1b; 把建好的html文件放到templates目錄里面&#xff1b; 約定好參數名字&#xff0c;單個名字可以直接使用&#xff1b;多參數使用字典傳遞&#xff1b; 樣例&#xff1a; from flask import render_template # 模板 (Templates) #Flask 使用…

SVN簡明教程——下載安裝使用

SVN教程目錄 一、開發中的實際問題二、簡介2.1 版本控制2.2 Subversion2.3 Subversion的優良特性2.4 工作原理2.5 SVN基本操作 三、Subversion的安裝與配置1. 服務器端程序版本2. 下載源碼包3. 下載二進制安裝包4. 安裝5. 配置版本庫① 為什么要配置版本庫&#xff1f;② 創建目…

OpenCV圖像拼接(1)概述

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 此圖說明了在Stitcher類中實現的拼接模塊流程。使用該類&#xff0c;可以配置/移除某些步驟&#xff0c;即根據特定需求調整拼接流程。流程中的所…

Ubuntu20.04安裝Nvidia顯卡驅動

Ubuntu20.04安裝Nvidia顯卡驅動 安裝環境為Dell R540服務器 官網下載Nvidia顯卡驅動 https://www.nvidia.cn/geforce/drivers/ 安裝顯卡驅動 chmod x NVIDIA-Linux-x86_64-470.63.01.run sudo ./NVIDIA-Linux-x86_64-470.63.01.run 遇到nouveau報錯 lsmod查看nouveau驅動…