算法学习(六)栈和队列

栈的基础使用

leetcode20. 有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。
    输入: "()" --- 输出: true
    输入: "()[]{}"---输出: true
    输入: "(]"---输出: false
    利用栈,遇到左括号加入栈,遇到右括号就看是否和栈顶元素匹配,如果匹配将栈顶元素出栈,如果不匹配返回false
import java.util.Stack;

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        char[] str = s.toCharArray();
        for (int i = 0; i < str.length; i++) {
            if (str[i] == '(' || str[i] == '{' || str[i] == '[') {
                stack.push(str[i]);
            } else {
                if (stack.size() == 0) {
                    return false;
                } else {
                    char c = stack.peek();
                    char match = ' ';
                    if (str[i] == ')') {
                        match = ')';
                    } else if (str[i] == ']') {
                        match = ']';
                    } else if (str[i] == '}') {
                        match = '}';
                    }
                    if (c != match) {
                        return false;
                    }
                }
            }
        }
        if (stack.size() != 0) {
            return false;
        } else {
            return true;
        }
    }
}

相关问题150,71

150. 逆波兰表达式求值

class Solution {
    // 当遇到运算符"+"、"-"、"*"、"/"时,从栈中pop出两个数字计算,否则将数字入栈;
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack();
        for (String s : tokens) {
            if (s.equals("+")) {
                stack.push(stack.pop() + stack.pop());
            } else if (s.equals("-")) {
                stack.push(-stack.pop() + stack.pop());
            } else if (s.equals("*")) {
                stack.push(stack.pop() * stack.pop());
            } else if (s.equals("/")) {
                int num1 = stack.pop();
                stack.push(stack.pop() / num1);
            } else {
                stack.push(Integer.valueOf(s));
            }
        }

        return stack.pop();
    }
}

栈与递归的联系

比如遍历二叉树

 

 

当递归调用方法时,此时要向栈里推入一些内容,如上一个函数具体执行到了哪里,函数返回的时候取出栈顶元素就知道之后要执行的

leetcode144. 二叉树的前序遍历

  • 下面使用非递归方式
