小白学java栈的经典算法问题——第四关白银挑战

内容1.括号匹配问题
2.最小栈
3.最大栈

1.括号匹配问题

栈的典型题目还是非常明显的,括号匹配、表达式计算等等几乎都少不了栈,本小节我们就看两个最经典的问题

首先是LeetCode20,链接

 本道题还是比较简单的,其中比较麻烦的是如何判断两个符号是不是一组的

我们可以用哈希表将所有符号先存储,左半边做key,右半边做 value。遍历字符串的时候,遇到左半边符号就入栈,遇到右半边符号就与栈顶的符号比较,不匹配就返回false

class Solution {// 定义一个映射表,用于匹配括号private static final Map<Character, Character> map = new HashMap<Character, Character>() {{put('{', '}');put('[', ']');put('(', ')');put('?', '?');}};public boolean isValid(String s) {// 若字符串长度大于0且首字符不在映射表中,则直接返回falseif (s.length() > 0 && !map.containsKey(s.charAt(0))) {return false;}// 使用链表作为栈的实现,初始化时添加一个占位符'?'LinkedList<Character> stack = new LinkedList<Character>() {{add('?');}};// 遍历字符串中的每个字符for (Character c : s.toCharArray()) {if (map.containsKey(c)) {  // 若字符为左括号,则将其入栈stack.addLast(c);} else if (map.get(stack.removeLast()) != c) {  // 若字符为右括号,则与栈顶字符进行匹配return false;}}// 最终,若栈中只剩下一个占位符'?',则所有括号都匹配成功return stack.size() == 1;}
}

这种问题使用java还是比较方便,但是对于C语言就非常不友好,需要自己构造,然后再实现算法。

LeetCode给我们制造了十几个括号匹配问题,但是都是条件变来变去,解决起来有难有易。

例如,LeetCode22链接

代码如下:

import java.util.ArrayList;
import java.util.List;public class Solution {// 做减法public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();// 特判if (n == 0) {return res;}// 执行深度优先遍历,搜索可能的结果dfs("", n, n, res);return res;}/*** 执行深度优先遍历,搜索可能的括号组合* @param curStr 当前递归得到的结果* @param left   剩余可用的左括号个数* @param right  剩余可用的右括号个数* @param res    结果集*/private void dfs(String curStr, int left, int right, List<String> res) {// 因为每一次尝试,都使用新的字符串变量,所以无需回溯// 在递归终止的时候,直接把它添加到结果集即可,注意与「力扣」第 46 题、第 39 题区分if (left == 0 && right == 0) {res.add(curStr);return;}// 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)if (left > right) {return;}if (left > 0) {dfs(curStr + "(", left - 1, right, res); // 尝试放一个左括号}if (right > 0) {dfs(curStr + ")", left, right - 1, res); // 尝试放一个右括号}}
}

 LeetCode32最长有效括号

class Solution {public int longestValidParentheses(String s) {int maxans = 0; // 最长有效括号的长度int[] dp = new int[s.length()]; // 用于存储以当前字符结尾的最长有效括号的长度for (int i = 1; i < s.length(); i++) {if (s.charAt(i) == ')') { // 当前字符为右括号时if (s.charAt(i - 1) == '(') { // 前一个字符为左括号时// 更新以当前字符结尾的最长有效括号的长度dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {// 当前字符前面有一段有效括号,且该段有效括号前面是左括号时// 更新以当前字符结尾的最长有效括号的长度dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;}maxans = Math.max(maxans, dp[i]); // 更新最长有效括号的长度}}return maxans; // 返回最长有效括号的长度}
}

