玩转算法面试(四)栈和队列(Java)

本文详细探讨了栈和队列在算法面试中的应用,包括使用Java实现有效的括号检查、二叉树的递归与非递归遍历、广度优先搜索(BFS)以及单调队列在滑动窗口最大值问题中的作用。通过实例讲解LeetCode题目,如二叉树的层序遍历、前K个高频元素等。
摘要由CSDN通过智能技术生成

栈和队列

一、栈

1、简单的栈运用

20. 有效的括号

给定一个字符串,只包含(,[,{,),],}, 判定字符串中的括号匹配是否合法。

  • 如"()”,“()[{"是合法的
  • 如“(]","([)]" 是非法的
class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for(int i = 0;i < s.length();i++) {
            char t = s.charAt(i);
            if(t == '[' || t == '{' || t == '(' ) {
                stack.push(t);
            }
            else {
            if(stack.isEmpty()) return false;
            if(t == ')') {
                if(stack.isEmpty()) return false;
                if(stack.peek() == '(') stack.pop();
                else return false;
            }
            if(t == '}') {
    
                if(stack.peek() == '{') stack.pop();
                else return false;
            }
            if(t == ']') {

                if(stack.peek() == '[') stack.pop();
                else return false;
            }
        }
        }

        if(!stack.isEmpty()) return false;
        
        return true;
    }
}

拓展:

  1. Evaluate Reverse Polish Notation

  2. Simplify Path

2、栈和递归的紧密关系

2.1、递归算法
二叉树中的算法

前序、中序和后序遍历

  1. Binary Tree Preorder Traversal

image.png

image.png

  1. Binary Tree Inorder Traversal

  2. Binary Tree Postorder Traversal

2.2、使用栈模拟系统栈,写出非递归程序
LeetCode 144. 二叉树前序遍历

image.png

image.png

public class Solution144 {

    // Definition for a binary tree node.
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    private class Command{
        String s;   // go, print
        TreeNode node;
        Command(String s, TreeNode node){
            this.s = s;
            this.node = node;
        }
    };

    public List<Integer> preorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<Command> stack = new Stack<Command>();
        stack.push(new Command("go", root));
        while(!stack.empty()){
            Command command = stack.pop();

            if(command.s.equals("print"))
                res.add(command.node.val);
            else{
                assert command.s.equals("go");
                if(command.node.right != null)
                    stack.push(new Command("go",command.node.right));
                if(command.node.left != null)
                    stack.push(new Command("go",command.node.left));
                stack.push(new Command("print", command.node));
            }
        }
        return res;
    }

}

public class Solution1 {

    public List<Integer> preorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        while(!stack.empty()){
            TreeNode curNode = stack.pop();
            res.add(curNode.val);

            if(curNode.right != null)
                stack.push(curNode.right);
            if(curNode.left != null)
                stack.push(curNode.left);
        }
        return res;
    }

}
LeetCode 94.二叉树中序遍历
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/// 94. Binary Tree Inorder Traversal
/// https://leetcode.com/problems/binary-tree-inorder-traversal/solution/
/// 非递归二叉树的中序遍历
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
public class Solution094 {

    // Definition for a binary tree node.
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    private class Command{
        String s;   // go, print
        TreeNode node;
        Command(String s, TreeNode node){
            this.s = s;
            this.node = node;
        }
    };

    public List<Integer> inorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<Command> stack = new Stack<Command>();
        stack.push(new Command("go", root));
        while(!stack.empty()){
            Command command = stack.pop();

            if(command.s.equals("print"))
                res.add(command.node.val);
            else{
                assert command.s.equals("go");
                if(command.node.right != null)
                    stack.push(new Command("go",command.node.right));
                stack.push(new Command("print", command.node));
                if(command.node.left != null)
                    stack.push(new Command("go",command.node.left));
            }
        }
        return res;
    }

}



/// Source : https://leetcode.com/problems/binary-tree-inorder-traversal/solution/
/// Author : liuyubobobo
/// Time   : 2018-05-30

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

// Classic Non-Recursive algorithm for inorder traversal
// Time Complexity: O(n), n is the node number in the tree
// Space Complexity: O(h), h is the height of the tree
public class Solution1 {

    public List<Integer> inorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.empty()){

            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }

            cur = stack.pop();
            res.add(cur.val);
            cur = cur.right;
        }
        return res;
    }
}
LeetCode 145.二叉树后序遍历
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/// 145. Binary Tree Postorder Traversal
/// https://leetcode.com/problems/binary-tree-postorder-traversal/description/
/// 非递归的二叉树的后序遍历
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
public class Solution145 {

    // Definition for a binary tree node.
    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
    }

    private class Command{
        String s;   // go, print
        TreeNode node;
        Command(String s, TreeNode node){
            this.s = s;
            this.node = node;
        }
    };

    public List<Integer> postorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<Command> stack = new Stack<Command>();
        stack.push(new Command("go", root));
        while(!stack.empty()){
            Command command = stack.pop();

            if(command.s.equals("print"))
                res.add(command.node.val);
            else{
                assert command.s.equals("go");
                stack.push(new Command("print", command.node));
                if(command.node.right != null)
                    stack.push(new Command("go",command.node.right));
                if(command.node.left != null)
                    stack.push(new Command("go",command.node.left));
            }
        }
        return res;
    }

}
/// Source : https://leetcode.com/problems/binary-tree-postorder-traversal/description/
/// Author : liuyubobobo
/// Time   : 2018-05-30

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

