1. 字符串操作基础与双指针技巧
字符串处理是算法面试中的高频考点,而双指针技巧则是解决这类问题的利器。在实际开发中,字符串反转、替换等操作也经常出现在数据处理、文本解析等场景中。
1.1 字符串与数组的关系
字符串本质上是一个字符数组,在Java中表现为char[]。理解这一点很重要,因为这意味着我们可以使用数组操作来处理字符串问题。例如:
java复制String s = "hello";
char[] chars = s.toCharArray(); // 转为字符数组
这种转换让我们能够利用数组的随机访问特性(O(1)时间复杂度访问任意元素),这在处理字符串问题时非常有用。
1.2 双指针法的核心思想
双指针法通过维护两个指针(通常是索引)来协同工作,常见的有以下几种模式:
- 对向指针:一个指针从起始位置开始,另一个从末尾开始,向中间移动(如反转字符串)
- 同向快慢指针:两个指针从同一端出发,以不同速度移动(如删除重复元素)
- 滑动窗口:维护一个窗口,通过调整左右指针来满足特定条件
提示:在处理字符串/数组问题时,先思考是否可以用双指针法,这通常能带来O(n)时间复杂度的解决方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 字符串反转问题详解
2.1 简单反转(344题)
这是最基本的反转问题,要求原地修改输入数组。双指针解法如下:
java复制public void reverseString(char[] s) {
int left = 0, right = s.length - 1;
while (left < right) {
// 交换左右指针指向的字符
char temp = s[left];
s[left] = s[right];
s[right] = temp;
// 移动指针
left++;
right--;
}
}
时间复杂度分析:O(n),其中n是字符串长度。我们只需遍历一半的数组。
空间复杂度:O(1),只使用了常数级别的额外空间。
2.2 进阶反转(541题)
这个问题增加了间隔反转的条件,需要更精细的指针控制:
java复制public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i += 2 * k) {
int start = i;
// 确定反转的结束位置
int end = Math.min(ch.length - 1, start + k - 1);
// 执行反转
while (start < end) {
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
return new String(ch);
}
关键点:
- 每次前进2k个字符
- 只反转前k个字符
- 处理剩余字符不足k个的情况
注意:边界条件的处理是这类问题的难点,务必仔细检查指针的移动和终止条件。
3. 字符串替换问题实战
3.1 问题分析与解法选择
将数字替换为"number"的特殊之处在于:
- 替换后的字符串长度会增加
- 需要原地修改(或最小化额外空间使用)
为什么选择从后向前处理?
- 从前向后处理会导致每次插入都需要移动后面所有元素,时间复杂度升至O(n²)
- 从后向前可以避免这种重复移动,保持O(n)时间复杂度
3.2 具体实现步骤
java复制import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
int len = s.length();
// 第一步:计算新长度
for (int i = 0; i < s.length(); i++) {
if (Character.isDigit(s.charAt(i))) {
len += 5; // "number"比数字多5个字符
}
}
// 第二步:创建足够大的数组
char[] ret = new char[len];
// 第三步:初始化数组(复制原字符串)
for (int i = 0; i < s.length(); i++) {
ret[i] = s.charAt(i);
}
// 第四步:从后向前替换
int i = s.length() - 1, j = len - 1;
while (i >= 0) {
if (Character.isDigit(ret[i])) {
// 反向填充"number"
ret[j--] = 'r';
ret[j--] = 'e';
ret[j--] = 'b';
ret[j--] = 'm';
ret[j--] = 'u';
ret[j--] = 'n';
} else {
ret[j--] = ret[i];
}
i--;
}
System.out.println(new String(ret));
}
}
优化技巧:
- 使用
Character.isDigit()方法判断数字更可靠 - 可以用System.arraycopy()替代手动复制原字符串
- 替换"number"时可以预先存储字符数组提高效率
4. 常见问题与调试技巧
4.1 边界条件处理
在字符串问题中,常见的边界情况包括:
- 空字符串
- 只有一个字符的字符串
- 全数字或全字母的字符串
- k=0或k=1的特殊情况
调试方法:
- 打印指针位置和数组状态
- 使用小规模测试用例(如k=1)
- 检查循环终止条件
4.2 性能优化
对于大规模字符串处理:
- 尽量减少不必要的字符串转换
- 使用StringBuilder代替字符串拼接
- 预先计算需要的空间大小
- 考虑多线程处理(对于超长字符串)
4.3 代码可读性提升
- 为指针变量起有意义的名字(如readPos/writePos)
- 提取重复逻辑为方法
- 添加注释说明关键步骤
- 使用常量代替魔法数字(如"number"的长度6)
5. 扩展应用与实际场景
5.1 实际开发中的应用
- 数据清洗:处理用户输入,规范化字符串格式
- 文本编辑器:实现查找替换功能
- 数据压缩:运行长度编码等算法
- 解析器开发:处理标记和符号
5.2 类似问题推荐
- 反转字符串中的单词(保留单词顺序)
- 压缩字符串(如aaabbbcc→a3b3c2)
- 验证回文字符串(考虑大小写和标点)
- 字符串的排列组合问题
5.3 进阶学习方向
- KMP算法(字符串匹配)
- 后缀数组和Trie树
- 正则表达式引擎实现
- 编辑距离问题
在实际面试中,字符串问题常常作为考察候选人编码能力和边界条件处理能力的题目。掌握双指针技巧不仅能解决这类问题,还能培养出更高效的算法思维。建议在理解这些基础问题后,尝试解决更复杂的字符串处理问题,如实现基本的正则表达式匹配或字符串压缩算法。