 LeetCode301链接

 

class Solution {private List<String> res = new ArrayList<String>(); // 用于存储有效的括号组合public List<String> removeInvalidParentheses(String s) {int lremove = 0; // 需要移除的左括号数量int rremove = 0; // 需要移除的右括号数量for (int i = 0; i < s.length(); i++) {if (s.charAt(i) == '(') {lremove++; // 统计左括号数量} else if (s.charAt(i) == ')') {if (lremove == 0) {rremove++; // 统计右括号数量,如果没有对应的左括号,则需要移除该右括号} else {lremove--; // 匹配到一个右括号,可以抵消一个左括号}}}helper(s, 0, lremove, rremove); // 辅助函数进行递归处理return res; // 返回有效的括号组合列表}private void helper(String str, int start, int lremove, int rremove) {if (lremove == 0 && rremove == 0) { // 当需要移除的括号数量为0时,判断当前字符串是否有效if (isValid(str)) {res.add(str); // 如果有效,则将其添加到结果列表中}return; // 结束递归}for (int i = start; i < str.length(); i++) {if (i != start && str.charAt(i) == str.charAt(i - 1)) {continue; // 避免重复计算,如果当前字符和前一个字符相同,则跳过}// 如果剩余的字符无法满足去掉的数量要求,直接返回if (lremove + rremove > str.length() - i) {return;}// 尝试去掉一个左括号if (lremove > 0 && str.charAt(i) == '(') {helper(str.substring(0, i) + str.substring(i + 1), i, lremove - 1, rremove);}// 尝试去掉一个右括号if (rremove > 0 && str.charAt(i) == ')') {helper(str.substring(0, i) + str.substring(i + 1), i, lremove, rremove - 1);}}}private boolean isValid(String str) {int cnt = 0; // 记录左括号和右括号的差值for (int i = 0; i < str.length(); i++) {if (str.charAt(i) == '(') {cnt++; // 遇到左括号,增加差值} else if (str.charAt(i) == ')') {cnt--; // 遇到右括号,减少差值if (cnt < 0) {return false; // 如果差值小于0,说明右括号多余左括号,无效}}}return cnt == 0; // 差值为0,说明左右括号一一对应,有效}
}

2.最小栈

LeetCoede155链接

本道题的关键在于理解getMin()到底表示什么,可以看一个例子上的示例画成示意图如下: 

 

这里的关键是理解对应的Min栈内,中间元素为什么是-2,理解了本道题就非常简单。

题目要求在常数事件内获得栈的最小值,因此不能在getMin()的时候再去计算最小值,最好在push或者pop的时候就已经计算好了当前栈中的最小值。

对于栈来说,如果一个元素a在入栈时,栈里有其他元素的b,c,d,那么无论这个栈在之后经历了什么操作,只要a在栈中,b,c,d就一定在栈中,因为在a被弹出之前,b,c,d就不会被弹出。

那么,我们可以在每个元素a入栈的时候把当前栈的最小值m存储起来,在这之后无论何时,如果栈顶元素是a,我们就可以直接返回存储的最小值m。

按照上面的思路,我们只需要设计一个数据结构,使得每一个元素a与其相应的最小值m时刻保持一一对应,因此我们使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。

  • 当一个元素要入栈的时候,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中。
  • 当一个元素要出栈的时候,我们把辅助栈的栈顶元素也一并弹出。
  • 在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。
/*** 实现一个最小栈 MinStack,支持 push、pop、top 和 getMin 操作*/
class MinStack {private Stack<Integer> stack;  // 存储栈元素的主栈private Stack<Integer> min_stack;  // 存储当前栈中最小值的辅助栈public MinStack() {stack = new Stack<>();min_stack = new Stack<>();}/*** 向栈中压入元素x* @param x 待压入的元素*/public void push(int x) {stack.push(x);  // 将元素压入主栈中if (min_stack.isEmpty() || x <= min_stack.peek()) {min_stack.push(x);  // 如果是最小值,则将其压入辅助栈中}}/*** 弹出栈顶元素*/public void pop() {if (stack.pop().equals(min_stack.peek())) {min_stack.pop();  // 如果是最小值,则从辅助栈中弹出}}/*** 获取栈顶元素* @return 栈顶元素*/public int top() {return stack.peek();  // 直接返回主栈的栈顶元素}/*** 获取栈中最小值* @return 栈中最小值*/public int getMin() {return min_stack.peek();  // 直接返回辅助栈的栈顶元素,即为栈中最小值}
}

