力扣经典题---压缩字符串(JS实现)

题目描述:

给你一个字符数组 chars ,请使用下述算法压缩:

从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 :

  • 如果这一组长度为 1 ,则将字符追加到 s 中。
  • 否则,需要向 s 追加字符,后跟这一组的长度。

压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 10 或 10 以上,则在 chars 数组中会被拆分为多个字符。

请在 修改完输入数组后 ,返回该数组的新长度。

你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

示例 1:

输入:chars = ["a","a","b","b","c","c","c"]
输出:返回 6 ,输入数组的前 6 个字符应该是:["a","2","b","2","c","3"]
解释:"aa" 被 "a2" 替代。"bb" 被 "b2" 替代。"ccc" 被 "c3" 替代。

示例 2:

输入:chars = ["a"]
输出:返回 1 ,输入数组的前 1 个字符应该是:["a"]
解释:唯一的组是“a”,它保持未压缩,因为它是一个字符。

示例 3:

输入:chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:返回 4 ,输入数组的前 4 个字符应该是:["a","b","1","2"]。
解释:由于字符 "a" 不重复,所以不会被压缩。"bbbbbbbbbbbb" 被 “b12” 替代。

提示:

  • 1 <= chars.length <= 2000
  • chars[i] 可以是小写英文字母、大写英文字母、数字或符号

思路:

  1. 初始化索引:定义两个索引,readIndex用于遍历原始字符数组charswriteIndex用于记录压缩后数据在数组中的位置。

  2. 遍历数组:使用while循环,通过readIndex从数组的开始位置遍历到结束。

  3. 记录当前字符和计数:在循环内部,使用currentChar记录当前遍历到的字符,count记录该字符连续出现的次数。

  4. 计算连续字符数:使用内部的while循环来确定currentChar连续出现的次数,同时更新readIndex以跳过这些连续的字符。

  5. 压缩逻辑

    • 如果count大于1,表示有多个连续的相同字符,需要压缩。此时,将currentChar写入chars数组的writeIndex位置,然后递增writeIndex
    • count转换为字符串countStr,然后遍历countStr的每个字符,将它们依次写入chars数组,每次写入后递增writeIndex
    • 如果count等于1,表示没有压缩的必要,直接将currentChar写入chars数组的writeIndex位置,然后递增writeIndex
  6. 移动读取指针:在每次内部循环结束后,将readIndex向前移动一位,以便在下一次迭代中读取下一个字符。

  7. 返回新长度:当readIndex遍历完整个数组后,循环结束。此时,writeIndex表示压缩后的数据在数组中的长度。返回writeIndex作为函数的结果。

代码实现:

/**
 * @param {character[]} chars
 * @return {number}
 */
var compress = function (chars) {
    let readIndex = 0; // 用于遍历原始数组
    let writeIndex = 0; // 用于记录压缩后字符串在数组中的位置

    while (readIndex < chars.length) {
        let count = 1; // 记录当前字符的连续出现次数
        let currentChar = chars[readIndex];

        // 计算当前字符连续出现的次数
        //当前指针+1小于数组长即没有越界时&&当前元素和下一个元素相同
        //也可以用当读指针 read 位于字符串的末尾,或读指针 read 指向的字符不同于下一个字符时来结束循环
        //readIndex === length - 1 || chars[readIndex] !== chars[readIndex + 1]
        while (readIndex + 1 < chars.length && chars[readIndex + 1] === currentChar) {
            readIndex++;
            count++;
        }

        // 如果当前字符连续出现次数大于1,则需要记录字符和次数
        if (count > 1) {
            // 将字符写入数组
            chars[writeIndex] = currentChar;
            writeIndex++;

            // 将次数转换为字符串,并根据需要拆分写入数组
            let countStr = count.toString();
            for (let i = 0; i < countStr.length; i++) {
                chars[writeIndex] = countStr[i];
                writeIndex++;
            }
        } else {
            // 如果连续出现次数为1,直接将字符写入数组
            chars[writeIndex] = currentChar;
            writeIndex++;
        }

        // 移动读取指针
        readIndex++;
    }

    // 返回压缩后数组的新长度
    return writeIndex;
};

 运行结果分析

运行后发现内存的消耗比较高

上述代码的效率主要取决于字符数组的遍历次数和写入次数。由于题目要求使用常量额外空间,我们不能使用额外的数据结构来存储中间结果。因此优化主要集中在减少不必要的操作和优化写入逻辑上。

性能优化:

在查询资料后,发现要想优化性能可以从以下几个方面进行

  1. 减少字符到字符串的转换次数:在处理重复字符组的计数时,如果计数大于9,则需要将其转换为字符串并逐个字符写入数组。我们可以预先计算出需要写入的字符数,然后一次性写入,而不是在循环中重复转换和写入。

  2. 优化写入逻辑:在写入字符和计数时,我们可以预先分配好空间,然后一次性写入,减少数组的多次修改。

  3. 减少条件判断:尽量减少在循环中的条件判断,尤其是在内层循环中。

在查询资料后,发现要想优化性能可以从以下几个方面进行

优化实现思路:

  1. 初始化指针writeIndex初始化为0,用于记录压缩后字符串在数组中的位置。

  2. 遍历字符数组:使用一个for循环遍历chars数组,其中i作为当前遍历到的位置的索引。

  3. 记录当前字符:在循环内部,currentChar变量用来存储当前遍历到的字符。

  4. 计算连续字符数:使用一个while循环来计算当前字符连续出现的次数。如果下一个字符与当前字符相同,count加1,并且i向前移动一位。

  5. 写入当前字符:无论连续字符数是1还是更多,当前字符都会被写入到chars数组的writeIndex位置,然后writeIndex递增。

  6. 写入连续字符数:如果count大于1,表示有连续的字符需要压缩。此时,将count转换为字符串countStr,然后使用另一个for循环将countStr中的每个字符写入到chars数组中,每次写入后writeIndex递增。

  7. 更新索引:在while循环中,由于已经确定了连续字符的数量,所以i索引会跳过这些连续的字符,直接移动到下一个不同的字符处。

  8. 返回新长度:遍历完成后,writeIndex表示压缩后的字符串在数组中所占的最终位置,即新的长度。函数返回这个值。

 优化代码实现:

/**
 * @param {character[]} chars
 * @return {number}
 */
var compress = function (chars) {
    let writeIndex = 0; // 用于记录压缩后字符串在数组中的位置


    for (let i = 0; i < chars.length; i++) {
        let currentChar = chars[i];
        let count = 1; // 记录当前字符的连续出现次数

        // 计算当前字符连续出现的次数
        //当前指针+1小于数组长即没有越界时&&当前元素和下一个元素相同
        //也可以用当读指针 read 位于字符串的末尾,或读指针 read 指向的字符不同于下一个字符时来结束循环
        //readIndex === length - 1 || chars[readIndex] !== chars[readIndex + 1]
        while (i + 1 < chars.length && chars[i + 1] === currentChar) {
            count++;

        //当前已遍历元素的下标
            i++;

        }

        // 写入字符
        chars[writeIndex++] = currentChar;
       // console.log(chars[writeIndex++])

        // 如果连续出现次数大于1,需要写入次数
        if (count > 1) {
            // 将次数转换为字符串并逐个字符写入数组
            let countStr = count.toString();
            for (let j = 0; j < countStr.length; j++) {  
                chars[writeIndex++] = countStr[j];
            }
        }
    }

    // 返回压缩后数组的新长度
    return writeIndex;
};

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值