1. 题目链接
LeetCode 面试题 01.01. 判定字符是否唯一
2. 题目描述
实现一个算法,确定一个字符串的所有字符是否全部唯一(即没有重复字符)。要求如下:
- 不使用额外的数据结构(如哈希表)
- 字符串仅包含小写字母
a-z
示例:
- 输入:
"abc"
→ 输出:true
- 输入:
"aba"
→ 输出:false
3. 示例分析
- 无重复字符:
"leetcode"
→false
('e'
重复)"abc"
→true
- 边界情况:
- 空字符串
""
→true
- 字符串长度超过 26 →
false
(26 个小写字母最多只能有 26 个唯一字符)
- 空字符串
4. 算法思路
核心思想:位图法(Bitmask)
- 位图表示:
- 使用一个整数
flag
的二进制位表示字符是否出现过。 - 每个二进制位对应一个小写字母,例如:
a
→ 第 0 位b
→ 第 1 位- …
z
→ 第 25 位
- 使用一个整数
- 遍历字符串:
- 若当前字符对应的二进制位为
1
,说明已重复,返回false
。 - 否则,将该位设为
1
,继续检查下一个字符。
- 若当前字符对应的二进制位为
优化点:
- 预判长度超过 26:若字符串长度超过 26,直接返回
false
(鸽巢原理)。 - 时间复杂度:O(n),空间复杂度:O(1)(仅需一个整数)。
5. 边界条件与注意事项
- 字符串长度:
- 若长度超过 26,直接返回
false
。 - 空字符串直接返回
true
。
- 若长度超过 26,直接返回
- 字符范围:
- 题目假设字符均为小写字母,若存在其他字符(如大写字母或符号),需预处理为小写。
- 位运算溢出:
- 移位操作
1 << i
中i
的范围需为 0~25,否则可能导致整数溢出。
- 移位操作
6. 代码实现
class Solution {
public:
bool isUnique(string astr)
{
if (astr.size() > 26) return false; // 鸽巢原理优化
int flag = 0; // 位图初始化
for (char ch : astr)
{
int i = ch - 'a'; // 计算字符对应的二进制位
if ((flag >> i) & 1)
{ // 判断该位是否为1
return false;
}
flag |= (1 << i); // 将该位设为1
}
return true;
}
};
与其他方法的对比
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
位图法 | O(n) | O(1) | 仅限小写字母 |
哈希表法 | O(n) | O(26) | 所有字符类型 |
数组计数法 | O(n) | O(26) | 字符范围明确且较小 |
暴力枚举法 | O(n²) | O(1) | 极短字符串(n ≤ 10) |
关键代码解析
-
位运算检查重复:
if ((flag >> i) & 1) // 判断第i位是否为1
- 将
flag
右移i
位后,与1
按位与,结果为1
则说明该位已被占用。
- 将
-
位图更新:
flag |= (1 << i); // 将第i位设为1
- 将
1
左移i
位后,与flag
按位或,确保第i
位被标记为已使用。
- 将
总结
位图法通过巧妙的位运算,将空间复杂度降至 O(1),同时保持线性时间复杂度。在面对限定字符范围的问题时(如小写字母、数字等),位图法是最优解之一。其核心在于将字符映射到整数的二进制位上,利用位运算的原子性快速判断重复性。实际应用中,需注意字符范围与整数位数的限制(例如,32 位整数最多覆盖 32 种字符)。