【代码随想录 | day08】(JavaScript) 字符串的操作:反转字符串、替换空格、翻转字符串里的单词、左旋转字符串

  • 344.反转字符串
  • 541.反转字符串II
  • 剑指Offer 05.替换空格
  • 151.翻转字符串里的单词
  • 剑指Offer58-II.左旋转字符串

344.反转字符串

题目链接:344. 反转字符串 - 力扣(LeetCode)

能用库函数解决核心问题的,就不要用库函数,自己尝试着去写写。

我的解法

既然是反转字符,肯定是字符串的某个中点将两边进行一下交换。

【代码随想录 | day08】(JavaScript) 字符串的操作:反转字符串、替换空格、翻转字符串里的单词、左旋转字符串_第1张图片

var reverseString = function(s) {
    let flag = s.length - 1;
    let mid; // 字符串的中点
    if(s.length % 2 == 0) {  // 数组长度为偶数
        mid = s.length / 2;
    } else { // 数组长度为奇数
        mid = Math.floor(s.length / 2)
    }
    for(let i = 0; i < mid; i++) {
            let temp = s[i];
            s[i] = s[flag - i];
            s[flag - i] = temp;
        }
    return s
}

卡神解法

在反转链表中,使用了双指针的方法

那么反转字符串依然是使用双指针的方法,只不过对于字符串的反转,其实要比链表简单一些。

因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。

var reverseString = function(s) {
    //Do not return anything, modify s in-place instead.
    reverse(s)
};

var reverse = function(s) {
    let l = -1, r = s.length;
    while(++l < --r) [s[l], s[r]] = [s[r], s[l]];
};

541.反转字符串II

题目链接:541. 反转字符串 II - 力扣(LeetCode)

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

原本想着,遍历字符串中的每一个字母,当遍历到(i % (2 * k) == 0)个时,就让当前字母和下一个字母交换一下位置,但是发现打印出来行不通。

我的解法

原来反转部分字符串确实有些细节点要把握住。

比如有这样的一个字符串,长度为7,s='abcdefg'k=2那么每四个数处理一次,最后剩余 e, f, g直接进行反转。

刚开始去解题的时候,甚至想过单独写一个函数,实现字符串切出来的每切出来的 2k 个字符中前 k 个字符进行反转。后来发现要考虑的地方太多,而且代码比较冗余。后来参考了Carl老师的解题思路,自己去写了一下,终于AC了。

var reverseStr = function(s, k) {
    let strArr = s.split('');
    for(let i = 0; i < strArr.length; i += 2*k) {
        let len = (i + k) > strArr.length ? strArr.length - i : k;
        let left = i, right = i + len;
        while(left < right) {
            let temp = strArr[left];
            strArr[left] = strArr[right - 1];
            strArr[right - 1] = temp;
            left++;
            right--;
        }
    }
    return strArr.join('')
};

注意:reverse() 方法将数组中元素的位置颠倒,并返回该数组。

首先解题思想就是双指针法left指向要反转的字符串最左边,right指向要反转的字符串最右边。

  • for(let i = 0; i < strArr.length; i += 2*k)
    解读:每次处理的字母都是 2k 的倍数去处理,因为要找的也就是每2 * k 区间的起点,所以 for 循环里面不要习惯性的写 i++ ,这里应该写 i += 2*k。本题中就相当于每次取4个字母,让前两个位置反转一下。

  • (i + k) > strArr.length ? strArr.length - i : k
    解读:借用( s=‘abcdefghij’ k=4)来解释一下。取出8个字母,要反转前4个。那么第一次for循环的时候, left = strArr[0],经过三元运算可知,right = [4]。自己草稿纸上写写,就能知道接下来while循环可以把前4个字母反转过来。但是到第二次for循环的时候,只剩两个字母['i', 'j']。此时如果右边的指针还是从 i 的下标处(8)再加上4肯定会报错,所以要对 right 的位置进行处理。

    strArr.length - i即10-8=2,则len = 2,right = 10。长度为10的数组中,下标10是不存在的,所以写成strArr[right - 1]。交换的就是 strArr[8] 和 strArr[9] 的位置。

三元运算符(也称为条件运算符)可用于执行内联条件检查,而不是使用if...else语句。它使代码更短,更易读。它可用于根据条件为变量赋值,或根据条件执行表达式。

卡神解法

如果,i+k <= s.length 每次翻转,是反转 i 到 i+k 这一段

var reverseStr = function(s, k) {
    const len = s.length;
    let resArr = s.split(""); 
    for(let i = 0; i < len; i += 2 * k) {
        let l = i - 1, r = i + k > len ? len : i + k;
        while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
    }
    return resArr.join("");
};
  • 因为到尾部的时候,新的起点到结束,不够 k 个怎么办?如果直接对 i 到 i+k 进行反转,就操作空的数组了。
  • 当 i 到 i+k 的距离小于 k 时,则需要将当前的 i 到 s末尾的字母全部反转一下。

剑指Offer 05.替换空格

题目链接:https://leetcode.cn/problems/ti-huan-kong-ge-lcof/

我的解法

var replaceSpace = function(s) {
    let arr = s.split(' ');
    let newStr = arr.join('%20');
    return newStr
};

当时考虑的比较简单,直接使用库函数就解决了问题。实际上想要在算法上面有所进步,还是得思考一下库函数底层怎么实现的。

卡神解法

其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

