前端算法 ==== 栈的好戏还要继续!| 1047. 删除字符串中的所有相邻重复项

目录

解题

思路

题外话


给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

复制代码
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

解题

充分理解题意后,我们可以发现,当字符串中同时有多组相邻重复项时,我们无论是先删除哪一个,都不会影响最终的结果。因此我们可以从左向右顺次处理该字符串。

而消除一对相邻重复项可能会导致新的相邻重复项出现,如从字符串 abba 中删除 bb 会导致出现新的相邻重复项 aa 出现。因此我们需要保存当前还未被删除的字符。一种显而易见的数据结构呼之欲出:栈。我们只需要遍历该字符串,如果当前字符和栈顶字符相同,我们就贪心地将其消去,否则就将其入栈即可。

思路

本题要删除相邻相同元素,相对于20. 有效的括号来说其实也是匹配问题,20. 有效的括号 是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。

本题也是用栈来解决的经典题目。

那么栈里应该放的是什么元素呢?

我们在删除相邻重复项的时候,其实就是要知道当前遍历的这个元素,我们在前一位是不是遍历过一样数值的元素,那么如何记录前面遍历过的元素呢?

所以就是用栈来存放,那么栈的目的,就是存放遍历过的元素,当遍历当前的这个元素的时候,去栈里看一下我们是不是遍历过相同数值的相邻元素。

然后再去做对应的消除操作。 如动画所示:

从栈中弹出剩余元素,此时是字符串ac,

复制代码
/**
 * 移除字符串中相邻字符对的函数
 * 该函数通过模拟栈操作,依次遍历字符串中的字符,每当遇到不匹配的相邻字符时,将其重新压入栈中
 * 最终,栈中剩余的字符即为去除所有相邻字符对后的结果
 *
 * @param {string} s 输入的字符串
 * @return {string} 移除相邻字符对后的字符串
 */
var removeDuplicates = function (s) {
  // 使用数组模拟栈数据结构
  const stack = [];
  // 遍历输入字符串的每个字符
  for (const x of s) {
    // 弹出栈顶元素,用于比较是否与当前字符x相同
    let prev = stack.pop();
    // 如果弹出的元素与当前字符x不同,则将弹出的元素重新压入栈,并将当前字符x也压入栈
    if (prev !== x) {
      stack.push(prev);
      stack.push(x);
    }
    // 如果相同,则不做任何操作,即实现了移除一对相邻字符的目的
  }
  // 最终栈中剩余的字符即为结果,将其转换为字符串返回
  return stack.join("");
};

// 调用示例
removeDuplicates("abbaca");

题外话

这道题目就像是我们玩过的游戏对对碰,如果相同的元素放在挨在一起就要消除。

可能我们在玩游戏的时候感觉理所当然应该消除,但程序又怎么知道该如果消除呢,特别是消除之后又有新的元素可能挨在一起。

此时游戏的后端逻辑就可以用一个栈来实现(我没有实际考察对对碰或者爱消除游戏的代码实现,仅从原理上进行推断)。

游戏开发可能使用栈结构,编程语言的一些功能实现也会使用栈结构,实现函数递归调用就需要栈,但不是每种编程语言都支持递归,例如:

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

相信大家应该遇到过一种错误就是栈溢出,系统输出的异常是Segmentation fault(当然不是所有的Segmentation fault 都是栈溢出导致的) ,如果你使用了递归,就要想一想是不是无限递归了,那么系统调用栈就会溢出。

而且在企业项目开发中,尽量不要使用递归!在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)

相关推荐
还是大剑师兰特1 分钟前
Vite + Vue 3 一体化开发调试插件:vite-plugin-vue-devtools
前端·javascript·vue.js
叹一曲当时只道是寻常1 分钟前
Tauri v2 + Rust 实现 MCP Inspector 桌面应用:进程管理、Token 捕获与跨平台踩坑全记录
开发语言·后端·rust
晓得迷路了2 分钟前
栗子前端技术周刊第 123 期 - axios 包遭入侵、Babylon.js 9.0、Node.js 25.9.0...
前端·javascript·axios
独特的螺狮粉5 分钟前
开源鸿蒙跨平台Flutter开发:应对重症监护警报疲劳:BLoC 架构下的 FSM (有限状态机) 建模与全局消息干预机制
开发语言·flutter·华为·开源·harmonyos
Cobyte8 分钟前
如何使用飞书机器人连接本地 AI Agent
前端·aigc·ai编程
lifallen9 分钟前
Flink Agent:ActionTask 与可续跑状态机 (Coroutine/Continuation)
java·大数据·人工智能·语言模型·flink
5720 天窗9 分钟前
classfinal加密Spring boot3
java·spring boot·后端·classfinal·class final
starsky762389 分钟前
深入理解 Web 容器:从反射扫描到服务器启动的完整实现
java·前端·tomcat
路飞雪吖~11 分钟前
【测试】接口测试---1个框架,5个模块
开发语言·python·测试工具
sycmancia11 分钟前
QT——计算器核心算法
开发语言·qt·算法