【数据结构】栈(2)

简介: 【数据结构】栈

3.3 成员方法

3.3.1 压栈

首先得判断数组容量是否已满,如果满了则需要扩容,扩容为有效元素个数的两倍,如果没满则不需要扩容。直接在数组的 size 位置添加元素,然后 size++ 。因为 size 是有效元素的个数,数组下标是从 0 开始,size - 1 则为最后一个元素,size 则为最后一个元素后面的一个位置


public int push(int data) {
    // 判断是否需要增容
    if (this.size == this.elem.length) {
        this.elem = Arrays.copyOf(this.elem, this.size * 2);
    }
    // 压栈只能往栈顶压栈
    this.elem[this.size++] = data;
    return data;
}

3.3.2 出栈

首先判断栈是否为 null,如果为空则不能出栈,直接报异常。如果不为空让 size = size - 1,因为出栈了一个元素,那么有效元素个数也就需要减少一个


// 出栈
public int pop() {
    // 判断栈是否为null
    if (this.size == 0) {
        throw new MyStackEmptyException("栈为空!");
    }
    this.size = this.size - 1;
    return this.elem[this.size];
}

3.3.3 异常

public class MyStackEmptyException extends RuntimeException {
    public MyStackEmptyException() {
    }
    public MyStackEmptyException(String message) {
        super(message);
    }
}

3.3.4  查看元素

首先判断栈是否为 null,如果为空则不能出栈,直接报异常。如果不为空则直接返回最后一个元素


// 查看栈顶元素
public int peek() {
    // 判断栈是否为null
    if (this.size == 0) {
        throw new MyStackEmptyException("栈为空!");
    }
    return this.elem[this.size - 1];
}

3.3.5 清空栈

直接让 size = 0


public boolean empty() {
    return this.size == 0;
}

4.关于栈的 OJ 题

4.1 有效的括号 (题目来源:力扣)

题目:给定一个只包括 '( ',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。


有效字符串需满足:


左括号必须用相同类型的右括号闭合。

左括号必须以正确的顺序闭合。

每个右括号都有一个对应的相同类型的左括号。

解题思路:如果字符串为空,则返回 false。否则就实例化一个栈,依次遍历字符串中的每一个字符,如果为左括号,则直接入栈。如果为右括号,则先判断栈是否为空,如果为空则说明右括号没有对应的左括号,则直接返回false。如果栈不为空,则出栈顶的元素,并且将这个元素的值用一个临时变量存储起来,然后判断这两个括号是否匹配,如果不匹配则直接返回false。如果匹配则继续执行字符串下一个字符,直到遍历完字符串中的每一个字符,才会跳出循环。跳出循环后,判断栈是否为空,如果为空,说明括号全部匹配。如果不为空,说明左括号比右括号多则返回 false。


import java.util.*;
class Solution {
    public boolean isValid(String s) {
        //判断字符串是否为空
        if(s == null) {
            return false;
        }
        //实例化一个栈
        Stack<Character> stack = new Stack<>();
        //遍历字符串中的每一个字符
        for(int i = 0; i < s.length(); i++) {
            char right = s.charAt(i);//获取字符串中第i个位置的字符
            //判断字符串是否为左括号
            if(right == '(' || right == '{' || right == '[') {
                stack.push(right);//入栈
            } else {
                //判断栈是否为空
                if(stack.empty()) {
                    return false;
                } else {
                    char left = stack.pop();//出栈
                    //判断左括号与右括号是否匹配
                    if(!(left == '(' && right == ')' || left == '{' && right == '}' || left == '[' && right == ']')) {
                        return false;
                    }
                }
            }
        }
        if(stack.empty()) {
            return true;
        }
        return false;
    }
}

4.2 逆波兰表达式求值(题目来源:力扣)

题目:根据逆波兰表示法,求表达式的值。


有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。


注意 两个整数之间的除法只保留整数部分。


可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。


示例 1:


输入:tokens = ["2","1","+","3","*"]

输出:9

解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9


解题思路:首先判断这个字符串数组是否为空,如果为空我们就返回一个无效的值(假设这个无效的值为:-1),那我们就返回 -1。如果不为空我们就创建一个存储整型元素的栈,用来存储左操作数和右操作数。然后我们就遍历字符串数组,每一次遍历的时候我们都需要判断它是否为 + - * / 这些操作符,如果不是就将这个字符串转换为整型数入栈,如果是则需要判断栈当中是否有两个及以上的操作数,如果有则需要在栈中pop两个元素并存储在两个整型变量中当做左操作数和右操作数,然后匹配对应的操作符进行运算将运行的结果在放入栈中,如果栈中小于两个元素则返回无效值。如果在循环的时候没有返回,说明运算完成,将栈中最后一个元素pop并且返回即可。


class Solution {
    public int evalRPN(String[] tokens) {
        if (tokens == null) {
            return -1;
        }
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if (!(tokens[i].equals("+") || tokens[i].equals("-") || tokens[i].equals("*")  || tokens[i].equals("/"))) {
                int tmp = Integer.parseInt(tokens[i]);
                stack.push(tmp);
            } else if (stack.size() >= 2){
                int right = stack.pop();
                int left = stack.pop();
                int sum = 0;
                switch (tokens[i]) {
                    case "+":sum = left + right;break;
                    case "-":sum = left - right;break;
                    case "*":sum = left * right;break;
                    case "/":sum = left / right;break;
                }
                stack.push(sum);
            } else {
                return -1;
            }
        }
        return stack.pop();
    }
}


4.3 栈的压入、弹出序列

题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。


解题思路:首先判断传过来的两个整型数组是否都为空,如果都为空则返回 true。否则判断它们两个当中是否有一个为空,如果有则返回 false。否则就判断两个数组的长度是否相同,如果不相同则直接返回 false。然后上述的条件都不满足则说明数组都不为空,且长度相等。创建一个存放整型元素的栈,创建两个整型变量i,j用来存放两个数组的下标位置。然后遍历 pushA 数组,在遍历的时候判断 pushA[i] 位置的元素是否与 popA[j] 位置的元素不相等,如果不相等则把 pushA[i] 位置的元素入栈,如果相等则不让 pushA[i] 位置的元素入栈,然后 j++,循环判断栈是否为空且判断栈顶元素与此时的 popA[j] 是否相等,如果不为空且栈顶元素与此时的 popA[j] 相等,则出栈,然后j++。当遍历完 pushA 时,判断栈是否为空,如果为空则返回 true,否则返回 false。


import java.util.*;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA == null && popA == null) {
            return true;
        } else if(pushA == null || popA == null) {
            return false;
        } else if(pushA.length != popA.length) {
            return false;
        }
        Stack<Integer> stack = new Stack<>();
        int i = 0;
        int j = 0;
        while(i < pushA.length) {
            if(pushA[i] != popA[j]) {
                stack.push(pushA[i]);
            } else {
                j++;
                while(!(stack.empty()) && stack.peek() == popA[j]) {
                    stack.pop();
                    j++;
                }
            }
            i++;
        }
        if(stack.empty()) {
            return true;
        }
        return false;
    }
}



目录
打赏
0
0
0
0
1
分享
相关文章
|
8月前
|
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
754 9
|
8月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
185 58
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
23 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
6月前
|
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
301 77
|
5月前
|
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
81 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
6月前
|
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
222 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
6月前
|
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
114 9
|
6月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
163 7
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等