LeetCode刷题-栈

1.有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

解题思路:
(1)判断字符串的长度是否为奇数,如果为奇数,返回false;
(2)遍历字符串,遇到‘{’、‘[’、‘(’入栈;
(3)如果栈顶元素与当前元素刚好匹配,将栈顶元素出栈;若不匹配,返回false;
(4)栈最后为空,返回true,否则false.

class Solution {
    public boolean isValid(String s) {
        if(s.length() % 2 == 1){
            return false;
        }

        Stack<Character> stack = new Stack<>();
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            if(c == '{' || c == '[' || c == '('){
                stack.push(c);
            }else{
                if(stack.empty()){
                    return false;
                }
                char curchar = stack.peek();
                if((curchar == '{' && c == '}') || (curchar == '(' && c == ')') || (curchar == '[' && c == ']')){
                    stack.pop();
                }else{
                    return false;
                }
            
            }
            
        }

        return stack.empty();

    }
}

运行结果:
在这里插入图片描述

2.最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

解题思路:
设置一个辅助栈min_stack,保存最小元素。
push()方法: 每当push()新值进来时,如果 小于等于 min_stack栈顶值,则一起push()到min_stack,即更新了栈顶最小值;
pop()方法: 判断将pop()出去的元素值是否是min_stack栈顶元素值(即最小值),如果是则将min_stack栈顶元素一起pop(),这样可以保证min_stack栈顶元素始终是stack中的最小值。
getMin()方法: 返回min_stack栈顶即可。

class MinStack {

    private Stack<Integer> stack;
    private Stack<Integer> min_stack;

    /** initialize your data structure here. */
    public MinStack() {
        stack = new Stack<>();
        min_stack = new Stack<>();

    }
    
    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();
        }

    }
    
    public int top() {
        return stack.peek();

    }
    
    public int getMin() {
        return min_stack.peek();

    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

运行结果:
在这里插入图片描述

3.用队列实现栈

使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

解题思路:
使用top维护栈顶元素。考虑入栈时直接让元素加入到队列中,更新top的值。
弹出栈顶元素时,也就是要将队列中最后一个元素删除并返回。假设现在队列中有n个元素,将前n-1个元素依次出队再入队,此时要弹出的元素就在队列头部,此时调用q.remove()即可将栈顶元素弹出。不过这里要注意更新变量tail,因为弹出之后栈顶元素就变了。

class MyStack {

    Queue<Integer> queue;
    int top;

    /** Initialize your data structure here. */
    public MyStack() {
        queue = new LinkedList<>();
      
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue.add(x);
        top = x;
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        int size = queue.size();
        for(int i = 0; i < size-2; i++){
            queue.add(queue.remove());
        }
        top = queue.remove();
        queue.add(top);
        return queue.remove();
          
    }
    
    /** Get the top element. */
    public int top() {
        return top;
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

运行结果:
在这里插入图片描述

4.用栈实现队列

使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

解题思路:
栈:后进先出;队列:先进先出。使用两个栈实现队列。
入队操作:栈的push操作。
出队操作:当stack2为空时,需要将stack1中所有元素弹出并放入stack2中,再将此时stack2中栈顶元素弹出即可。如果out不为空时,直接弹出其栈顶元素即可。
peek操作:与pop类似,只不过取元素时不删除栈中元素,而是返回stack2的栈顶元素。
判断是否为空:由于我们使用了两个栈来存放元素,所以必须两个栈都为空时才能判断队列为空。

lass MyQueue {

    Stack<Integer> stack_1;
    Stack<Integer> stack_2;
    

    /** Initialize your data structure here. */
    public MyQueue() {
        stack_1 = new Stack<>();
        stack_2 = new Stack<>();
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        stack_1.push(x);    

    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if (stack_2.empty()) {
            int size = stack_1.size();
            for (int i = 0; i < size; i++)
                stack_2.push(stack_1.pop());
        }
        return stack_2.pop();

    }
    
    /** Get the front element. */
    public int peek() {
       if (stack_2.empty()) {
            int size = stack_1.size();
            for (int i = 0; i < size; i++)
                stack_2.push(stack_1.pop());
        }
        return stack_2.peek();

    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return (stack_1.empty() && stack_2.empty());

    }
}

运行结果:
在这里插入图片描述

5.下一个更大元素I

给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在
nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1。

解题思路:
首先忽略数组 nums1,先将 nums2 中的每一个元素,求出其下一个更大的元素。随后将此元素与其更大元素放入哈希映射(HashMap)中,再遍历数组 nums1,并直接找出答案。对于 nums2,我们可以使用单调栈来解决这个问题。
首先把第一个元素 nums2[0] 放入栈,随后对于第二个元素 nums2[1],如果 nums2[1] > nums2[0],那么我们就找到了 nums2[0] 的下一个更大元素 nums2[1],此时就可以把 nums2[0] 出栈并把 nums2[1] 入栈;如果 nums2[1] <= nums2[0],我们就仅把 nums2[1] 入栈。对于第三个元素 nums2[2],此时栈中有若干个元素,那么所有比 nums2[2] 小的元素都找到了下一个更大元素(即 nums2[2]),因此可以出栈,在这之后,我们将 nums2[2] 入栈,以此类推。
在这里插入图片描述

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {

        Stack<Integer> stack = new Stack<>();
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();

        int[] res = new int[nums1.length];

        for(int i = 0; i < nums2.length; i++){
            while(!stack.empty() && nums2[i] > stack.peek()){
                map.put(stack.pop(), nums2[i]);
            }
            stack.push(nums2[i]);
        }
        while(!stack.empty()){
            map.put(stack.pop(), -1);
        }

        for(int i = 0; i < nums1.length; i++){
            res[i] = map.get(nums1[i]);
        }

        return res;

    }
}

运行结果:
在这里插入图片描述

6.棒球比赛

你现在是棒球比赛记录员。 给定一个字符串列表,每个字符串可以是以下四种类型之一:
1.整数(一轮的得分):直接表示您在本轮中获得的积分数。
2. “+”(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。
3. “D”(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。
4. “C”(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。

每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。 你需要返回你在所有回合中得分的总和。

解题思路:
用栈保存分数。如果遇到整数,直接将整数入栈,如果遇到“+”,将栈顶元素和下一个元素相加的和入栈;如果遇到“D”,将栈顶元素的2倍入栈;如果遇到“C”,将栈顶元素出栈。

class Solution {
    public int calPoints(String[] ops) {
        Stack<Integer> stack = new Stack<>();
        for(int i = 0; i < ops.length; i++){
            if(ops[i].equals("+")){
                int top = stack.pop();
                int newtop = top + stack.peek();
                stack.push(top);
                stack.push(newtop);
            }else if(ops[i].equals("D")){
                stack.push(stack.peek() * 2);
            }else if(ops[i].equals("C")){
                stack.pop();
            }else{
                stack.push(Integer.valueOf(ops[i]));
            }
        }

        int sum = 0;
        for(int a : stack){
            sum += a;
        }

        return sum;

    }
}

运行结果:
在这里插入图片描述

7.比较含退格的字符串

给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

解题思路:
利用栈来保存每次输入的字符。如果字符不等于‘#’,就入栈;否则(栈不为空)出栈。

class Solution {
    public boolean backspaceCompare(String S, String T) {

        return build(S).equals(build(T));
    }

    public String build(String S) {
        Stack<Character> ans = new Stack();
        for (char c: S.toCharArray()) {
            if (c != '#')
                ans.push(c);
            else if (!ans.empty())
                ans.pop();
        }
        return String.valueOf(ans);
    }
}

运行结果:
在这里插入图片描述

8.删除最外层的括号

有效括号字符串为空 ("")、"(" + A + “)” 或 A + B,其中 A 和 B 都是有效的括号字符串,+
代表字符串的连接。例如,"","()","(())()" 和 “(()(()))” 都是有效的括号字符串。

如果有效字符串 S 非空,且不存在将其拆分为 S = A+B 的方法,我们称其为原语(primitive),其中 A 和 B
都是非空有效括号字符串。

给出一个非空有效字符串 S,考虑将其进行原语化分解,使得:S = P_1 + P_2 + … + P_k,其中 P_i
是有效括号字符串原语。

对 S 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 S 。

解题思路:
思路1:遍历字符串,遇到左括号就入栈,遇到右括号就出栈,每次栈空的时候,都说明找到了一个原语,记录下每个原语的起始位置和结束位置,取原字符串在原语的起始位置+1到原语的结束位置的子串便得到原语删除了最外层括号的字符串,拼接,即可解出答案。

class Solution {
    public String removeOuterParentheses(String S) {
        int start = 0;
        int end = 0;
        boolean flag = false;

        Stack<Character> stack = new Stack<>();
        StringBuilder sb = new StringBuilder();

        for(int i = 0; i < S.length(); i++){
            char c = S.charAt(i);
            if(c == '('){
                stack.push(c);
                if(!flag){
                    start = i;
                    flag = true;
                }
            }
            if(c == ')'){
                stack.pop();
                if(stack.isEmpty()){
                    end = i;
                    sb.append(S.substring(start+1, end));
                    flag = false;
                    start = end;
                }
            }
        }

        return sb.toString();
    }
}

思路2:不使用栈。

class Solution {
    public String removeOuterParentheses(String S) {

        StringBuilder sb = new StringBuilder();
        int level = 0;

        for(char c:S.toCharArray()){
            if(c == ')'){
                level--;
            }
            if(level >= 1){
                sb.append(c);
            }
            if(c == '('){
               level++;
            }
        }

        return sb.toString();

    }
}

运行结果:
在这里插入图片描述

9.删除字符串中的所有相邻重复项

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

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

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

解题思路:
利用栈保存不重复的字符,如果当前字符与栈顶元素相同,则将栈顶元素出栈;如果不同则入栈。

class Solution {
    public String removeDuplicates(String S) {
        Stack<Character> stack = new Stack<>();
        
        for(int i = 0; i < S.length(); i++){
            char c = S.charAt(i);
            if(stack.isEmpty() || c != stack.peek()){
                stack.push(c);
            }else {
                stack.pop();
            }
        }

        StringBuilder str = new StringBuilder();
        for (Character c : stack) {
            str.append(c);
        }
        return str.toString();

    }
}

运行结果:
在这里插入图片描述

10.用栈操作构造数组

给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。

请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。

题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。

请返回构建目标数组所用的操作序列。

解题思路:
建立两个指针i和j,i指向target数组,j指向list = {1,2,…,n}。
同时遍历target和list,当i指向的元素大于j指向的元素,说明此时j指向的元素在target中不需要,因此执行"Push"和"Pop",略去不需要的元素,同时j自增,直至i指向的元素不大于j指向的元素。当i指向的元素不大于j指向的元素(实为等于),说明此时j指向的元素正是target中需要的元素,因此执行"Push"。
i遍历至target末端退出。

class Solution {
    public List<String> buildArray(int[] target, int n) {
        List<String> list = new ArrayList<>();
        int j = 1;
        for(int i = 0; i < target.length;i++){
            while(target[i] > j){
                list.add("Push");
                list.add("Pop");
                j++;
            }
            list.add("Push");
            j++;
        }

        return list;

    }
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的内容,推荐的LeetCode刷题顺序是按照题目类型刷题,优先选择树、链表、二分查找、DFS、BFS、动态规划等常见类型的题目。可以先做2~4道简单题,然后再做中等难度的题目。在选择题目时,可以优先选择题目序号小、点赞多、提交成功率高的题目,这样可以从简单入手,节省时间。同时,LeetCode每道题目都有“模拟面试”功能,可以给自己设定时间限制,如果做不出来可以看答案,然后记住思路后再自己尝试一遍。每种类型的题目做完10+道后,可以总结规律。 根据引用\[3\]的内容,题目可以按照不同的分类进行刷题,比如数组与贪心算法、子数组与贪心算法、子序列与贪心算法、数字与贪心、单调法、双指针法等。可以根据自己的兴趣和需求选择相应的题目进行刷题。 综上所述,LeetCode刷题顺序可以按照题目类型或者题目分类进行选择。 #### 引用[.reference_title] - *1* [LeetCode 刷题顺序,按标签分类,科学刷题!](https://blog.csdn.net/fengyuyeguirenenen/article/details/125099023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [leetcode 刷题指南 & 刷题顺序](https://blog.csdn.net/qijingpei/article/details/125561071)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [leetcode-刷题顺序推荐](https://blog.csdn.net/weixin_38087674/article/details/114107841)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值