力扣hot100|栈、堆

二刷:

单调栈, 堆排序(借助优先队列), 哈希去重(统计出现频率)

单调栈:一个一个元素(st是单调栈,保存当前元素后比它大的最近的元素的索引)

堆排序

哈希去重(统计出现频率)

一:栈

1.有效的括号

遇到左括号时,将其对应的右括号入栈

遇到右括号时,与栈顶元素比较,如果匹配则将栈顶元素出栈否则返回 false

class Solution {
    public boolean isValid(String s) {
        Deque<Character> stk=new LinkedList<Character>();
        char[] ss=s.toCharArray();

        for (char c : ss) {
            if (c=='(') {
                stk.push(')');
            }else if (c=='[') {
                stk.push(']');
            }else if (c=='{') {
                stk.push('}');
            }else if (stk.isEmpty()||c!=stk.pop()) {
                return false ;
            }
        }

        return stk.isEmpty();
    }
}

2.最小栈

class MinStack {
    private Stack<Integer> stack;//正常的栈          1 - 2 - 3 - 0
    private Stack<Integer> min_stack;//用来保存最小值 1 - 1 - 1 - 0

    /**
     * 构造函数初始化两个栈
     */
    public MinStack() {
        stack =new Stack<>();
        min_stack=new Stack<>();
    }
    
    /**
     * 将元素压入栈中,并更新最小值栈
     */
    public void push(int val) {
        stack.push(val);
        if (min_stack.isEmpty()) {
            min_stack.push(val);// 当前最小栈为空时,直接压入当前元素
        }else if (min_stack.peek()>val) {
            min_stack.push(val);// 当前元素小于最小值时,更新最小值栈
        }else {
            min_stack.push(min_stack.peek());// 当前元素不小于最小值时,保持最小值栈不变
        }
    }
    
    /**
     * 从栈中弹出顶部元素
     */
    public void pop() {
        stack.pop();
        min_stack.pop();
    }
    
    /**
     * 获取栈顶元素
     */
    public int top() {
        return stack.peek();
    }
    
    /**
     * 获取栈中的最小元素
     */
    public int getMin() {
        return min_stack.peek();
    }
}

3.字符串解码

class Solution {

    public String decodeString(String s) {
        StringBuilder res=new StringBuilder();//返回值
        //每次遍历后,用stack保存当前的res(就一个元素)。
        //下次遍历结束后,res 为stack.pop()与 当前遍历的结果temp(局部变量)的拼接
        Deque<String> stack_res=new LinkedList<>();
        //保存重复次数,注意次数不一定小于10 ,所以用栈保存、
        Deque<Integer> stack_time =new LinkedList<>();
        int time=0;//保存重复次数,初始为0

        for (char c : s.toCharArray()) {
            if (Character.isDigit(c)) {
                time=time*10+c-'0';//强转c为数字
            }else if (c=='[') {
                //把之前的数字存进栈里
                stack_time.push(time);
                //把之前的结果res保存到栈里
                stack_res.push(res.toString());
                time=0;//初始化下一轮计数初始值为0
                res=new StringBuilder();//重置为null
            }else if (c==']') {
                //循环time(保存在栈里)次保存到中间变量中temp
                StringBuilder temp=new StringBuilder();
                int cur_time=stack_time.pop();
                for (int i = 0; i <cur_time ; i++) {
                    temp.append(res);
                }
                //拼接
                res = new StringBuilder(stack_res.pop()+temp);
            }else {
                res.append(c);
            }
        }

        return res.toString();
    } 
}

4.每日温度--单调栈

单调栈【基础算法精讲 26】_哔哩哔哩_bilibili

栈st保存的是  索引下标

st.append(i),入栈的下标

ans为栈顶的下标减去当前元素的下标

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Stack<Integer> st=new Stack<>();//保存下标
        int L=temperatures.length;
        int[] ans =new int[L];//返回结果

        for (int i = L-1; i >=0; i--) {
            int t=temperatures[i];
            while (!st.isEmpty() && t>=temperatures[st.peek()]) {
                st.pop();//去除st中比当前元素小的值
            }
            if (!st.isEmpty()) {
                ans[i]=st.peek()-i;
            }
            st.push(i);
        }
        return ans;
    }
}

