LeetCode栈和队列——代码随想录

栈和队列

基本实现

关于java中栈和队列的实现,下面链接有讲到如何选择接口实现与方法。
java中栈和队列的实现

232.用栈实现队列

双栈

class MyQueue {
    
    Stack<Integer> s1;//Vector下的Stack尽量不用
    Stack<Integer> s2;
    //Deque<Integer> s1;
    
    public MyQueue() {
        s1 = new Stack<Integer>(); 
        s2 = new Stack<Integer>(); 
       	//s1 = new ArrayDeque();
    }

    public void push(int x) {
        s1.push(x);
    }
    
    public int pop() {
       if(s2.empty()){
            in2out();
        }
        return s2.pop();
    }
    
    public int peek() {
        if(s2.empty()){
            in2out();
        }
        return s2.peek();
    }
    
    public boolean empty() {
        return s2.empty()&& s1.empty();
    }

    private void in2out() {
        while (!s1.isEmpty()) {
            s2.push(s1.pop( ));
        }
    }
}

在这里插入图片描述

225. 用队列实现栈

双栈

class MyStack {
    Deque<Integer> queue1;
    Deque<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList();
        queue2 = new LinkedList();
    }

    public void push(int x) {
        queue1.offer(x);
    }

    public int pop() {
        int length = queue1.size();
        for(int i = 0 ; i< length-1 ; i++){
            int t = queue1.pop();
            queue2.offer(t);
        }
        int t = queue1.pop();

        Deque<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
        return t;
    }

    public int top() {
        int t = 0;
        int length = queue1.size();
        for(int i = 0 ; i< length ; i++){
            t = queue1.pop();
            queue2.offer(t);
        }

        Deque<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
        return t;
    }

    public boolean empty() {
        return queue1.isEmpty();
    }
}

在这里插入图片描述
单栈实现

class MyStack {
    Deque<Integer> queue1;

    public MyStack() {
        queue1 = new LinkedList();
    }
    
    public void push(int x) {
        queue1.offer(x);
    }
    
    public int pop() {

        for(int i =0 ; i< queue1.size()-1; i++){
            int t = queue1.pop();
            queue1.offer(t);
        }
        return queue1.pop();
        
    }
    
    public int top() {
        int result = 0;
        for(int i =0 ; i< queue1.size(); i++){
            result = queue1.pop();
            queue1.offer(result);
        }
        return result;
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

20. 有效的括号

放入括号,条件判断

class Solution {
    public boolean isValid(String s) {
        char[] c = s.toCharArray();
        Deque<Character> stack = new ArrayDeque();
        for(int i = 0 ; i < c.length; i++){
            if(c[i] == '(' || c[i] == '{' || c[i] == '['  ){
                stack.push(c[i]);
            }else if( stack.isEmpty() ||
                     !( (c[i] == ')' && stack.poll() == '(') || 
                        ( c[i] == '}' && stack.poll() == '{' ) ||
                        ( c[i] == ']' && stack.poll() == '[' ) ))
            {
                return false;
            }
            
        }
        return stack.isEmpty();
    }
}

优化代码易懂

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {
            ch = s.charAt(i);
            //碰到左括号,就把相应的右括号入栈
            if (ch == '(') {
                deque.push(')');
            }else if (ch == '{') {
                deque.push('}');
            }else if (ch == '[') {
                deque.push(']');
            } else if (deque.isEmpty() || deque.peek() != ch) {
                return false;
            }else {//如果是右括号判断是否和栈顶元素匹配
                deque.pop();
            }
        }
        //最后判断栈中元素是否匹配
        return deque.isEmpty();
    }
}

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

利用栈(19ms)

class Solution {
    public String removeDuplicates(String s) {
        char[] c = s.toCharArray();
        Deque<Character> stack = new ArrayDeque();

        for(int i = 0; i< c.length; i++){
            if(stack.isEmpty() || c[i] != stack.peek()){
                stack.push(c[i]);
            }else{
                stack.pop();
            }
        }
        
        StringBuilder result = new StringBuilder();
        for(Character t : stack){
            result.append(t);
        }
        return result.reverse().toString();
    }
}

