LeetCode算法(栈)

今天的练习是栈的相关题目,首先先解释一下什么是栈

栈的基本思路是通过数组来存储栈中的元素,并通过栈顶指针指示栈顶元素在数组中的位置。

栈具有以下特点:

存储结构:使用数组作为底层存储结构,数组的每个元素存储栈中的一个元素;

操作受限:栈只能从栈顶插入和删除元素,不支持在栈中间插入和删除元素;

先进后出:栈的元素遵循 "先进后出" 的原则,即后插入的元素先被删除;

顺序访问:只能从栈顶开始访问栈中的元素,不能从栈底或中间位置访问元素。

在开始栈相关的算法前,我们需要一个栈,那么先来手写一个栈结构

手写栈:

首先就是基础的属性:栈内所存的元素个数,栈的初始化,栈的初始化长度

java 复制代码
    //1.定义一个数组
    private int[] array;
    //2.定义栈的元素个数
    private int stackSize = 0;
    //3.定义栈的默认容量
    private final int CAPACITY_STACK = 5;

    public MyStack() {
        this.array = new int[CAPACITY_STACK];
    }

入栈:

入栈的逻辑:元素的个数就是每个元素在数组中对应的索引位,当有元素入栈时,本质上就是按照当前的顺序,放入到对应的索引位上

需要注意的就是栈的扩容,因为底层还是数组,所以需要考虑溢出的情况,当元素的个数与数组的长度相等时,证明栈已经满了,我们需要进行扩容,实际的操作就是,将数组的内容拷贝到一个新的更大的数组里

代码如下:

java 复制代码
    public int push(int e){
        //放入元素后,判断当前元素个数是否已经溢出,如果溢出则进行数组的扩容
        if (isFull()){
            array = Arrays.copyOf(array, array.length * 2);
        }
        array[stackSize] = e;
        stackSize++;
        return e;
    }  //将e入栈,并返回e

    public boolean isFull() { //判断数组是否满了的方法
        return stackSize == array.length;
    }

出栈:

出栈的逻辑也很简单,栈的特点是先进后出,所以从数组的尾部进行弹出即可,还是根据元素个数去进行索引的对应

出栈考虑的事情就是,如果数组是空的,可能会出现索引越位的问题,那么就需要进行判空处理

代码如下:

java 复制代码
    public int pop(){
        //将stackSize当前位置的元素变为null,然后stackSize--,返回当前位置元素
        if (empty()) {
            System.out.println("当前栈为空");
            return -1;
        }

        int result = array[stackSize - 1]; // 从栈顶位置取出元素
        array[stackSize - 1] = 0; // 清除栈顶元素(可选)
        stackSize--;
        return result;
    }  //将栈顶元素出栈并返回

    public boolean empty(){
        return stackSize == 0;
    }  //检测栈是否为空

到这里,差不多就好了,下面开始栈相关的算法题

1.有效的括号

链接:有效的括号

示例 :

输入:s = "()[]{}"

输出:true

输入:s = "(]"

输出:false

这个题目的目标是,需要对应的括号匹配,对于这种匹配问题,栈是很好用的

思路:

括号被分为左括号和右括号,那么当匹配到左括号时,我们将对应的右括号入栈,当匹配到右括号时,进行出栈,如果相等,则证明相匹配,正常的弹出,如果不匹配或者栈内没有对应匹配的括号,可以直接返回false