// Non-Recursive
// Using a tag to record whether the node has been visited
//
// Time Complexity: O(n), n is the node number in the tree
// Space Complexity: O(h), h is the height of the tree
public class Solution1 {

    private class TagNode{
        TreeNode node;
        boolean isFirst;
        TagNode(TreeNode node){
            this.node = node;
            this.isFirst = false;
        }
    };

    public List<Integer> postorderTraversal(TreeNode root) {

        ArrayList<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;

        Stack<TagNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.empty()){

            while(cur != null){
                stack.push(new TagNode(cur));
                cur = cur.left;
            }

            TagNode tagNode = stack.pop();
            cur = tagNode.node;
            if(tagNode.isFirst == false){
                tagNode.isFirst = true;
                stack.push(tagNode);
                cur = cur.right;
            }
            else{
                res.add(cur.val);
                cur = null;
            }
        }
        return res;
    }
}
  1. Flatten Nested List Iterator

二、队列

队列的基本应用

  • 广度优先遍历
  • 树;层序遍历
  • 图;无权图的最短路径

1、广度优先遍历

LeetCode 102. 二叉树层序遍历
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<>(){{add(root);}};
        ArrayList<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;

        while(!q.isEmpty()){
            ArrayList<Integer> tmp = new ArrayList<>();
            for(int i= q.size();i>0;i--){// 打印这一层
                TreeNode node = q.poll();
                tmp.add(node.val);
                if(node.left != null) q.add(node.left);
                if(node.right != null) q.add(node.right);
            }
            res.add(tmp);
        }
    return res;
    }
}

剑指offer层序遍历三连

  1. Binary Tree Right Side View

2、BFS和图的最短路径

LeetCode 279. 完全平方数

image.png

image.png

class Solution {
    public int numSquares(int n) {
        Queue<Pair<Integer,Integer>> q = new LinkedList<>();
        q.add(new Pair(n,0));
        boolean[] visited = new boolean[n + 1];
        visited[n] = true;
        while(!q.isEmpty()) {
            Pair<Integer,Integer> front = q.poll();
            int num = front.getKey();
            int step = front.getValue();

            if(num == 0) return step;

            for(int i = 0;;i++){
                int a = num - i * i;
                if(a < 0 ) break;
                if(!visited[a]) {
                    q.add(new Pair(a, step + 1));
                    visited[a] = true;
                }
            }
        }

        return 0;
    }
}

[源码](https://github.com/liuyubobobo/Play-with-Algorithm-Interview/tree/master/06-Stack-and-Queue/Course Code (Java)/05-Perfect-Squares/src)

拓展

  1. Word Ladder

  2. Word Ladder II

3、优先队列

底层实现——堆

对于堆的底层实现,白板编程

Queue<Integer> queue = new PriorityQueue<>((v1, v2) -> v2 - v1);// 大顶堆
Queue<Integer> queue = new PriorityQueue<>()// 小顶堆
LeetCode 347. 前 K 个高频元素

最简单的思路:扫描一-遍统计频率;排序找到前k个出现频率最高的元素。O(nlogn)

维护一个含有k个元素的优先队列。如果遍历到的元素比队列中的最小频率元素的频率高,则取出队列中最小频率的元素,将新元素入队。最终,队列中剩下的,就是前k个出现频率最高的元素。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int num:nums) {
            if(!map.containsKey(num)) map.put(num,1);
            else map.put(num,map.get(num) + 1);
        }
        // 优先队列的定义
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return map.get(a) - map.get(b);
            }
        });// 关键比较器

        for(Integer key : map.keySet()) {
            if(pq.size() == k) {
                if(map.get(pq.peek()) < map.get(key)) {
                    pq.poll();
                    pq.add(key);
                }
            }
            else pq.add(key);
        }
        int[] res = new int[k];
        int i = 0;
        for(Integer item:pq) {
            res[i++] = item;
        }
        return res;
    }
}

思路3:维护优先队列,时间复杂度: O(nlog(n-k))

LeetCode 23. Merge k Sorted Lists

4、单调队列

单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。
单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除。

剑指 Offer 59 - I. 滑动窗口的最大值

image.png

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];

        int[] res = new int[nums.length - k + 1];
        LinkedList<Integer> help = new LinkedList<>();
        // 形成窗口之前
        for(int i = 0;i < k;i++) {
            while(!help.isEmpty() && nums[i] > help.getLast()) help.removeLast();
            help.addLast(nums[i]);
        } 

        res[0] = help.getFirst();

        for(int i = k;i < nums.length;i++) {
            if(nums[i - k] == help.getFirst()) help.removeFirst();
            while(!help.isEmpty() && nums[i] > help.getLast()) help.removeLast();
            help.addLast(nums[i]);
            res[i - k  + 1] = help.getFirst();
        }

        return res;
    }
}
剑指 Offer 59 - II. 队列的最大值

image.png

class MaxQueue {
    Queue<Integer> q;
    LinkedList<Integer> help;

    public MaxQueue() {
        q = new LinkedList<>();
        help = new LinkedList<>();
    }
    
    public int max_value() {
        return q.size()==0?-1:help.getFirst();
    }
    
    public void push_back(int value) {  
        q.offer(value);
        while(!help.isEmpty() && help.getLast() < value) help.removeLast();
        help.add(value);
    }
    
    public int pop_front() {
        if(q.size() == 0) return -1;
        if((int)q.peek() == help.getFirst()) help.removeFirst();
        return q.poll();
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值