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 的集合操作
相关推荐
-dzk-4 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
风筝在晴天搁浅4 小时前
hot100 78.子集
java·算法
Jasmine_llq4 小时前
《P4587 [FJOI2016] 神秘数》
算法·倍增思想·稀疏表(st 表)·前缀和数组(解决静态区间和查询·st表核心实现高效预处理和查询·预处理优化(提前计算所需信息·快速io提升大规模数据读写效率
超级大只老咪4 小时前
快速进制转换
笔记·算法
m0_706653235 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你915 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
qq_423233905 小时前
C++与Python混合编程实战
开发语言·c++·算法
TracyCoder1235 小时前
LeetCode Hot100(19/100)——206. 反转链表
算法·leetcode
m0_715575345 小时前
分布式任务调度系统
开发语言·c++·算法