Stack算法题

分析,画图,代码,扩展,小结。

四步分析法
1.模拟:模拟题目的运行。
2.规律:尝试总结出题目的一般规律和特点。
3.匹配:找到符合这些特点的数据结构与算法。
4.边界:考虑特殊情况。

特性:先进后出(LIFO)、后进先出。

//Java代码
Stack<Character> t = new Stack<Character>();
t.push('a');
t.push('b');
t.peek(); // 这里得到栈顶元素'b'
t.pop(); // 这里将栈顶元素'b'弹出
t.peek(); // 此时栈顶元素为'a'
t.pop(); // 这里将栈顶元素'a'弹出

例 1:判断字符串括号是否合法

【题目】字符串中只有字符'('和')'。合法字符串需要括号可以配对。比如:
输入:"()"
输出:true
解释:(),()(),(())是合法的。)(,()(,(()是非法的。
请你实现一个函数,来判断给定的字符串是否合法。

boolean isValid(String s);

当遇到左括号'('时,进行压栈操作
当遇到右括号')'时,进行弹栈操作

boolean isValid(String s) {
// 当字符串本来就是空的时候,我们可以快速返回true
if (s == null || s.length() == 0) {
    return true;
}
// 当字符串长度为奇数的时候,不可能是一个有效的合法字符串
if (s.length() % 2 == 1) {
    return false;
}
// 消除法的主要核心逻辑:
Stack<Character> t = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
    // 取出字符
    char c = s.charAt(i);
    if (c == '(') {
    // 如果是'(',那么压栈
        t.push(c);
    } else if (c == ')') {
    // 如果是')',那么就尝试弹栈
        if (t.empty()) {
    // 如果弹栈失败,那么返回false
        return false;
        }
    t.pop();
}

return t.empty();
}

复杂度分析:每个字符只入栈一次,出栈一次,所以时间复杂度为 O(N),而空间复杂度为 O(N),因为最差情况下可能会把整个字符串都入栈。

计数器实现

package stackTest;

import java.util.Stack;

/**
 * @Author luminous
 * @Description //匹配括号的数量
 * @Date 11:31 2022/2/23
 **/
public class Demo2 {
    public static boolean isValid(String s) {
        if (s.length()%2==1)
            return false;
        if (s==null||s.length()==0)
            return true;
        int leftBraceNumber=0;
        for (int i=0;i<s.length();i++){
            char c=s.charAt(i);
            if (c=='(')
                leftBraceNumber+=1;
            else if (c==')'){
                if (leftBraceNumber<=0)
                    return false;
                leftBraceNumber--;
            }
        }
        return leftBraceNumber==0;


    }

    public static void main(String[] args) {
        System.out.println(isValid("()())"));

    }
}

大鱼吃小鱼
【题目】在水中有许多鱼,可以认为这些鱼停放在 x 轴上。再给定两个数组 Size,Dir,Size[i] 表示第 i条鱼的大小,Dir[i] 表示鱼的方向 (0 表示向左游,1 表示向右游)。这两个数组分别表示鱼的大小和游动的方向,并且两个数组的长度相等。鱼的行为符合以下几个条件:
1.所有的鱼都同时开始游动,每次按照鱼的方向,都游动一个单位距离;
2.当方向相对时,大鱼会吃掉小鱼;
3.鱼的大小都不一样。
输入:Size = [4, 2, 5, 3, 1], Dir = [1, 1, 0, 0, 0]
输出:3
请完成以下接口来计算还剩下几条鱼?

package stackTest;

import java.util.Stack;

public class Demo31 {
    public static int solution(int[] size, int[] dir){
        //鱼的数量
        int fishNumber= size.length;
//        鱼的方向
        final int left=0;
        final int right=1;
        Stack<Integer> t =new Stack<>();
        for (int i=0;i<fishNumber;i++){

            int curFishDir=dir[i];
            int curFishSize=size[i];
            //当前鱼是否被吃
            boolean hasEat=false;
//            栈中有鱼且栈中的鱼与当前的鱼方向相对,
            while(!t.empty()&&dir[t.peek()]==right&&curFishDir==left){
//                判断鱼的大小,如果吃掉就继续下一条鱼
                if (size[t.peek()]>curFishSize) {
                    hasEat=true;
                    break;
                }
//                如果栈中的鱼比当前鱼小,弹出去栈中的鱼,并准备入栈。
                t.pop();
            }
//            第一次直接入栈
            if (!hasEat)
                t.push(i);
        }
        return t.size();
    }
    public static void main(String[] args) {
        int size[] = {4, 2, 5, 3, 1};
        int dir[] = {1, 1, 1, 1, 0};
        System.out.println(solution(size,dir));

    }
}

找出数组中右边比我小的元素
【题目】一个整数数组 A,找到每个元素:右边第一个比我小的下标位置,没有则用 -1 表示。
输入:[5, 2]
输出:[1, -1]
解释:因为元素 5 的右边离我最近且比我小的位置应该是 A[1],最后一个元素 2 右边没有比 2 小的元
素,所以应该输出 -1。

package stackTest;

import java.util.Stack;

/**
 * @Author luminous
 * @Description //找到右边第一个比我小的下标位置,没有则用 -1 表示
 * @Date 11:31 2022/2/23
 **/
public class Demo41 {
    public static int[] findRightSmall(int[] A){
        int[] ans=new int[A.length];
        Stack<Integer> t=new Stack<>();
        for (int i=0;i<A.length;i++){
            while(!t.empty()&&A[t.peek()]>A[i]){
                ans[t.peek()]=i;
                t.pop();

            }
            t.push(i);
        }
        while(!t.empty()){
            ans[t.peek()]=-1;
            t.pop();
        }
        return ans;


    }


    public static void main(String[] args) {
        int size[] = {1, 2, 4, 9, 4,0,5};

        int dir[] = {1, 1, 0, 0, 0};
        for (int i :
                findRightSmall(size)) {
            System.out.println(i);
        }
        System.out.println(findRightSmall(size));

    }
}

例 4:字典序最小的 k 个数的子序列
【题目】给定一个正整数数组和 k,要求依次取出 k 个数,输出其中数组的一个子序列,需要满足:1.
长度为 k;2.字典序最小。
输入:nums = [3,5,2,6], k = 2
输出:[2,6]
解释:在所有可能的解:{[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6] 字典序最小。
所谓字典序就是,给定两个数组:x = [x1,x2,x3,x4],y = [y1,y2,y3,y4],如果 0 ≤ p < i,xp == yp 且 xi <
yi,那么我们认为 x 的字典序小于 y。

package stackTest;

import java.util.Stack;

/**
 * @Author luminous
 * @Description //
 * 给定一个正整数数组和 k,要求依次取出 k 个数,输出其中数组的一个子序列,
 * 需要满足:1.
 * 长度为 k;2.字典序最小。
 * 输入:nums = [3,5,2,6], k = 2
 * 输出:[2,6]
 * 解释:在所有可能的解:{[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中
 * ,[2,6] 字典序最小。
 * @Date 11:31 2022/2/23
 **/
public class Demo5 {
    public static int[] findSmallSeq(int[] nums, int k){
        int[] ans=new int[k];
        Stack<Integer> s=new Stack<>();
        for (int i=0;i< nums.length;i++){
            final int x=nums[i];
            final int left= nums.length-i;//剩下的元素
//            (s.size()+left>k)栈中的元素和剩余的元素不能大于k
            while(!s.empty()&&(s.size()+left>k)&&s.peek()>x){
                s.pop();
            }
            s.push(x);
        }
//        特殊情况:如果栈中的元素太多,就扔掉。
        while (s.size()>k){
            s.pop();
        }
        for (int i=k-1;i>=0;i--){
            ans[i]=s.peek();
            s.pop();
        }
        return ans;

    }

    public static void main(String[] args) {
        int size[] = {1, 2, 4, 9, 4,0,5};

        int dir[] = {1, 1, 0, 0, 0};
        for (int i :
                findSmallSeq(size,3)) {
            System.out.println(i);
        }
//        System.out.println(findRightSmall(size));

    }
}

 图片来自lagouedu

84. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

/*正在研究。。。
 * 思路:
 * 
 * 2个重要的性质:
 * 
 * 一个递增栈里面存放的是数组的下标 stack = [i, j]
 * 
 * 性质1: 对于j而言,下标[i+1 ... j-1] 这里面的元素的A[x]值都 >= A[j]
 *       对于i而言,(-1, i-1] 这里面元素的值都 >= A[i];
 * 
 * 如果此时A[k]要入栈。并且A[k] < A[j],要将A[j]出栈。
 * 
 * 性质2: A[j+1 ... k-1]这个区间里面的元素都大于A[j]
 *
 */

// @lc code=start
class Solution {
    public int largestRectangleArea(int[] A) {
        final int N = A == null ? 0 : A.length;

        // 虽然可以用Stack<Integer>,但是这里我们为了更快地操作,我们用
        // 数组模拟栈来运行,因为我们知道最多存放的内容实际上就是N个
        int top = 0;
        // s[top-1]表示栈顶元素
        int[] s = new int[N];

        int ans = 0;

        // 注意,这里我们取到了i == N
        // 按理说,不应该取到i == N的。但是这时候,主要是为了处理这种数组
        // A = [1, 2, 3]
        // 没有任何元素会出栈。
        // 那么最后我们用一个0元素,把所有的元素都削出栈。
        // 这样代码就可以统一处理掉。
        for (int i = 0; i <= N; i++) {
            // 注意:当i == N的时候,x = -1;
            // 比数组中的元素都要小。
            final int x = i == N ? -1 : A[i];
            while (top > 0 && A[s[top - 1]] > x) {
                // 计算以A[s[top]]的元素的高度的矩形。
                final int height = A[s[--top]];
                // i元素要将index = s[top-1]的元素出栈。
                // 那么根据性质2:
                // 此时A[s[top-1] .... i) 这个区间里面的元素都是
                // 大于A[s[top-1]]的
                final int rightPos = i;
                // 这里需要使用性质1.
                // 注意:当栈中一个元素都没有的时候,要取-1
                final int leftPos = top > 0 ? s[top - 1] : -1;
                final int width = rightPos - leftPos - 1;
                final int area = height * width;
                ans = Math.max(ans, area);
            }

            s[top++] = i;
        }

        return ans;
    }
}

// @lc code=end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值