LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序
题目
解题
解题一:使用语言特性
// javascript
var reverseWords = function(s) {
return s.trim().split(/\s+/).reverse().join(' ');
};
解题二:队列
// javascript
var reverseWords = function(s) {
const n = s.length;
let left = 0, right = n - 1;
while (left <= right && s[left] === ' ') { // 去掉字符串开头的空白字符
left++;
}
while (left <= right && s[right] === ' ') { // 去掉字符串末尾的空白字符
right--;
}
let word = [];
const queue = [];
while (left <= right) {
if (s[left] === ' ' && word.length > 0) { // 如果 left 指向空格但是 word 为空:该空格是多余的
queue.unshift(word.join('')); // 将单词拼接插入队列的头部
word = []; // 将单词置空
} else if (s[left] !== ' ') {
word.push(s[left]); // 将字符 push 进 word
}
left++;
}
// 记得要把最后一个单词拼接插入队列的头部
// 因为最后一个单词后面没有空格:字符串末尾的空白字符已经在上一个 while 循环内被跳过
queue.unshift(word.join(''));
return queue.join(' ');
};
或者:
// javascript
var reverseWords = function(s) {
const n = s.length;
let left = 0, right = n - 1;
while (left <= right && s[left] === ' ') { // 去掉字符串开头的空白字符
left++;
}
while (left <= right && s[right] === ' ') { // 去掉字符串末尾的空白字符
right--;
}
let i = right, j = right;
const queue = [];
while (i >= left) {
while (i >= left && s[i] !== ' ') { // 搜索前一个空格
i--;
}
queue.push(s.slice(i + 1, j + 1)); // 添加单词
while (i >= left && s[i] === ' ') { // 跳过单词间空格
//if (queue[queue.length - 1] !== ' ') {
// queue.push(' ');
//}
i--;
}
j = i; // j 指向下个单词的尾字符
}
return queue.join(' '); // 拼接并返回,用空格拼接
//return queue.join('');
};
解题三:原地翻转
因为部分语言(含 JS)字符串是不可改变的(immutable),所以 O ( 1 ) O(1) O(1) 额外空间复杂度的原地解法是不可能实现的。在这里用字符数组来模拟一下原地翻转的操作(除了 arr 字符数组外,空间复杂度为 O(1),如果字符串允许被改变,可以直接在 s 字符串上完成对 arr 字符数组的操作)。
// javascript
var reverseWords = function(s) {
const n = s.length;
const arr = s.split(''); // 转换成数组
reverse(arr, 0, n - 1); // 整个数组反转
let start = 0; // start 记录处理后每个单词首字母的位置
let fast = 0, slow = 0; // 用快慢指针把 ' ' 移到非空格的后面(空格移到数组末尾)
while (fast < n) {
// fast 寻找下一个非空格元素
if (arr[fast] !== ' ') {
// 如果[0, fast]没有空格,slow、fast相等 -> 不交换,slow、fast移动后指向下一个需要判断的元素
// 否则,将slow指向的空格和fast指向的非空格交换,[slow+1,fast-1]都是空格,slow指向下一个空格
if (fast !== slow) {
[arr[slow], arr[fast]] = [arr[fast], arr[slow]];
}
slow++;
// 如果当前非空格元素的后一个元素是空格:说明 [start, slow - 1] 是一个单词(因为 slow++ 了)
if (fast < n - 1 && arr[fast + 1] === ' ') {
reverse(arr, start, slow - 1); // 单词反转
slow++; // 单词之间需要空格,保留一个空格
start = slow; // start 指向处理后下个单词首字母的位置
}
}
fast++;
}
if (arr[slow - 1] === ' ') { // 最后一个单词后面有多余空格导致它后面也保留了一个空格-删除
slow--;
} else { // 否则最后一个单词后没有空格,在循环里未触发反转条件,要记得反转
reverse(arr, start, slow - 1);
}
return arr.slice(0, slow).join(''); // [0, slow - 1] 是想要的部分,截取后拼接返回
};
const reverse = (arr, left, right) => {
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
};