代码随想录day8| 344.反转字符串、383. 赎金信、541. 反转字符串II、 151.翻转字符串里的单词

70 阅读3分钟

344.反转字符串

image.png

思路: 不使用reverse()函数的话其实就是自己实现一下,不使用另外的数组,直接更改数组反转相当于把位置 i和位置 len -i-1的元素互换

/**
 * @param {character[]} s
 * @return {void} Do not return anything, modify s in-place instead.
 */
var reverseString = function(s) {
    const n = s.length
    for(let i= 0;i<n / 2;i++){
        [s[i] , s[n -1 -i]] = [ s[n -1 -i], s[i]]
    }
    return s
};

541. 反转字符串II

image.png 思路:把字符串按照2k长度分隔,作为数组元素放入数组,对每个元素进行前k个字符的反转,再拼接

const reverse = (s) => {
    const arr = s.split('')
    const len = arr.length
    for (let i = 0; i < len / 2; i++) {
        [arr[i], arr[len - 1 - i]] = [arr[len - 1 - i], arr[i]]
    }
    return arr.join('')
}
var reverseStr = function (s, k) {
    let sArr = []
    let i = 0
    while (s.length > 2 * i * k) {
        sArr.push(s.slice(2 * i * k, 2 * i * k + 2 * k))
        i++
    }
    sArr = sArr.map(item => {
        let s1 = reverse(item.slice(0, k))
        const s2 = item.slice(k, item.length)
        return `${s1}${s2}`
    })
    return sArr.join('')
};

另一种方法:将整个字符串split成数组,对数组元素的值进行交换

/**
 * @param {string} s
 * @param {number} k
 * @return {string}
 */
var reverseStr = function(s, k) {
    const len = s.length;
    let resArr = s.split(""); 
    for(let i = 0; i < len; i += 2 * k) {  // 每隔 2k 个字符的前 k 个字符进行反转
        let l = i - 1
        let r = i + k > len ? len : i + k;
        while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
    }
    return resArr.join("");
};

卡码网:54.替换数字

image.png

思路:使用额外空间的话,创建新数组,遍历原数组(字符串转数组),遇到数字则向新数组push'number',否则push对应的字母,没有难度。

不使用额外空间的话,遍历原数组,获得数字的个数count,改变原数组的大小为size + 5*count,然后通过双指针分别指向原数组的末尾和扩充大小之后的末尾位置

例: 数组 a1b2c3 , arr.length = 6+5*3 = 21 , 设两个指针为p1, p2 ,p1指向 3的位置,也就是index 为5,p2指向index为20,p1,p2向数组头方向移动, 当p1的位置为数字时,p2向前赋值'number', p1位置为字母时,p2位置赋值对应字母

其实很多数组填充类的问题,其做法都是先预先给数组扩容带填充后的大小,然后在从后向前进行操作。 这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。

 151.翻转字符串里的单词

image.png

第一反应:split然后reverse

/**
 * @param {string} s
 * @return {string}
 */
var reverseWords = function(s) {
    // 后面的filter是为了去除单纯的空格产生的空数组项
    let sArr = s.split(' ').filter(item=>item)
    return sArr.reverse().join(' ')
};

不使用额外空间:

  1. 去掉重复的空格(双指针法, 头尾空格单独判断)
  2. 翻转整个字符串
  3. 翻转字符串中的单词(遇到空格了前面的就是单词,或者最后走到了字符串结尾)
/**
 * @param {string} s
 * @return {string}
 */
 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--;
  }
}

卡码网:55.右旋转字符串

image.png

思路:不使用额外空间的话,整体思路类似于替换上一题

  1. 整体翻转
  2. 单独翻转

abcdef ---> fedcba fedcba ---- (fe->ef,dcba->abcd)-->efabcd

单独总结:

替换数字和翻转字符串里的单词两道题,都用到了双指针法,这里双指针法用来在不创建额外空间的情况下,对原数组进行删除元素或替换元素的操作。