双指针(5ms)

class Solution {
    public String removeDuplicates(String s) {
        char[] ch = s.toCharArray();
        int fast = 0;
        int slow = 0;
        while(fast < s.length()){
            // 直接用fast指针覆盖slow指针的值
            ch[slow] = ch[fast];
            // 遇到前后相同值的,就跳过,即slow指针后退一步,下次循环就可以直接被覆盖掉了
            if(slow > 0 && ch[slow] == ch[slow - 1]){
                slow--;//两个元素都消失,slow将在下轮循环被fast替代
            }else{
                slow++;
            }
            fast++;
        }
        return new String(ch,0,slow);
    }
}

150. 逆波兰表达式求值

利用栈

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new ArrayDeque();
        int result = 0;
        for(int i = 0; i< tokens.length; i++){
            if(tokens[i].equals("+")){
                int num1 = stack.poll();
                int num2 = stack.poll();
                stack.push(num2+num1);
            }else if(tokens[i].equals("-")){
                int num1 = stack.poll();
                int num2 = stack.poll();
                stack.push(num2-num1);
            }else if(tokens[i].equals("*") ){
                int num1 = stack.poll();
                int num2 = stack.poll();
                stack.push(num2*num1);
            }else if(tokens[i].equals("/")){
                int num1 = stack.poll();
                int num2 = stack.poll();
                stack.push(num2/num1);
            }else{
                stack.push(Integer.parseInt(tokens[i]));
            }
        }
        return stack.poll();
    }
}

优化

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList();
        for (String s : tokens) {
            if ("+".equals(s)) {        
                stack.push(stack.pop() + stack.pop());      
            } else if ("-".equals(s)) {
                stack.push(-stack.pop() + stack.pop());
            } else if ("*".equals(s)) {
                stack.push(stack.pop() * stack.pop());
            } else if ("/".equals(s)) {
                int temp1 = stack.pop();
                int temp2 = stack.pop();
                stack.push(temp2 / temp1);
            } else {
                stack.push(Integer.valueOf(s));
            }
        }
        return stack.pop();
    }
}

239. 滑动窗口最大值(Hard)

利用单调队列

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        int[] result = new int[n-k+1];
        Deque<Integer> que = new ArrayDeque();
        
        for(int i = 0; i< n; i++){
            //每轮移走最前面的一个
            //如果要移除的数字,是队列头也就是最大值,则移走
            if( i >= k && nums[i-k] == que.peek()){
                que.poll();
            }
            //添加新元素,从后面遍历,确保队列单调
            while(!que.isEmpty() && nums[i]>que.getLast()){
                que.removeLast();
            }
            que.offer(nums[i]);
            //如果前k个已完成
            if(i >= k-1){
                result[i-k+1] = que.peek();
            }
        }
        return result;
    }
}

单调队列
在这里插入图片描述
输出过程
在这里插入图片描述

自己定义队列

分两个循环走,一样

//解法一
//自定义数组
class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    //弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
    //同时判断队列当前是否为空
    void poll(int val) {
        if (!deque.isEmpty() && val == deque.peek()) {
            deque.poll();
        }
    }
    //添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    //保证队列元素单调递减
    //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
    void add(int val) {
        while (!deque.isEmpty() && val > deque.getLast()) {
            deque.removeLast();
        }
        deque.add(val);
    }
    //队列队顶元素始终为最大值
    int peek() {
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 1) {
            return nums;
        }
        int len = nums.length - k + 1;
        //存放结果元素的数组
        int[] res = new int[len];
        int num = 0;
        //自定义队列
        MyQueue myQueue = new MyQueue();
        //先将前k的元素放入队列
        for (int i = 0; i < k; i++) {
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for (int i = k; i < nums.length; i++) {
            //滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
            myQueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myQueue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQueue.peek();
        }
        return res;
    }
}

