「給定一個隨機字符串,找出出現次數最多的字符及其計數」是前端/全棧面試高頻題。它考察:對象/Map 計頻、遍歷技巧、時間空間複雜度權衡、邊界處理。本文提供兩種經典解法,並延伸至生產級健壯實現。
function findMaxChar(str) {
if (!str || typeof str !== 'string') return { char: '', count: 0 };
// 1. 計頻:使用 Map 保持插入順序、支持任意字符 Key
const freqMap = new Map();
for (const ch of str) {
freqMap.set(ch, (freqMap.get(ch) || 0) + 1);
}
// 2. 找最大:單次遍歷 Map
let maxChar = '';
let maxCount = 0;
for (const [ch, count] of freqMap) {
if (count > maxCount) {
maxCount = count;
maxChar = ch;
}
}
return { char: maxChar, count: maxCount };
}
// 測試
console.log(findMaxChar("fjalsfj1fds;lafsafafafasfas"));
// { char: 'a', count: 8 } (假設 a 出現 8 次)
console.log(findMaxChar("adadfdfseffserfefsefseeffffftsdg"));
// { char: 'f', count: 10 } (假設 f 出現 10 次)
function findMaxCharLegacy(str) {
if (!str) return { char: '', count: 0 };
var freq = {}; // 普通對象作哈希表
for (var i = 0; i < str.length; i++) {
var ch = str.charAt(i);
freq[ch] = (freq[ch] || 0) + 1;
}
var maxChar = '', maxCount = 0;
for (var ch in freq) {
if (freq[ch] > maxCount) {
maxCount = freq[ch];
maxChar = ch;
}
}
return { char: maxChar, count: maxCount };
}
chreplace(new RegExp(ch, 'g'), '') 全局替換移除所有 chch 出現次數function findMaxCharReplace(str) {
if (!str) return { char: '', count: 0 };
var maxCount = 0, maxChar = '';
while (str.length > 0) {
var ch = str.charAt(0);
var originalLen = str.length;
// 關鍵:動態構建正則,全局替換
str = str.replace(new RegExp(ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '');
var count = originalLen - str.length;
if (count > maxCount) {
maxCount = count;
maxChar = ch;
}
}
return { char: maxChar, count: maxCount };
}
ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 必須對特殊字符轉義,否則 .*+?^${}()|[]\ 等會被正則引擎誤解析導致錯誤或安全風險 (ReDoS)。
評價:此解法僅作為面試展示「異常思維」,生產環境嚴禁使用:性能差、正則風險、破壞原數據、無法處理 Unicode 組合字符。
/**
* 查找出現次數最多的字符(支持多個並列最大)
* @param {string} str 輸入字符串
* @param {Object} options 配置
* @param {boolean} options.caseSensitive 是否大小寫敏感,默認 true
* @param {boolean} options.returnAllMax 是否返回所有並列最大字符,默認 false
* @returns {Object|Array} {char, count} 或 [{char, count}, ...]
*/
function findMaxCharAdvanced(str, options = {}) {
const { caseSensitive = true, returnAllMax = false } = options;
if (!str || typeof str !== 'string') return returnAllMax ? [] : { char: '', count: 0 };
const processStr = caseSensitive ? str : str.toLowerCase();
const freqMap = new Map();
// 正確遍歷 Unicode 字符(支持 Emoji、組合字符)
for (const ch of processStr) {
freqMap.set(ch, (freqMap.get(ch) || 0) + 1);
}
// 找最大計數
let maxCount = 0;
for (const count of freqMap.values()) {
if (count > maxCount) maxCount = count;
}
// 收集所有並列最大字符
const maxChars = [];
for (const [ch, count] of freqMap) {
if (count === maxCount) maxChars.push({ char: ch, count });
}
return returnAllMax ? maxChars : maxChars[0];
}
// 使用示例
console.log(findMaxCharAdvanced("aabbbcc"));
// { char: 'b', count: 3 }
console.log(findMaxCharAdvanced("aabbbcc", { returnAllMax: true }));
// [ { char: 'b', count: 3 } ]
console.log(findMaxCharAdvanced("aabbcc", { returnAllMax: true }));
// [ { char: 'a', count: 2 }, { char: 'b', count: 2 }, { char: 'c', count: 2 } ]
console.log(findMaxCharAdvanced("😀😀😃😃😃", { returnAllMax: true }));
// [ { char: '😃', count: 3 } ] (正確處理 Emoji)
| 延伸問題 | 核心回答要點 |
|---|---|
| 如果字符串極大 (GB 級),內存不夠怎麼辦? | 流式處理:分塊讀取 + 外部排序/哈希分區 + 歸併;或使用布隆過濾器/Count-Min Sketch 近似算法 |
| 如何統計「單詞」而非字符? | 正則 /\b\w+\b/g 分詞 → 同樣 Map 計頻 |
| 如果要找前 K 個高頻字符 (Top K)? | Map 計頻 + 小頂堆 (大小 K) 維護 Top K,時間 O(n log k) |
| 並發環境下多線程統計如何合併? | 各線程局部 Map 計頻,最終合併 (ConcurrentHashMap 或 reduce 合併) |
| 為什麼用 Map 而不用 Object? | Map:Key 類型任意、保持插入順序、可迭代、size 屬性、無原型鏈污染、性能更好(頻繁增刪) |
面試中遇到此題:
掌握這道題的標準解、優化解、邊界處理、延伸思考,足以應對 90% 以上同類面試考察。