LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序

LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序

  • 题目
  • 解题
    • 解题一:使用语言特性
    • 解题二:队列
    • 解题三:原地翻转

题目

LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序_第1张图片

解题

解题一:使用语言特性

LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序_第2张图片

// javascript
var reverseWords = function(s) {
    return s.trim().split(/\s+/).reverse().join(' ');
};

在这里插入图片描述

解题二:队列

LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序_第3张图片

// 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(' ');
};

或者:
LeetCode151 翻转字符串里的单词 & 剑指Offer 58 - I 翻转单词顺序_第4张图片

// 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--;
    }
};

你可能感兴趣的:(剑指,Offer,刷题笔记,字符串,双指针)