五分钟带你领略: 腾讯半年来出现最频繁的算法之一——字符串解码

简要介绍

你们好,我是神三元。今天给你们分享一道有意思的算法题,在leetcode平台上截图以下: 前端

近半年来广受各大公司的青睐,出现很是频繁,在腾讯仅仅半年就出现了17次,若是说给满分给5颗星的话,那么这一题算得上实打实的五星题。算法

那到底是什么让这个算法从众多的leetcode题库中脱颖而出,在各个大厂中一而再、再而三地出现呢?编程

接下来,就让咱们解开它的奥秘,领略这个算法到底多么经典!数据结构

开局思路

刚开始拿到这道题,看到括号匹配问题,直觉上就想到了利用栈先进后出的性质,来完成先后字符串的拼接。可是后来尝试了好久,发现问题并无那么简单,主要概括以下:编程语言

  1. 在中括号层次比较深的状况下,如何完成回溯,将当前的字符与以前的字符拼接。
  2. 当出现次数并非个位数,而是相似100,1000 这样的多位数,如何来解析。

接着,咱们利用不一样的方法,来一步步来解决这两个棘手的问题。spa

利用栈

说一下总体思路。扫描一遍字符串,针对不一样的字符进行不一样的处理:code

首先有两个重要的变量,表示重复次数的 multi 值和累积字符串 res。cdn

    1. 遇到数字, 直接参加计算,累积multi值。
    1. 遇到普通字符(除[,]外),累积到 res 后面。
    1. 遇到 [, 将以前累积的字符串res压栈,当前multi值压到另外一个栈。而后当前 multi归零,res置空。
    1. 遇到 ], 取出栈中multi值,将当前的 res 字符串重复 multi 次,赋值给临时变量tmp,而后让另外一个存放累积字符串的栈中弹出栈顶元素和当前的tmp拼接,做为最新的累积字符串赋值给res。

若是如今没看懂,没有关系,给出代码就明白了,让你们直观感觉一下:blog

var decodeString = function (s) {
  // 存放 【重复次数】 的栈
  let countStack = [];
  // 存放 【累积字符串】 的栈
  let resStack = [];
  // 用来累积的字符串 res
  let res = "";
  // 表示重复次数
  let multi = 0;
  for (let i = 0; i < s.length; i++) {
    let cur = s.charAt(i);
    if (cur == '[') {
      // 双双压栈,保存了当前的状态
      countStack.push(multi);
      resStack.push(res);
      // 纷纷置空,准备下面的累积
      multi = 0;
      res = "";
    } else if (cur == ']') {
      // 遇到 ],表示累积结束,要算帐了。
      // 【当前的串出现多少次】还保存在栈中,把它取出来
      let count = countStack.pop();
      let temp = "";
      // 让 [ 和 ] 之间的字符串(就是累积字符串res)重复 count 次
      for(let i = 0; i < count; i++) {
        temp += res;
      }
      // 和前面已经求得的字符串进行拼接
      res = resStack.pop() + temp;
    } else if (cur >= '0' && cur <= '9') {
      // multi累积
      multi = multi * 10 + (cur - '0');
    } else {
      // 字符累积
      res += cur;    
    }
  }
  return res;
};
复制代码

利用递归程序

递归的思路就容易一点,一旦遇到[,立马进入新的递归程序,扫描到对应的]为止。也就是说,凡是遇到括号,括号里面的事情,所有交给子程序完成。建议你们看完代码再来体会这句话:递归

var decodeString = function (s) {
  // 从第 0 个元素开始处理
  return dfs(s, 0);
};

let dfs = (s, n) => {
  let res = "";
  // 保存起始索引
  let i = n;
  // 同上,表示重复的次数
  let multi = 0;
  while(i < s.length) {
    let cur = s.charAt(i);
    // 遇到数字,累积 multi 值
    if(cur >= '0' && cur <= '9') 
      multi = multi * 10 + (cur - '0');
    else if(cur === '[') {
      // 划重点!给子程序,把对应的 ] 索引和括号包裹的字符串返回
      // 即tmp 的格式为 [索引,字符串]
      let tmp = dfs(s, i + 1);
      // 这样下次遍历就是从对应的 ] 后面遍历了,由于当前已经把括号里面的处理完了
      i = tmp[0];
      // 须要重复的字符串已经返回来了
      let repeatStr = tmp[1];
      for(let j = 0; j < multi; j++) {
        res += repeatStr;
      }
      // 当前已经把括号里面的处理完,multi 置零,为下一轮遍历准备
      multi = 0;
    }else if(cur === ']') {
      // 遇到了对应的 ] ,返回 ] 索引和括号包裹的字符串
      return [i, res];
    } else {
      res += cur;
    }
    // 继续遍历
    i++;
  }
  return res;
}
复制代码

两种方法都顺利经过。

估计作完这道题,仔细回味一下,也可以发现这道题的经典之处了:

  1. 考察对栈这种数据结构的理解
  2. 考察字符串的基本操做,涉及对编程语言的考察
  3. 考察对递归和回溯思想的理解。(其实字符串拼接就是向前回溯的过程)

作完这道题,是否是刷新了本身对于栈这种数据结构的认知呢?

它其实具备着自然的递归性质,只是咱们初学的时候,容易先入为主地把这种先入后出的数据结构想的太简单。固然它还有其余神奇的功能,咱们放在下一期来分享。

❤️ 看完两件事

若是你以为这篇内容对你挺有启发,我想邀请你帮我两个小忙:

  1. 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)

  2. 关注公众号「前端三元同窗」,每日坚持灵魂之问,碰见更好的本身!

相关文章
相关标签/搜索