有两个指针,left 指向旧长度的末尾,right 指向新长度的末尾。

 var replaceSpace = function(s) {
   // 字符串转为数组
  const strArr = Array.from(s);
  let count = 0;

  // 计算空格数量
  for(let i = 0; i < strArr.length; i++) {
    if (strArr[i] === ' ') {
      count++;
    }
  }

  let left = strArr.length - 1;
  let right = strArr.length + count * 2 - 1;

  while(left >= 0) {
    if (strArr[left] === ' ') {
      strArr[right--] = '0';
      strArr[right--] = '2';
      strArr[right--] = '%';
      left--;
    } else {
      strArr[right--] = strArr[left--];
    }
  }

  // 数组转字符串
  return strArr.join('');
};
  • let right = strArr.length + count * 2 - 1; 指向新长度的末尾
    为什么新数组的长度是strArr.length + count * 2?
    解读:如果说用 ‘%’ ‘2’ ‘0’ 填充进新数组的话,就算 百分号抵掉了一个空格的长度,那么 ‘2’ ‘0’ 还占了两个格子,所以新数组长度要加上 count * 2。同理,如果用’%’ ‘20’ 填充的话,新数组长度要加上 count,用来放 ‘20’

【代码随想录 | day08】(JavaScript) 字符串的操作:反转字符串、替换空格、翻转字符串里的单词、左旋转字符串_第2张图片


151.翻转字符串里的单词

题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)

我的解法(错)

先说一下我的思路,或许可以给小伙伴们提个醒。

  • 字符串转为数组,去掉空格。如果字符串是"We are one",那么就会变成['We', 'are', 'one']
var reverseWords = function (s) {
  let strArr = s.split(' ');
  let flag = strArr.length - 1;
  let mid; // 字符串的中点
  if (strArr.length % 2 == 0) {  // 数组长度为偶数
    mid = strArr.length / 2;
  } else { // 数组长度为奇数
    mid = Math.floor(strArr.length / 2)
  }
  for (let i = 0; i < mid; i++) {
    let temp = strArr[i];
    strArr[i] = strArr[flag - i];
    strArr[flag - i] = temp;
  }
  return strArr.join(' ')
};

如果遇到" hello world ",首尾各有两个空格,那么期望得到的是"world hello"

但是在代码第一步let strArr = s.split(' ');时,得到的就是['', '', 'hello', 'world', '', ''],只能去除中间的空格。

参考文章里面写了Array原生API,没有提到去除首尾空格的操作,所以具体怎么去除,还得看下面卡神的解法。

卡神解法

不要使用辅助空间,空间复杂度要求为O(1)。

将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"the sky is blue "

  • 移除多余空格 : “the sky is blue”
  • 字符串反转:“eulb si yks eht”
  • 单词反转:“blue is sky the”
 var reverseWords = function(s) {
   // 字符串转数组
   const strArr = Array.from(s);
   // 移除多余空格
   removeExtraSpaces(strArr);
   // 翻转
   reverse(strArr, 0, strArr.length - 1);

   let start = 0;

   for(let i = 0; i <= strArr.length; i++) {
     if (strArr[i] === ' ' || i === strArr.length) {
       // 翻转单词
       reverse(strArr, start, i - 1);
       start = i + 1;
     }
   }

   return strArr.join('');
};

// 删除多余空格
function removeExtraSpaces(strArr) {
  let slowIndex = 0;
  let fastIndex = 0;

  while(fastIndex < strArr.length) {
    // 移除开始位置和重复的空格
    if (strArr[fastIndex] === ' ' && (fastIndex === 0 || strArr[fastIndex - 1] === ' ')) {
      fastIndex++;
    } else {
      strArr[slowIndex++] = strArr[fastIndex++];
    }
  }

  // 移除末尾空格
  strArr.length = strArr[slowIndex - 1] === ' ' ? slowIndex - 1 : slowIndex;
}

// 翻转从 start 到 end 的字符
function reverse(strArr, start, end) {
  let left = start;
  let right = end;

  while(left < right) {
    // 交换
    [strArr[left], strArr[right]] = [strArr[right], strArr[left]];
    left++;
    right--;
  }
}

剑指Offer58-II.左旋转字符串

题目链接:剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

我的解法

在下不才,目前能想到的解法就是如下:

  • 字符串 转为 数组。用for循环不断删除数组第一个数,然后再压入数组末尾。
var reverseLeftWords = function(s, n) {
    let strArr = Array.from(s);
    let leftTR = [];
    for(let i = 0; i < n; i++) {
        leftTR.push(strArr.shift())
        
    }
    // console.log(strArr, leftTR)
    let comBine = [...strArr, ...leftTR]
    return comBine.join('')
};

卡神解法

为了让本题更有意义,提升一下本题难度:不能申请额外空间,只能在本串上操作

具体步骤为:

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串
var reverseLeftWords = function (s, n) {
    /** Utils */
    function reverseWords(strArr, start, end) {
        let temp;
        while (start < end) {
            temp = strArr[start];
            strArr[start] = strArr[end];
            strArr[end] = temp;
            start++;
            end--;
        }
    }
    /** Main code */
    let strArr = s.split('');
    let length = strArr.length;
    reverseWords(strArr, 0, length - 1);
    reverseWords(strArr, 0, length - n - 1);
    reverseWords(strArr, length - n, length - 1);
    return strArr.join('');
};

参考文章

  • javascript字符串中指定字符怎么删除-js教程-PHP中文网
  • JavaScript 删除或抽取字符串指定字符的几种方法,极为常用“建议收藏”
  • 字符串反转的三种方式
  • JavaScript String 参考手册 (w3school.com.cn)
  • js数组怎么转为字符串

你可能感兴趣的:(javascript,算法,leetcode)