不存数值存下表

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        int n = nums.length;
        int[] res = new int[n - k + 1];
        int idx = 0;
        for(int i = 0; i < n; i++) {
            // 根据题意,i为nums下标,是要在[i - k + 1, i] 中选到最大值,只需要保证两点
            // 1.队列头结点需要在[i - k + 1, i]范围内,不符合则要弹出
            while(!deque.isEmpty() && deque.peek() < i - k + 1){
                deque.poll();
            }
            // 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出
            while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }

            deque.offer(i);

            // 因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了
            if(i >= k - 1){
                res[idx++] = nums[deque.peek()];
            }
        }
        return res;
    }
}

347.前 K 个高频元素

PriorityQueue

  • 基于优先级堆的无界优先级queue 。 优先级队列的元素根据其natural ordering或队列构造时提供的Comparator进行排序 ,具体取决于使用的构造函数。 优先级队列不允许null元素。 依赖于自然排序的优先级队列也不允许插入不可比较的对象(这样做可能导致ClassCastException )。
PriorityQueue(Comparator<? super E> comparator) 创建具有默认初始容量的 PriorityQueue ,其元素根据指定的比较器进行排序。  
Comparator接口
/*Comparator接口说明:
 * 返回负数,形参中第一个参数排在前面;返回正数,形参中第二个参数排在前面
 * 对于队列:排在前面意味着往队头靠
 * 对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆),
 *                            从队头到队尾按从大到小排就是最大堆(大顶堆)--->队头元素相当于堆的根节点
 * */

普通实现

        //根据出现次数排序
        Collections.sort(newlist, new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                //降序
                return (int) o2.getValue() - o1.getValue();
            }
        });

函数式编程

        //函数式编程comparator,降序
        Collections.sort(newlist, (o1,o2)-> o2.getValue() - o1.getValue());

1.运用HashMap和List

HashMap存数值和其出现的次数,然后将键值对放到List中根据次数排序,输出前k个数值

时间复杂度O(N*logN),排序的复杂度,12ms-15ms(11% - 85%)

当数组长度小于7时,只走插入排序O(n^2),否则执行归并排序O(nlogn)。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //value,times
        Map<Integer,Integer> map = new HashMap<>();
		//getOrDefault替代判断
 	    for(int i = 0; i< nums.length;i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        //Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        //所有键值对放在list里
        List<Map.Entry<Integer, Integer>> newlist = new ArrayList<>(map.entrySet());
        //函数式编程comparator
        Collections.sort(newlist, (o1,o2)-> o2.getValue() - o1.getValue());
        int[] ans = new int[k];
		//输出前k个
        for(int i =0 ;i<k; i++){
            ans[i] = newlist.get(i).getKey();
        }
        return ans;
    }
}

2.运用HashMap和优先队列(堆)

大顶堆

维护所有元素,最后从堆顶便利,类似List, 12ms

    //解法1:基于大顶堆实现
	public int[] topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }
        //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从大到小排,出现次数最多的在队头(相当于大顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1]-pair1[1]);
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){//大顶堆需要对所有元素进行排序
            pq.add(new int[]{entry.getKey(),entry.getValue()});
        }
        int[] ans = new int[k];
        for(int i=0;i<k;i++){//依次从队头弹出k个,就是出现频率前k高的元素
            ans[i] = pq.poll()[0];
        }
        return ans;
    }
小顶堆

因为要统计最大前k个元素,维护K个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。 12ms,三种算法基本时间复杂度一致。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //value,times
        Map<Integer,Integer> map = new HashMap<>();
 	    for(int i = 0; i< nums.length;i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
        PriorityQueue<int[]> que = new PriorityQueue<>((o1,o2)->o1[1]-o2[1]);

        for(Map.Entry<Integer, Integer> t : map.entrySet()){//小顶堆只需要维持k个元素有序
            if(que.size()<k){//小顶堆元素个数小于k个时直接加
                //que.add() is the same
                que.offer(new int[]{t.getKey(),t.getValue()});
            }else if(t.getValue() > que.peek()[1]){
                {//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                //弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
                que.poll();
                que.offer(new int[]{t.getKey(),t.getValue()});
            }
        }
        int[] ans = new int[k];
        //依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
        for(int i = k-1 ; i >= 0 ; i--){
            ans[i] = que.poll()[0];
        }
        return ans;
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值