 3.最大栈

LeetCode716.设计一个最大栈数据结构,既支持栈操作,又支持查找栈中的最大元素。

实现MaxStack类:

MaxStack()初始化栈对象
void push(int x)将元素x压入栈中。
int pop()移除栈顶元素并返回这个元素
int top()返回栈顶元素,无需移除
int peeMax()检索并返回栈中最大元素,无需移除。
int popMax()检查并返回栈中最大元素,并将其移除。
如果有多个元素,只要移除 最靠近栈顶的那个。

示例:

输入:
["MaxStack","push","push","top","popMax","top","premix","pop","top"]
[[],[5],[1],[5],[],[],[],[],[],[]]
输出:
[null,null,null,null,5,5,1,5,1,5]
解释:
MaxStack stk=new MaxStack();
stk.push(5);   //[5] - 5既是栈顶元素,也是最大元素
stk.push(1);   //[5,1】 - 栈顶元素是1,最大元素是5
stk.push(5);   //[5,1,5] - 5既是栈顶元素,也是最大元素
stk.top();     //返回 5,[5,1,5] - 栈没有改变
stk.popMax();  //返回 5,[5,1] - 栈发生改变,栈顶元素不再是最大元素
stk.top();    //返回 1,[5,1] - 栈没有改变
stk.popMax(); //返回 5,[5,1] - 栈没有改变
stk.pop();     //返回 1,[5] - 次操作后,5既是栈顶元素,也是最大元素
stk.top();    //返回5,[5] - 栈没有改变