这时还会出现一种情况: (()

如果我们按照上面的方式进行入栈出栈,最后栈中还剩下一个右括号,大家可以想一想,那么这种情况也是错误的,所以我们还需要多加一个判断,就是栈必须为空

代码如下:

java 复制代码
import java.util.Arrays;

public class EffectiveParentheses {
    public class MyStack {
        /*
        通过数组实现栈
         */
        //1.定义一个数组
        private int[] array;
        //2.定义栈的元素个数
        private int stackSize = 0;
        //3.定义栈的默认容量
        private final int CAPACITY_STACK = 5;

        public MyStack() {
            this.array = new int[CAPACITY_STACK];
        }

        public int push(int e) {
            //放入元素后,判断当前元素个数是否已经溢出,如果溢出则进行数组的扩容
            if (isFull()) {
                array = Arrays.copyOf(array, array.length * 2);
            }
            array[stackSize] = e;
            stackSize++;
            return e;
        }  //将e入栈,并返回e

        public boolean isFull() { //判断数组是否满了的方法
            return stackSize == array.length;
        }

        public int pop() {
            //将stackSize当前位置的元素变为null,然后stackSize--,返回当前位置元素
            if (empty()) {
                System.out.println("当前栈为空");
                return -1;
            }

            int result = array[stackSize - 1]; // 从栈顶位置取出元素
            array[stackSize - 1] = 0; // 清除栈顶元素(可选)
            stackSize--;
            return result;
        }  //将栈顶元素出栈并返回

        public int peek() {
            if (empty()) {
                System.out.println("当前栈为空");
            }

            return array[stackSize - 1];
        }  //获取栈顶元素,栈顶元素不出栈

        public int size() {
            return stackSize;
        }  //获取栈中有效元素个数

        public boolean empty() {
            return stackSize == 0;
        }  //检测栈是否为空
    }

    /*
    有效的括号
     */
    public boolean isValid(String s) {
        /*
        思路:首先遍历整个字符串,如果遍历到{ ( [ ,则压入栈中,如果再遍历到} ) ]则弹出栈的头元素
        如果相等,则有效,继续遍历判断,否则直接返回false
         */
        char[] charArray = s.toCharArray();
        MyStack stack = new MyStack();

        for (char str : charArray){
            if (str == '('){
                stack.push(')');
            } else if (str == '{') {
                stack.push('}');
            } else if (str == '[') {
                stack.push(']');
            }
            if (str == ')' || str == '}' || str == ']') {
                int pop = stack.pop();
                if (pop != str || pop == -1){
                    return false;
                }
            }
        }
        return stack.empty();
    }
}

2.最小栈

示例 :

输入:

["MinStack","push","push","push","getMin","pop","top","getMin"]

[[],[-2],[0],[-3],[],[],[],[]]

输出:

[null,null,null,null,-3,null,0,-2]

解释:

MinStack minStack = new MinStack();

minStack.push(-2);

minStack.push(0);

minStack.push(-3);

minStack.getMin(); --> 返回 -3.

minStack.pop();

minStack.top(); --> 返回 0.

minStack.getMin(); --> 返回 -2.

这道题实际上就是为栈内的元素,在不改变原数组的情况下进行排序,本质上就是为数组进行排序 ,那么就比较简单了

思路:

(1)新建一个数组,将栈内的数组深拷贝到新数组中

(2)为数组进行排序

(3)返回数组的第一个值,为最小值

代码如下:

java 复制代码
import java.util.Arrays;

public class MinStack {

    /*
    通过数组实现栈
     */
    //1.定义一个数组
    private int[] array;
    //2.定义栈的元素个数
    private int stackSize = 0;
    //3.定义栈的默认容量
    private final int CAPACITY_STACK = 5;

    public MinStack() {
        this.array = new int[CAPACITY_STACK];
    }

    public int push(int e){
        //放入元素后,判断当前元素个数是否已经溢出,如果溢出则进行数组的扩容
        if (isFull()){
            array = Arrays.copyOf(array, array.length * 2);
        }
        array[stackSize] = e;
        stackSize++;
        return e;
    }  //将e入栈,并返回e

    public boolean isFull() { //判断数组是否满了的方法
        return stackSize == array.length;
    }

    public int pop(){
        //将stackSize当前位置的元素变为null,然后stackSize--,返回当前位置元素
        if (empty()) {
            System.out.println("当前栈为空");
        }

        int result = array[stackSize - 1]; // 从栈顶位置取出元素
        array[stackSize - 1] = 0; // 清除栈顶元素(可选)
        stackSize--;
        return result;
    }  //将栈顶元素出栈并返回

    public int top(){
        if (empty()) {
            System.out.println("当前栈为空");
        }

        return array[stackSize -1];
    }  //获取栈顶元素,栈顶元素不出栈

    public int size(){
        return stackSize;
    }  //获取栈中有效元素个数

    public boolean empty(){
        return stackSize == 0;
    }  //检测栈是否为空

    public int getMin() {
        if (empty()) {
            System.out.println("栈为空");
            return -1; // 返回一个无效值或抛出异常
        }

        // 深拷贝原数组到新数组
        int[] copyArray = Arrays.copyOf(array, stackSize);
        // 对新数组进行排序
        Arrays.sort(copyArray);
        // 返回排序后的第一个元素,即最小值
        return copyArray[0];
    }

}

3.字符串解码

链接:字符串解码

示例 1:

输入: s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入: s = "3[a2[c]]"
输出:"accaccacc"

这题很有难度,看起来不难,但是写起来总是被绕进去,第一个问题就是,如何保存对应括号内的元素的重复次数,第二个问题,如何处理嵌套,也就是示例2的情况。

首先需要确认区间问题,也就是怎么分组,将 **数字 + [ 元素 ]**作为一个整体,先不考虑嵌套问题,我们就需要将每个区间的数字保存起来

也就是说,当出现 ] 时 ,就需要进行整个区间的元素合并,数字 * 元素并与处理好的字符串进行拼接,那么大体的思路是这样,难度是我们该如何实现和处理嵌套问题

