字符频次统计是编程面试中的经典问题,看似简单却能全面考察候选人的基本功。我在技术面试中经常用这道题作为开场白,因为它能快速检验以下几个关键能力:
这道题的常见形式是:"给定一个字符串,统计每个字符出现的次数"。比如输入"hello",输出应该是{'h':1, 'e':1, 'l':2, 'o':1}。接下来我会分享7种不同思路的解法,从暴力枚举到函数式编程,覆盖Python、Java、JavaScript三种语言实现。
这是最直观的解法,适用于所有主流语言。核心思路是:
Python示例:
python复制def char_count(text):
counter = {}
for char in text:
if char in counter:
counter[char] += 1
else:
counter[char] = 1
return counter
时间复杂度:O(n) 空间复杂度:O(k)(k为不同字符数量)
注意:这里使用了if-else做存在性判断,其实Python的字典有更优雅的写法
Python的collections模块提供了更简洁的实现:
python复制from collections import defaultdict
def char_count(text):
counter = defaultdict(int)
for char in text:
counter[char] += 1
return dict(counter)
Java的等效实现:
java复制Map<Character, Integer> charCount(String s) {
Map<Character, Integer> map = new HashMap<>();
for (char c : s.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
return map;
}
Python的collections.Counter是专门为这种场景设计的:
python复制from collections import Counter
def char_count(text):
return dict(Counter(text))
虽然代码简洁,但面试时直接调用库函数可能无法展示编码能力,建议先实现基础解法再提及这种方法。
JavaScript的reduce方案:
javascript复制const charCount = str => [...str].reduce((acc, char) => {
acc[char] = (acc[char] || 0) + 1;
return acc;
}, {});
Python的lambda版本:
python复制from functools import reduce
def char_count(text):
return reduce(
lambda acc, char: {**acc, char: acc.get(char, 0) + 1},
text,
{}
)
提示:函数式写法虽然简洁,但可读性可能受影响,适合展示对语言特性的掌握程度
实际业务中可能需要忽略大小写或非字母字符:
python复制def alpha_count(text):
counter = {}
for char in text.lower():
if char.isalpha():
counter[char] = counter.get(char, 0) + 1
return counter
当字符集明确且有限时(如仅ASCII字符),可以用数组代替哈希表:
java复制int[] charCount(String s) {
int[] count = new int[128]; // ASCII码范围
for (char c : s.toCharArray()) {
count[c]++;
}
return count;
}
这种实现时间复杂度仍是O(n),但空间复杂度降为O(1)(固定大小的数组)
用Python的timeit模块测试10万次调用耗时(字符串长度100):
发现:专用工具类性能最优,函数式写法性能最差
对于超长字符串,可以改用生成器避免内存爆炸:
python复制def char_count_large(file_path):
counter = {}
with open(file_path) as f:
while True:
chunk = f.read(4096)
if not chunk:
break
for char in chunk:
counter[char] = counter.get(char, 0) + 1
return counter
现代编程需要考虑到多语言字符:
python复制def unicode_count(text):
return {
char: text.count(char)
for char in set(text)
}
注意:str.count()是O(n)操作,整体复杂度变为O(n²),仅适合短文本
使用多线程处理超大文件:
python复制from concurrent.futures import ThreadPoolExecutor
import os
def parallel_count(file_path, workers=4):
counter = {}
size = os.path.getsize(file_path)
chunk_size = size // workers
def process_chunk(start, end):
local_counter = {}
with open(file_path) as f:
f.seek(start)
chunk = f.read(end - start)
for char in chunk:
local_counter[char] = local_counter.get(char, 0) + 1
return local_counter
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = []
for i in range(workers):
start = i * chunk_size
end = size if i == workers - 1 else (i + 1) * chunk_size
futures.append(executor.submit(process_chunk, start, end))
for future in futures:
for char, count in future.result().items():
counter[char] = counter.get(char, 0) + count
return counter
面试时需要明确需求是否区分大小写:
python复制# 区分大小写
count = char_count("Hello") # {'H':1, 'e':1, 'l':2, 'o':1}
# 不区分大小写
count = char_count("Hello".lower()) # {'h':1, 'e':1, 'l':2, 'o':1}
边界情况要特别注意:
python复制assert char_count("") == {} # 而不是None或抛出异常
在Java中如果使用HashMap并发修改会抛出ConcurrentModificationException,应该用:
java复制Map<Character, Integer> map = new ConcurrentHashMap<>();
根据多年面试经验,我建议候选人:
在真实项目中,推荐根据场景选择:
最后分享一个实用技巧:在Python中如果需要保持字符顺序,可以用OrderedDict:
python复制from collections import OrderedDict
def ordered_char_count(text):
counter = OrderedDict()
for char in text:
counter[char] = counter.get(char, 0) + 1
return counter