解法一
实现思想,就是先整个的把字符串倒转过来,如 the sky is blue
直接倒转成 eulb si yks eht
,然后,再单独把每个单词还原即可。另外还需要注意说明中的条件。
// 执行用时:4 ms
public String reverseWords(String s) {
if (s==null||s.length()==0) return "";
char[] chars = s.toCharArray();
for (int i = 0, j = chars.length - 1; i < j; i++, j--) {
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
// 将每个被反转的单词还原
for (int i = 0; i < chars.length; i++) {
if (chars[i]!=' ') {
int j = i + 1;
while (j < chars.length && chars[j] != ' ') ++j;
--j;
if (i != j) {
for (int k = i,l = j; k <l ; k++,l--) {
char tmp = chars[k];
chars[k] = chars[l];
chars[l] = tmp;
}
i = j;
}
}
}
StringBuilder builder = new StringBuilder();
// 用 left、rught 两个指针先排除两端的 0 元素
int left = 0;
// 要防止数组越界异常
while (left<chars.length&&chars[left]== ' ') ++left;
int right = chars.length - 1;
while (right>=0&&chars[right]== ' ') --right;
// 用一个标识位排除连续的多位空格
boolean flag = true;
for (; left <= right; left++) {
char c = chars[left];
if (c ==' '&& flag) {
builder.append(c);
flag = false;
} else {
builder.append(c);
flag = true;
}
}
return builder.toString();
}
或者是基于上述思想,但是对于空格的处理是基于 chars 进行原地操作。
// 执行用时:2 ms
public String reverseWords2(String s) {
if (s == null || s.length() == 0) return "";
char[] chars = s.toCharArray();
for (int i = 0, j = chars.length - 1; i < j; i++, j--) {
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
for (int i = 0; i < chars.length; i++) {
if (chars[i] != ' ') {
int j = i + 1;
while (j < chars.length && chars[j] != ' ') ++j;
--j;
if (i != j) {
for (int k = i, l = j; k < l; k++, l--) {
char tmp = chars[k];
chars[k] = chars[l];
chars[l] = tmp;
}
i = j;
}
}
}
int left = 0;
int right = chars.length - 1;
// 要防止数组越界异常
while (left < chars.length && chars[left] == ' ') ++left;
while (right > left && chars[right] == ' ') --right;
// 将多个连续空格中多余的用之后的单词填充
int i = left;
while (i <= right) {
if (chars[i] == ' ') {
int tmp2 = i + 1;
while (tmp2 <= right && chars[tmp2] == ' ') ++tmp2;
// 如果有多个连续的空格的话
if (tmp2 - i > 1) {
++i;//将 i 指向连续空格的第二个开始
while (tmp2 <= right && chars[tmp2] != ' ') {
chars[i] = chars[tmp2];
chars[tmp2] = ' ';
// 表示到了末尾,所以直接缩小 right 的范围
if (tmp2 == right) {
right = i;
}
++i;
++tmp2;
}
} else {
// 如果没有多个连续空格,也要继续向右移动 i,
// 否则对于 chars[i] == ' ' && tmp2 - i <= 1时, i 一直会在原地不变
++i;
}
} else ++i;
}
return new String(chars, left, right - left + 1);
}
解法二
还有一种实现思路就是开辟一个辅助数组,从末尾遍历 s,边遍历,边倒转填充到辅助数组中。
// 执行用时:5 ms
public String reverseWords(String s) {
if (s == null || s.length() == 0) return "";
int len = s.length();
int left = 0;
int right = s.length() - 1;
// 得到两边有效的起始边界
while (left < len && s.charAt(left) == ' ') ++left;
while (right >= 0 && s.charAt(right) == ' ') --right;
// 这种情况就表示 s 中只有空格
if (left > right) return "";
// 直接开辟辅助数组去存储结果
char[] chars = new char[right - left + 1];
int i = 0;
boolean flag = true;
while (true) {
if (right < left) break;
// 针对 s 从末尾向前检测,如果检测到有空格或者 在 到来左边有效开始边界时
// 当 right != left 时,
// 需要注意的是 s.charAt(right) == ' ' 与 flag 不能同时判断,
// 如 s.charAt(right) == ' ' && flag
// 因为这样 flag 为 false 时会直接进入到 1 的 else 逻辑,将 flag 重置为 false
// 此时如果对于对于的空格就会导致 s.charAt(right) == ' ' && flag 成立了
if (s.charAt(right) == ' ' || right == left) {
if (flag || right == left) {
flag = false;
// 如果是在到了左边有效边界,那就不需要 +1 而实现移动到该段单词的首个字母
// 否则 s.charAt(right) == ' ' 则需要 +1 移动到单词的首个字母
int j = (right == left) ? right : right + 1;
for (; j < len && s.charAt(j) != ' '; j++) {
chars[i++] = s.charAt(j);
}
if (right != left) chars[i++] = ' ';
}
} else { // 1
flag = true;
}
--right;
}
return new String(chars, 0, i);
}
解法三
是解法二的递归实现,代码很简洁,参考自提交记录 执行用时为 1 ms 的范例
public static String reverseWords(String s) {
if (s==null||s.length()==0) return s;
char[] array = s.toCharArray();
char[] res = new char[array.length];
int len = helper(array, array.length - 1, res, 0, 0);
return new String(res, 0, len);
}
private int helper(char[] array, int r, char[] res, int l, int len) {
// 从后面往前遍历时,先排除空格
while (r >= 0 && array[r] == ' ') r--;
if (r < 0) return Math.max(0, len - 1);
int right = r;
// 得到该段单词的起始位置
while (r >= 0 && array[r] != ' ') r--;
// right-r 是该段单词的长度, +1 是加上一个空格的长度
// 因此在最后在末尾会多了一个空格
// 所以在递归 return 的时候是 len-1 不包括这个多余的空格
len += right - r + 1;
for (int left = r + 1; left <= right; left++, l++)
res[l] = array[left];
if (l < res.length)
res[l++] = ' ';
return helper(array, r, res, l, len);
}