这里就需要使用栈,我的思路是使用双栈

思路:

第一个栈用于保存每个区间的出现次数,第二个栈用于保存处理好的字符串。

StringBuilder代表正在处理的字符串

当遍历到数字时,先记录下来,当遍历到左括号时,证明区间开始,将本次区间的出现次数和处理过的元素保存到对应的栈中(先进后出)

为了处理嵌套问题,我们需要将变量初始化,避免出现错误

当遍历到元素时,正常的添加到StringBuilder中

最后遍历到右括号,也就是区间结束时,证明这次的元素遍历结束,取出本次区间的出现次数,和之前处理好的字符串,将记录下来的StringBuiler与其进行拼接,这时StringBuilder存储的就是最终结果

代码如下:

java 复制代码
import java.util.Stack;

public class StringDecoding {
    /*
    字符串解码
     */
    public String decodeString(String s) {
        //存储出现次数
        Stack<Integer> countStack = new Stack<>();
        //存储已经处理好的字符串
        Stack<StringBuilder> stringStack = new Stack<>();
        //正在处理的字符串
        StringBuilder currentString = new StringBuilder();
        //出现数字
        int currentNumber = 0;
        //  2[ab2[c]]  --> abccabcc
        //  ab2[c]  --> abcc
        for (char c : s.toCharArray()) {
            if (c >= '0' && c <= '9'){
                currentNumber = currentNumber * 10 + (c - '0');
            } else if (c == '[') {
                //进入第一个区间
                //保存已经处理好的字符串
                //保存该区间的重复次数
                stringStack.push(currentString);
                countStack.push(currentNumber);

                //可能还有嵌套,需要重复次数清0,字符串同理
                currentNumber = 0;
                currentString = new StringBuilder();

            } else if (c == ']') {
                //证明区间已经结束,取出该区间的重复值
                Integer integer = countStack.pop();
                //取出正在处理,也就是区间的字符串
                StringBuilder stringBuilder = currentString;
                //取出保存好的字符串,进行拼装
                currentString = stringStack.pop();
                for (int i = 0; i < integer; i++) {
                    currentString.append(stringBuilder);
                }
            }else {
                //正常添加
                currentString.append(c);
            }
        }
        return currentString.toString();
    }
}

4.每日温度

链接:每日温度

这道题也是看别的大佬做出来的哈哈哈,他讲的很清晰,大家有兴趣可以去看一下这篇文章吧

【Java图解算法】739.每日温度_每日温度 算法 java-CSDN博客

今天的练习就到这里了!

相关推荐
IT猿手1 小时前
基于PWLCM混沌映射的麋鹿群优化算法(Elk herd optimizer,EHO)的多无人机协同路径规划,MATLAB代码
算法·elk·机器学习·matlab·无人机·聚类·强化学习
m0_675988236 小时前
Leetcode2545:根据第 K 场考试的分数排序
python·算法·leetcode
破-风6 小时前
leetcode---mysql
算法·leetcode·职场和发展
Wils0nEdwards6 小时前
Leetcode 合并两个有序链表
算法·leetcode·链表
eternal__day8 小时前
数据结构十大排序之(冒泡,快排,并归)
java·数据结构·算法
姚先生978 小时前
LeetCode 35. 搜索插入位置 (C++实现)
c++·算法·leetcode
Theodore_10228 小时前
3 需求分析
java·开发语言·算法·java-ee·软件工程·需求分析·需求
阿华的代码王国9 小时前
【算法】栈
数据结构·算法·leetcode·
SpongeG9 小时前
数据结构_平衡二叉树
数据结构·算法
arnold669 小时前
华为OD E卷(100分)31-敏感字段加密
算法·华为od