/**
 * Definition for a binary tree node. public class TreeNode { int val; TreeNode
 * left; TreeNode right; TreeNode(int x) { val = x; } }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        LinkedList<TreeNode> stack = new LinkedList<>();
        LinkedList<Integer> output = new LinkedList<>();
        if (root == null) {
            return output;
        }

        stack.add(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pollLast();
            output.add(node.val);
            if (node.right != null) {
                stack.add(node.right);
            }
            if (node.left != null) {
                stack.add(node.left);
            }
        }
        return output;
    }
}

相关问题341

341. 扁平化嵌套列表迭代器

public class NestedIterator implements Iterator<Integer> {
    Queue<Integer> queue = new LinkedList<>();
    public NestedIterator(List<NestedInteger> nestedList) {
        getInteger(nestedList);
    }

    private void getInteger(List<NestedInteger> nestedList) {
        for(NestedInteger nest: nestedList){
            if(nest.isInteger()){
                queue.offer(nest.getInteger());
            }else{
                getInteger(nest.getList());
            }
        }
    }
    @Override
    public Integer next() {
        return queue.poll();
    }

    @Override
    public boolean hasNext() {
        return !queue.isEmpty();
    }
}

队列

队列的基本应用-广度优先遍历

  • 树;层序遍历
  • 图;无权图的最短路径

leetcode102. 二叉树的层次遍历

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
      List<List<Integer>> levels = new ArrayList<List<Integer>>();
      if (root == null) return levels;
  
      Queue<TreeNode> queue = new LinkedList<TreeNode>();
      queue.add(root);
      int level = 0;
      while ( !queue.isEmpty() ) {
        // start the current level
        levels.add(new ArrayList<Integer>());
  
        // number of elements in the current level
        int level_length = queue.size();
        for(int i = 0; i < level_length; ++i) {
          TreeNode node = queue.remove();
  
          // fulfill the current level
          levels.get(level).add(node.val);
  
          // add child nodes of the current level
          // in the queue for the next level
          if (node.left != null) queue.add(node.left);
          if (node.right != null) queue.add(node.right);
        }
        // go to next level
        level++;
      }
      return levels;
    }
  }

相关问题,107,103,199

107. 二叉树的层次遍历 II

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        DFS(root, 0, res);
        return res;
    }

    private void DFS(TreeNode root, int level, List<List<Integer>> res) {
        if (root == null) {
            return;
        }
        if (res.size() <= level) {
            res.add(0, new ArrayList<>());
        }
        res.get(res.size() - 1 - level).add(root.val);
        DFS(root.left, level + 1, res);
        DFS(root.right, level + 1, res);
    }
}

103. 二叉树的锯齿形层次遍历

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        DFS(root, 0, res);
        return res;
    }

    // 使用dfs,对应层判断一下奇偶,决定在表头还是表尾添加元素
    private void DFS(TreeNode root, int depth, List<List<Integer>> res) {
        if (root == null) {
            return;
        }

        if (res.size() == depth) {
            res.add(new ArrayList<Integer>());
        }

        if (depth % 2 == 0) {
            res.get(depth).add(root.val);
        } else {
            res.get(depth).add(0, root.val);
        }
        
        DFS(root.left, depth+1, res);
        DFS(root.right, depth + 1, res);
    }
}

199. 二叉树的右视图

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        if (root == null) {
            return Collections.emptyList();
        }
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        List<Integer> list = new ArrayList<>();
        TreeNode node;
        while (queue.size() > 0) {
            int size = queue.size();
            while (size-- > 0) {
                node = queue.remove();
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
                if (size == 0) {
                    list.add(node.val);
                }
            }
        }
        return list;
        
    }
}

BFS和图的最短路径

leetcode279. 完全平方数

给定正整数 n,找到若干个完全平方数(完全平方数就是一个整数是平方,比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
对于任何一个正整数,他一定能表示为n个完全平方数的和(因为1是一个完全平方数,最少可以表示为n个1相加)

  • 贪心是想法:看能先填入一个最大完全平方数,之后再看剩下的数字还能不能填下其他的完全平方数
    但是这个问题贪心算法不成立,比如12=9+1+1+1,但是最少的组合是12=4+4+4
  • 对问题建模:
    整个问题转化为一个图论问题,从n到0,每个数字表示一个节点,如果两个数字x到y相差一个完全平方数,则连接一条边,得到了一个无权图,问题转化成求这个无权图中从n到0的最短路径

     

     

class Solution {
    static class Node {
        int val;//表示数字是多少
        int step;//已经走了几步
    
        public Node(int val, int step) {
            this.val = val;
            this.step = step;
        }
    }

    // 该算法在往队列里面添加节点的时候会 add 很多重复的节点,导致超时
    /*public int numSquares(int n) {
        Queue<Node> queue = new LinkedList<>();
        queue.add(new Node(n, 0));
        //广度优先遍历
        while (!queue.isEmpty()) {
            int num = queue.peek().val;//取出队首元素
            int step = queue.peek().step;//已经走了几步
            queue.remove();//出队
            if(num==0){
                return step;
            }
            for(int i=1;num-i*i>=0;i++){
                queue.add(new Node(num-i*i, step + 1));
            }
        }
        return -1;
    }*/

    // 优化办法是,加入 visited 数组,检查要 add 的数据是否已经出现过了,防止数据重复出现,从而影响图的遍历
    // 同时优化:num - i * i 表达式,只让他计算一次
    // 同时在循环体里面判断退出或返回的条件,而不是在循环体外
    public int numSquares(int n) {
        Queue<Node> queue = new LinkedList<>();
        queue.add(new Node(n, 0));
        // 其实一个真正的图的 BSF 是一定会加上 visited 数组来过滤元素的
        boolean[] visited = new boolean[n+1];
        while (!queue.isEmpty()) {
            int num = queue.peek().val;
            int step = queue.peek().step;
            queue.remove();

            for (int i = 1; ; i++) {
                int a = num - i * i;
                if (a < 0) {
                    break;
                }
                // 若 a 已经计算到 0 了,就不必再往下执行了
                if (a == 0) {
                    return step + 1;
                }
                if (!visited[a]) {//还没有访问过
                    queue.add(new Node(a, step + 1));
                    visited[a] = true;
                }
            }
        }
        return -1;
    }
}

相关问题127,126

优先队列

底层实现,堆

leetcode347. 前 K 个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

  • 最简单的思路:扫描一遍,统计频率,排序找到前k个出现频率最高的元素。O(nlogn)
  • 维护一个含有k个元素的优先队列,如果遍历到的元素比队列中的最小频率元素的频率高,则取出队列中最小频率的元素,将新元素入队,最终队列剩下的就是前k个出现频率最高的元素,O(nlogk)
class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
        //统计每个元素出现的频率
        HashMap<Integer, Integer> count = new HashMap();
        for (int n: nums) {
        count.put(n, count.getOrDefault(n, 0) + 1);
        }
        //遍历map,用最小堆维护当前出现频率最高的k个元素
        PriorityQueue<Integer> heap =
                new PriorityQueue<Integer>((n1, n2) -> count.get(n1) - count.get(n2));
    
        for (int n: count.keySet()) {
        heap.add(n);
        if (heap.size() > k)
            heap.poll();
        }
    
        
        List<Integer> top_k = new LinkedList();
        while (!heap.isEmpty())
        top_k.add(heap.poll());
        Collections.reverse(top_k);
        return top_k;
    }
}

相关问题23,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值