LeetCode 451 - 根据字符出现频率排序


文章目录

摘要

这道题看起来很简单:
统计字符出现次数,然后按次数排序。

但如果你真在工程里做过类似的事,比如:

  • 搜索关键词权重排序
  • 日志里统计最常出现的错误码
  • 文本分析中提取高频字符或 Token

你会发现这类问题的核心,其实是「频率统计 + 排序策略」。

LeetCode 451 正好是一个非常干净、非常标准的模板题,非常适合用来练:

  • 字符统计
  • 排序规则设计
  • Swift 中 Dictionary + Array 的组合用法

描述

题目给你一个字符串 s,要求你:

  • 统计每个字符在字符串中出现的次数
  • 按出现频率 从高到低排序
  • 相同字符必须放在一起
  • 如果频率相同,字符之间的相对顺序不重要

需要注意的几个点:

  • 大写字母和小写字母是 不同字符
  • 字符串长度最大可以到 5 * 10^5
  • 所以实现不能太"暴力"

题解答案(整体思路)

这道题的解法其实非常清晰,可以拆成三步:

第一步:统计字符频率

遍历字符串,用一个字典:

swift 复制代码
[Character: Int]

来记录每个字符出现的次数。

第二步:按频率排序

把字典转成数组:

swift 复制代码
[(Character, Int)]

然后按 value(出现次数)做降序排序。

第三步:按排序结果拼接字符串

排序完成后,按顺序把字符重复 count 次,拼接成最终字符串。

题解代码(Swift 可运行 Demo)

下面是完整、可直接运行的 Swift 实现:

swift 复制代码
class Solution {
    func frequencySort(_ s: String) -> String {
        // 1. 统计字符频率
        var freq: [Character: Int] = [:]
        for ch in s {
            freq[ch, default: 0] += 1
        }
        
        // 2. 按出现次数降序排序
        let sorted = freq.sorted { $0.value > $1.value }
        
        // 3. 构造结果字符串
        var result = ""
        for (ch, count) in sorted {
            result += String(repeating: ch, count: count)
        }
        
        return result
    }
}

题解代码分析

1. 为什么用 Dictionary 统计?

swift 复制代码
var freq: [Character: Int] = [:]

这是最自然、也最直观的方式:

  • key 是字符
  • value 是出现次数

Swift 的 Dictionary 对这种计数场景支持得非常友好。

2. 排序这一步在干什么?

swift 复制代码
let sorted = freq.sorted { $0.value > $1.value }

sorted 之后的数据结构其实是:

swift 复制代码
[(Character, Int)]

也就是一个 (字符, 次数) 的数组。

排序规则很简单:

  • 谁出现次数多,谁排前面

3. 为什么不能一边统计一边排序?

因为:

  • 统计是 O(n)
  • 排序是 O(k log k),k 是不同字符数量

混在一起只会让逻辑变复杂,不会更快。

4. 字符拼接这一步为什么这样写?

swift 复制代码
result += String(repeating: ch, count: count)

这一步非常直观:

  • 一个字符出现几次,就拼接几次
  • 保证相同字符一定是连续的

同时也满足了题目「相同字母必须放在一起」的要求。

示例测试及结果

示例 1

swift 复制代码
let solution = Solution()
print(solution.frequencySort("tree"))

输出可能是:

复制代码
eert

或者:

复制代码
eetr

都是正确结果。

示例 2

swift 复制代码
print(solution.frequencySort("cccaaa"))

输出:

复制代码
cccaaa

或者:

复制代码
aaaccc

示例 3

swift 复制代码
print(solution.frequencySort("Aabb"))

输出:

复制代码
bbAa

注意这里:

  • 'A''a' 是不同字符
  • 大小写不会混在一起

实际场景结合

这道题的模式在实际开发中非常常见,比如:

  • 搜索系统里,统计关键词出现频率
  • 日志分析中,找最常见的错误类型
  • 文本分析中,做简单的词频或字符分布

把这套逻辑稍微改一下,就可以变成:

  • Top K 高频元素
  • 热词统计
  • 标签权重排序

时间复杂度

  • 统计频率:O(n)
  • 排序(字符种类数为 k):O(k log k)
  • 构造字符串:O(n)

总体时间复杂度:

复制代码
O(n + k log k)

在实际情况下,k 通常远小于 n

空间复杂度

  • 字典存储频率:O(k)
  • 排序数组:O(k)
  • 结果字符串:O(n)

空间复杂度:

复制代码
O(n + k)

总结

LeetCode 451 是一道非常「工程友好」的题:

  • 思路清晰
  • 模板性强
  • 非常适合拿来练 Swift 的集合操作
相关推荐
JoannaJuanCV2 小时前
自动驾驶—CARLA仿真(17)invertedai_traffic demo
人工智能·算法·自动驾驶·carla
浔川python社2 小时前
C++小程序编写系列(2)
c++·算法·图论
LYFlied2 小时前
【每日算法】LeetCode 22. 括号生成
数据结构·算法·leetcode·面试·职场和发展
桓琰2 小时前
非线性滤波——基于EKF的INS/GPS松组合算法的研究(直接法|EKF|欧拉角)
算法·matlab·卡尔曼滤波算法
想自律的露西西★2 小时前
js.39. 组合总和
前端·javascript·数据结构·算法
johnny2332 小时前
Raft算法理解
算法
zore_c2 小时前
【数据结构】栈——超详解!!!(包含栈的实现)
c语言·开发语言·数据结构·经验分享·笔记·算法·链表
Chen--Xing2 小时前
LeetCode 15.三数之和
c++·python·算法·leetcode·rust
月明长歌2 小时前
【码道初阶】【Leetcode105&106】用遍历序列还原二叉树:前序+中序、后序+中序的统一套路与“先建哪边”的坑
java·开发语言·数据结构·算法·leetcode·二叉树