用deque快

 5.柱状图中最大的矩形--单调栈

单调栈:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int L=heights.length;
        Deque<Integer> stk_left=new LinkedList<>();//保存左端的小于当前h的最近元素下标
        int[] left=new int[L];
        Deque<Integer> stk_right=new LinkedList<>();//保存右端的小于当前h的最近元素下标
        int[] right=new int[L];
        int ans=0;//返回的结果

        //构造right
        for (int i = L-1; i >=0; i--) {
            int h=heights[i];
            while (!stk_right.isEmpty() && h<=heights[stk_right.peek()]) {
                stk_right.pop();
            }
            // if (!stk_right.isEmpty()) {
            //     right[i]=stk_right.peek();
            // } 
            right[i] = stk_right.isEmpty() ? L :stk_right.peek();
            stk_right.push(i);
        }

        //构造left
        for (int i = 0; i <L; i++) {
            int h=heights[i];
            while (!stk_left.isEmpty() && h<=heights[stk_left.peek()]) {
                stk_left.pop();
            }
            // if (!stk_left.isEmpty()) {
            //     left[i]=stk_left.peek();
            // }
            left[i] = stk_left.isEmpty() ? -1 :stk_left.peek();
            stk_left.push(i);
        }

        //计算每个i上的矩形面积
        for (int i = 0; i < L; i++) {
            int s= heights[i]*(right[i]-left[i]-1);
            ans=Math.max(ans, s);
        }

        return ans;
    }
}

二:堆

1.数组中的第K个最大元素

堆排序

for (int num : nums) {
    heap.offer(num);
    if (heap.size()>k) {
         heap.poll(); //弹出的是第k+1个元素
     }
 }

2.前 K 个高频元素

数组去重:

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();
     //统计各元素出现的次数---去重了
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0)+1);
        }
    //遍历map,用最小堆保存频率最大的k个元素
    // 构建最小堆,map的值比较大小,但存的k
    PriorityQueue<Integer> pq=new PriorityQueue<>((a,b)->map.get(a)-map.get(b));
    //弹出第k+1个元素,最小堆保存频率最大的k个元素
    for (Integer key: map.keySet()) {
        pq.offer(key);
        if (pq.size()>k) {
            pq.poll();
        }
    }
    
    // 取出最小堆中的元素--pop()
    int[] ans =new int[k];
    int i=0;
    while (!pq.isEmpty()) {
        ans[i++]=pq.poll();
    }

    return ans;
    }
}

3.数据流的中位数

类似题目:

求中值

class MedianFinder {
    private PriorityQueue<Integer> minQ;//最小堆,升序
    private PriorityQueue<Integer> maxQ;//最大堆,降序
    private Integer m;//中值

    public MedianFinder() {
        minQ=new PriorityQueue<>((a,b)->b-a);
        maxQ=new PriorityQueue<>((a,b)->a-b);
    }

    public void addNum(int num) {
        if (m==null) {
            //第一个元素
            m=num; 
            return;
        }
        if (num>m) minQ.add(num);//加入最小堆
        if (num<m) maxQ.add(num);//加入最大堆
        //判断最小堆与最大堆的个数差
        if (minQ.size()-maxQ.size()>=2) {
            //最小堆多,栈顶元素为新的m,原来的m给最大堆
            //poll最小堆移除一个
            maxQ.add(m);
            m=minQ.poll();
        }else if (maxQ.size()-minQ.size()>=2) {
            //最大堆多,栈顶元素为新的m,原来的m给最小堆
            //poll最大堆移除一个
            minQ.add(m);
            m=maxQ.poll();
        }
    }
    
    public double findMedian() {
        if (maxQ.size()==minQ.size()) {
            return m;
        }else if (maxQ.size()-minQ.size()==1) {
            return (m+maxQ.peek())/2.0;
        }else {
            return (m+minQ.peek())/2.0;
        }
    }
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值