 本道题与上一题相反,但是处理方法是一致的,一个普通的栈可以支持前三种操作push(x)、pop()和top(),所以我们需要考虑的仅仅为后两种操作的peekMax()和popMax()。

对于peekMax(),我们可以另一个栈来存储每个位置到栈底的所有元素的最大值,例如,如果当前的第一个栈中元素为[2,1,5,3,9],那么第二个栈中的额元素为[2,2,5,5,9]。在push(x)操作时,只需要将第二个栈的栈顶和x的最大值入栈,而在pop()操作时,只需要将第二个栈进行出栈。

对于popMax(),由于我们知道当前栈中最大的元素值,因此可以直接将两个栈同时出栈,并存储第一个栈出栈的所有值。当某个时刻,第一个栈的出栈元素就等于当前栈中最大的元素值时,就找到了最大的元素。这个时候我们将之前第一个栈的所有元素重新入栈,并且同步更新第二个栈,就完成了popMax()操作。

package com.yugutou.charpter4_stack.level2;import java.util.Stack;/*** 实现一个支持 push、pop、top、peekMax 和 popMax 操作的最大栈 MaxStack*/
class MaxStack {Stack<Integer> stack;  // 存储栈元素的主栈Stack<Integer> maxStack;  // 存储当前栈中最大值的辅助栈public MaxStack() {stack = new Stack();  // 初始化主栈maxStack = new Stack();  // 初始化最大值辅助栈}/*** 向栈中压入元素x* @param x 待压入的元素*/public void push(int x) {int max = maxStack.isEmpty() ? x : maxStack.peek();  // 获取当前最大值maxStack.push(max > x ? max : x);  // 将当前最大值或新元素压入最大值辅助栈stack.push(x);  // 将元素压入主栈}/*** 弹出栈顶元素* @return 弹出的栈顶元素*/public int pop() {maxStack.pop();  // 弹出最大值辅助栈的栈顶元素return stack.pop();  // 弹出主栈的栈顶元素}/*** 获取栈顶元素* @return 栈顶元素*/public int top() {return stack.peek();  // 直接返回主栈的栈顶元素}/*** 获取栈中最大值* @return 栈中最大值*/public int peekMax() {return maxStack.peek();  // 直接返回最大值辅助栈的栈顶元素,即为栈中最大值}/*** 弹出栈中的最大值* @return 被弹出的最大值*/public int popMax() {int max = peekMax();  // 获取当前栈中的最大值Stack<Integer> buffer = new Stack();  // 辅助栈用于暂存元素while (top() != max) {buffer.push(pop());  // 将非最大值的元素暂存到辅助栈中}pop();  // 弹出栈中的最大值while (!buffer.isEmpty()) {push(buffer.pop());  // 将暂存的元素重新压入栈中}return max;  // 返回被弹出的最大值}
}

学习算法的时候真的是很费脑,但是看懂之后又觉得自己很厉害,自己走了很多步,再回头看,原来也不过如此,敢于尝试真的是勇敢的象征。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/208341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣面试题 08.12. 八皇后(java回溯解法)

Problem: 面试题 08.12. 八皇后 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 八皇后问题的性质可以利用回溯来解决&#xff0c;将大问题具体分解成如下待解决问题&#xff1a; 1.以棋盘的每一行为回溯的决策阶段&#xff0c;判断当前棋盘位置能否放置棋子 2.如何判…

hbuilder + uniapp +vue3 开发微信云小程序

1、创建项目&#xff1a; 2、创建项目完成的默认目录结构&#xff1a; 3、在根目录新建一个文件夹cloudFns&#xff08;文件名字随便&#xff09;&#xff0c;存放云函数源码&#xff1a; 4、修改manifest.json文件&#xff1a;添加 小程序 appid和cloudfunctionRoot&#xff0…

python的websocket方法教程

WebSocket是一种网络通信协议&#xff0c;它在单个TCP连接上提供全双工的通信信道。在本篇文章中&#xff0c;我们将探讨如何在Python中使用WebSocket实现实时通信。 websockets是Python中最常用的网络库之一&#xff0c;也是websocket协议的Python实现。它不仅作为基础组件在…

pyside/qt03——人机协同的编程教学—直接面向chatGPT实战开发(做中学,事上练)

先大概有个草图框架&#xff0c;一点点丰富 我纠结好久&#xff0c;直接用Python写UI代码 还是用designer做UI 再转Python呢&#xff0c; 因为不管怎么样都要转成Python代码&#xff0c; 想了想还是学一下designer吧&#xff0c;有个中介&#xff0c;有直观理解。 直接这样也可…

智能优化算法应用:基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.食肉植物算法4.实验参数设定5.算法结果6.参考…

设计并实现一个多线程图书馆管理系统,涉及数据库操作

没有实现全部功能&#xff0c;希望路过的大佬&#xff0c;可以实现全部功能&#xff0c;在评论区聊聊 创建数据库library-demo CREATE DATABASE library-demo创建图书表book CREATE TABLE book (bookId int(11) NOT NULL AUTO_INCREMENT COMMENT 图书ID,bookName varchar(15)…

HarmonyOS创建JavaScript(类 Web开发模式)项目

上文 HarmonyOS带大家创建自己的第一个Page页面并实现路由跳转(ArkTS)带大家创建了我们项目中第一个自己创建的page 并完成了一个跳转逻辑的编写 上文的开发模式是 ArkTS 的 也被称为 声明式开发范式 还有一种 javaScript的 类Web开发模式 这种方式就类似于我们传统的前端开发模…

基于微群机器人的二次开发

请求URL&#xff1a; http://域名地址/modifyGroupName 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId是String登录实例标识chatRoom…

读书笔记-《数据结构与算法》-摘要2[冒泡排序]

冒泡排序 核心&#xff1a;冒泡&#xff0c;持续比较相邻元素&#xff0c;大的挪到后面&#xff0c;因此大的会逐步往后挪&#xff0c;故称之为冒泡。 public class BubbleSort {public static void main(String[] args) {int unsortedArray[] new int[]{6, 5, 3, 1, 8, 7, 2…

Leetcode每日一题学习训练——Python3版(到达首都的最少油耗)

版本说明 当前版本号[20231205]。 版本修改说明20231205初版 目录 文章目录 版本说明目录到达首都的最少油耗理解题目代码思路参考代码 原题可以点击此 2477. 到达首都的最少油耗 前去练习。 到达首都的最少油耗 ​ 给你一棵 n 个节点的树&#xff08;一个无向、连通、无环…

倒计时模块复习

经典回顾倒计时 倒计时的基本布局介绍。 一个内容区域和一个输入区域&#xff0c;内容区域进行划分 直接使用flex布局会更快一点。 js代码 我们利用一下模块化思想&#xff0c;直接把获得时间这个功能写成一个函数。方便后续的调用 function getTime() {const date new Date…

MES管理系统通过哪些方面提升产品质量管理水平

在当今高度竞争的市场环境中&#xff0c;质量成为了企业生存和发展的关键因素。工厂作为生产产品的核心场所&#xff0c;其质量管理水平直接影响到产品的质量和企业的声誉。为了应对这一挑战&#xff0c;许多工厂引入了MES管理系统解决方案。本文将探讨MES管理系统如何帮助工厂…

【UE5】监控摄像头效果(上)

目录 效果 步骤 一、视角切换 二、摄像头画面后期处理 三、在场景中显示摄像头画面 效果 步骤 一、视角切换 1. 新建一个Basic关卡&#xff0c;添加第三人称游戏资源到项目浏览器 2. 新建一个Actor蓝图&#xff0c;这里命名为“BP_SecurityCamera” 打开“BP_Securit…

模电笔记。。。。

模电 2.8 蜂鸣器 按照蜂鸣器驱动方式分为有源蜂鸣器和无源蜂鸣器 有源的有自己的震荡电路&#xff0c;无源的要写代码控制。 里面有个线圈&#xff0c;相当于电感&#xff0c;储能&#xff0c;通直隔交。 蜂鸣器的参数&#xff1a;额定电压&#xff0c;工作电压&#xff0…

【CCF-B】1/2区,录用见刊极快!2个月录用!

计算机类 • 好刊解读 今天小编带来Taylor and Francis旗下计算机领域快刊&#xff0c;CCF-B类推荐的期刊解读&#xff0c;期刊审稿周期短&#xff0c;投稿友好&#xff0c;如您有投稿需求&#xff0c;可作为重点关注&#xff01;后文有相关领域真实发表案例&#xff0c;供您投…

防水,也不怕水。Mate X5是如何做到让你湿手湿屏也不影响操作的?

相信不少人都碰到过当手机屏幕存在小水珠时&#xff0c;触控变得不灵敏&#xff0c;或者出现“幽灵触屏”&#xff0c;指东打西的情况。 尤其是在洗澡、做饭&#xff0c;或者在户外遇到下雨天气时&#xff0c;如果打湿的手机收到重要聊天消息或者电话&#xff0c;却因为湿屏导…

生成fip.bin在Milkv-duo上跑rtthread的相关尝试,及其问题分析

前言 &#xff08;1&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;2&#xff09;本来是想在Milkv-duo上跑rtthread的&#xff0c;做了很多努力&#xff0c;一直没有结果。虽然不知道最终能不能成功做出来&#xff0c;还是把自己的相关努力分享出来…

MDK官网如何下载stm32支持包

网站&#xff1a;https://www.keil.com/demo/eval/arm.htm 1 2 3点这个下载

基于Mint Mate 21.2 Victoria 的Anjuta安装与测试

序言 Linux mint mate 21.2 命名为 victoria 版&#xff0c;在vmware虚拟机中安装按提示默认安装即可&#xff0c;不做更多记录。mint mate的优点是稳定&#xff0c;窗口质感好。安装完成后&#xff0c;需要关注一些常用功能配置。主要有&#xff1a;显示器调整、桌面调整、工…

当然热门的原创改写改写大全【2023最新】

在信息时代&#xff0c;随着科技的不断发展&#xff0c;改写软件逐渐成为提高文案质量和写作效率的重要工具。本文将专心分享一些好用的改写软件&#xff0c;其中包括百度文心一言智能写作以及147SEO改写软件。这些工具不仅支持批量改写&#xff0c;而且在发布到各大平台后能够…