344. 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
示例 2:
输入:s = [“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]
方法一:双指针
class Solution {
public void reverseString(char[] s) {
int n = s.length;
for (int left = 0, right = n - 1; left < right; ++left, --right) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
}
}
}
541. 反转字符串 II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = “abcdefg”, k = 2
输出:“bacdfeg”
示例 2:
输入:s = “abcd”, k = 2
输出:“bacd”
class Solution {
public String reverseStr(String s, int k) {
int n = s.length();
char[] arr = s.toCharArray();
for (int i = 0; i < n; i += 2 * k) {
reverse(arr, i, Math.min(i + k, n) - 1);
}
return new String(arr);
}
public void reverse(char[] arr, int left, int right) {
while (left < right) {
char temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right--;
}
}
}
这段代码定义了一个名为Solution
的类,该类中包含两个方法:reverseStr
和reverse
。这些方法共同实现了一个功能,即按照给定的规则反转字符串中的字符。
方法说明:
-
reverseStr(String s, int k):
- 输入: 一个字符串
s
和一个整数k
。 - 输出: 根据规则反转字符串
s
后的新字符串。 - 规则: 将字符串
s
分成长度为k
的子串,反转每个子串中的字符,如果子串长度不足k
则不反转剩余部分。例如,s="abcdefg"
和k=2
,结果为"bacdfeg"
。
- 输入: 一个字符串
-
reverse(char[] arr, int left, int right):
- 输入: 字符数组
arr
以及左右指针left
和right
。 - 功能: 反转数组中从
left
到right
之间的字符(含两端)。 - 实现: 使用双指针技术,交换左右指针所指的字符,直至两指针相遇。
- 输入: 字符数组
代码流程示例:
假设调用reverseStr("abcdefg", 2)
:
- 字符串转为字符数组
['a', 'b', 'c', 'd', 'e', 'f', 'g']
。 - 第一轮,反转前2个字符:
['b', 'a', 'c', 'd', 'e', 'f', 'g']
。 - 接下来是3个字符,不足4,反转前2个字符:
['b', 'a', 'c', 'd', 'f', 'e', 'g']
。 - 最终字符数组转回字符串,结果为
"bacdfeg"
。
总结:
这段代码通过分段反转字符串中字符的方式来实现一个特定规则的字符串反转操作,利用了双指针技巧高效地反转字符数组中的子区间,并通过主方法的逻辑控制反转的范围和频率,实现题目要求的功能。
151. 反转字符串中的单词
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = “the sky is blue”
输出:“blue is sky the”
示例 2:
输入:s = " hello world "
输出:“world hello”
解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
方法一:自行编写对应的函数
class Solution {
public String reverseWords(String s) {
StringBuilder sb = trimSpaces(s);
// 翻转字符串
reverse(sb, 0, sb.length() - 1);
// 翻转每个单词
reverseEachWord(sb);
return sb.toString();
}
public StringBuilder trimSpaces(String s) {
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}
// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}
// 将字符串间多余的空白字符去除
StringBuilder sb = new StringBuilder();
while (left <= right) {
char c = s.charAt(left);
if (c != ' ') {
sb.append(c);
} else if (sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}
++left;
}
return sb;
}
public void reverse(StringBuilder sb, int left, int right) {
while (left < right) {
char tmp = sb.charAt(left);
sb.setCharAt(left++, sb.charAt(right));
sb.setCharAt(right--, tmp);
}
}
public void reverseEachWord(StringBuilder sb) {
int n = sb.length();
int start = 0, end = 0;
while (start < n) {
// 循环至单词的末尾
while (end < n && sb.charAt(end) != ' ') {
++end;
}
// 翻转单词
reverse(sb, start, end - 1);
// 更新start,去找下一个单词
start = end + 1;
++end;
}
}
}
这段代码定义了一个名为 Solution
的类,其中包含了几个方法,旨在实现一个功能:反转字符串中的每个单词,同时去除多余的空格。具体分析如下:
方法概览
-
reverseWords(String s): 主方法,接收一个字符串
s
,处理后返回翻转单词后的字符串。- 首先调用
trimSpaces
去除前后空格及多余空格,然后对整个处理后的字符串进行翻转。 - 再调用
reverseEachWord
对每个单词进行翻转,恢复单词的正序。 - 最后返回处理好的字符串。
- 首先调用
-
trimSpaces(String s): 去除字符串首尾空格和中间多余的空格。
- 初始化左右指针,定位首尾。
- 移除首部空格。
- 移除尾部空格。
- 遍历,去除多余空格,构建新字符串。
- 返回处理后的
StringBuilder
。
-
reverse(StringBuilder sb, int left, int right): 反转
StringBuilder
中从left
到right
的子串。- 交换首尾指针内的字符直至相遇。
-
reverseEachWord(StringBuilder sb): 反转每个单词。
- 初始化指针,遍历字符串。
- 找到单词末尾,调用
reverse
反转单词。 - 更新指针,继续找下一个单词。
逻辑分析
- 整体流程:先通过
trimSpaces
处理字符串,确保处理后的字符串干净无多余空格且方便操作;接着通过一次全字符串翻转确保单词的逆序状态,此时单词内部是反的,但相邻单词间的顺序是对的;最后通过reverseEachWord
再次翻转每个单词,恢复单词内部顺序,得到最终结果。 - 注意事项:
trimSpaces
中处理字符串时,不仅要删除前后空格,还需压缩中间的连续空格为单个空格,确保单词间仅有一个空格间隔。
结论
这段代码实现了一个较为复杂的字符串处理逻辑,包括去除多余空格、翻转单词等,通过分步骤逐步构建出最终符合要求的字符串结果。逻辑较为清晰,但注意在实际应用中对边界条件和细节的处理,如 trimSpaces
中的错误复制粘贴问题(原代码中多个字符错写为 ‘)’ 应为 ’ ’ ')。
方法二:双端队列
class Solution {
public String reverseWords(String s) {
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}
// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}
Deque<String> d = new ArrayDeque<String>();
StringBuilder word = new StringBuilder();
while (left <= right) {
char c = s.charAt(left);
if ((word.length() != 0) && (c == ' ')) {
// 将单词 push 到队列的头部
d.offerFirst(word.toString());
word.setLength(0);
} else if (c != ' ') {
word.append(c);
}
++left;
}
d.offerFirst(word.toString());
return String.join(" ", d);
}
}
这段代码实现了一个方法,用于反转字符串中的单词顺序,并去除多余的空格。具体代码逻辑如下:
1. 去除开头和末尾的空白字符
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}
// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}
首先,通过 left
和 right
两个指针分别从字符串的左侧和右侧开始遍历,将开头和末尾的空白字符去除。
2. 使用双端队列存储单词
Deque<String> d = new ArrayDeque<String>();
StringBuilder word = new StringBuilder();
创建一个双端队列 d
和一个 StringBuilder
对象 word
用来构建单词。
3. 遍历字符串并提取单词
while (left <= right) {
char c = s.charAt(left);
if ((word.length() != 0) && (c == ' ')) {
// 将单词 push 到队列的头部
d.offerFirst(word.toString());
word.setLength(0);
} else if (c != ' ') {
word.append(c);
}
++left;
}
d.offerFirst(word.toString());
从 left
指针位置开始遍历字符串:
- 如果当前字符
c
是空格,并且word
中已经有字符了,将word
中的单词加入到队列的头部 (offerFirst
方法) ,并清空word
以构建下一个单词。 - 如果当前字符
c
不是空格,将其加入word
中。
4. 将最后一个单词加入队列
d.offerFirst(word.toString());
遍历结束后,将最后一个构建好的单词加入队列。
5. 拼接结果字符串
return String.join(" ", d);
使用 String.join(" ", d)
方法,将队列中的单词用空格连接起来,构成最终的结果字符串。
整体效果
通过上述步骤,代码将输入字符串中的单词顺序反转,并去掉多余的空格,返回处理后的字符串。
示例
假设输入字符串为 " the sky is blue "
- 去除开头和末尾空格后变为
"the sky is blue"
- 提取单词并加入队列后,队列内容为
["blue", "is", "sky", "the"]
- 最终拼接成字符串
"blue is sky the"
因此,返回的结果为 "blue is sky the"
。