【算法面试题汇总】LeetBook列表的算法面试题汇总---堆、栈与队列题目及答案

如果有错的还请各位大佬指出呀
有些是copy的还望不要介意
本人只做学习记录

堆、栈与队列

      • 最小栈
      • 数组中的第k个最大元素
      • 数据流的中位数
      • 有序矩阵中第k小的元素
      • 前k个高频元素
      • 滑动窗口最大值
      • 基本计算器Ⅱ
      • 扁平化嵌套列表迭代器
      • 逆波兰表达式求值

最小栈

问题描述:

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。

示例:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
  • 代码实现
    时间O(1),空间O(n)
class MinStack {
    Deque<Integer> stack;
    Deque<Integer> minStack;

    public MinStack() {
        stack = new LinkedList<>();
        minStack = new LinkedList<>();
        minStack.push(Integer.MAX_VALUE);
    }
    
    public void push(int val) {
         stack.push(val);
         //也就是每次主栈存入一个数辅助栈都会存当前最小的数,保证两个栈长度相同
         minStack.push(Math.min(val,minStack.peek()));
    }
    
    public void pop() {
        //出栈时两个都出
        stack.pop();
        minStack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */
  • 辅助栈

    时间O(1),空间O(n)

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;

    public MinStack() {
        stack = new Stack<Integer>();
        minStack = new Stack<Integer>();
    }
    
    public void push(int val) {
        stack.push(val);
        if(minStack.isEmpty() || minStack.peek() >= val){
            minStack.push(val);
        }
    }
    
    public void pop() {
        if(stack.pop().equals(minStack.peek())){
            minStack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */
  • 用变量存储最小值
class MinStack {
    Stack<Integer> stack;
    int min = Integer.MAX_VALUE;

    public MinStack() {
        stack = new Stack<>();
    }
    
    public void push(int val) {
        if(val <= min){
            //将之前的min先入栈,也就是说之前的最小值和此时的最小值是在一起的
            stack.push(min);
            //再更新最小值
            min = val;
        }
        stack.push(val);
    }
    
    public void pop() {
        //如果出栈的值为最小值,先出栈该值
        if(stack.pop() == min){
            //并把下一个值也出栈,此时该值为最小值
            //也就是说出栈为最小值时记得把记录前一个最小值也出栈
            min = stack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return min;
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */
  • 用变量存储最小值(栈保存差值)
public class MinStack {
    //long类型是因为保存的是差值可能会溢出
    long min;
  Stack<Long> stack;

  public MinStack(){
        stack=new Stack<>();
    }

  public void push(int val) {
    if (stack.isEmpty()) {
      min = val;
      stack.push(val - min);
    } else {
            //如果该值小于最小值则保存的是负数
      stack.push(val - min);
      if (val < min){
        min = val; 
      }
        
    }
  }

  public void pop() {
    if (stack.isEmpty())
      return;

    long pop = stack.pop();
    
    //此时值是负数,说明之前这里更新了 min,之前的min=上一个min-此时的负值
    if (pop < 0) {
      min = min - pop;
    }
  }

  public int top() {
    long top = stack.peek();
    //负数的话,值保存在 min 中
    if (top < 0) {
      return (int) (min);
    } else {
            //栈顶值加上最小值即可
      return (int) (top + min);
    }
  }

  public int getMin() {
    return (int) min;
  }
}


数组中的第k个最大元素

题目描述:

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题

示例:

输入: [3,2,1,5,6,4], k = 2
输出: 5
  • 暴力
class Solution {
    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length-k];
    }
}
  • 快排

    时间O(n)空间O(1)

class Solution {
    private final static Random random = new Random();
    public int findKthLargest(int[] nums, int k) {
        int len = nums.length;
        int target = len - k;

        int left = 0;
        int right = len - 1;

        while(true){
            int pivotIndex = partition(nums,left,right);
            if(pivotIndex == target){
                //当此时的下标等于target便是找到此位置
                return nums[target];
            }else if(pivotIndex < target){
                left = pivotIndex + 1;
            }else if(pivotIndex > target){
                right = pivotIndex - 1;
            }
        }
    }

    private int partition(int[] nums,int left,int right){
        //选取随机数来测试防止极端情况
        int randomIndex = left + random.nextInt(right - left + 1);
        //应对极端情况,防止递归树加深,循环开始前调换第一个与后面随机的一个数的位置
        swap(nums,left,randomIndex);

        int pivot = nums[left];
        int le = left + 1;
        int ge = right;

        while(left >= right){
            while(le <= ge && nums[le] < pivot){
                le++;
            }
            while(le <= ge && nums[ge] > pivot){
                ge--;
            }
            if(le>ge){
                break;
            }
            swap(nums,le,ge);
            le++;
            ge--;
        }
        swap(nums,left,ge);
        return ge;
    }
    private void swap(int[] nums,int left,int right){
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}
  • 小根堆(优先队列)(最差)
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //优先队列默认是小根堆
        Queue<Integer> max = new PriorityQueue<>(k);
        for(int i=0;i<k;i++){
            max.add(nums[i]);
        }

        int len = nums.length;
        for(int i=k;i<len;i++){
            Integer top = max.peek();
            if(top<nums[i]){
                max.poll();
                max.add(nums[i]);
            }
        }
        return max.peek();
    }
}

数据流的中位数

题目描述:
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

示例:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2

进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?

  • 大根堆和小根堆
class MedianFinder {
    Queue<Integer> min,max;
    public MedianFinder() {
        //小根堆,存储较大的数
        min = new PriorityQueue<>();
        //大根堆,存储较小的数
        max = new PriorityQueue<>((x,y) -> (y-x));
    }
    
    public void addNum(int num) {
        //规定大根堆比小根堆的数量多1个
        if(min.size() == max.size()){
            //偶数时,先把数存入小根堆,再把小根堆中最小的元素存入大根堆
            min.add(num);
            max.add(min.poll());
        }else{
            //基数时,先存入大根堆,再把最大的元素存入小根堆
            max.add(num);
            min.add(max.poll());
        }
    }
    
    public double findMedian() {
        if(min.size() == max.size()){
            return (min.peek()+max.peek())/2.0;
        }else{
            return max.peek()*1.0;
        }
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

有序矩阵中第k小的元素

题目描述:
给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。
你必须找到一个内存复杂度优于 O(n2) 的解决方案

示例:

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

进阶:

你能否用一个恒定的内存(即 O(1) 内存复杂度)来解决这个问题?
你能在 O(n) 的时间复杂度下解决这个问题吗?这个方法对于面试来说可能太超前了,但是你会发现阅读这篇文章( this paper )很有趣。

  • 排序
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;

        int[] ans = new int[m*n];
        int index = 0;
        for(int[] temp : matrix){
            for(int num:temp){
                ans[index++] = num;
            }
        }
        Arrays.sort(ans);
        return ans[k-1];
    }
}
  • 二分查找(最优)
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;
        int min = matrix[0][0];
        int max = matrix[m-1][n-1];
        while(min<max){
            int mid = left + (right - left) / 2;
            //查找小于mid的数有多少个
            int count = find(matrix,mid,m,n);
            if(count<k){
                //数量小于k代表第k小的元素在右边
                min = mid + 1;
            }else{
                //数量大于等于k代表第k小的元素在左边
                max = mid;
            }
        }
        return max;
    }

    private int find(int[][] matrix,int mid,int m,int n){
        int i=m - 1;
        int j=0;
        int count = 0;
        while(i>=0 && j<n){
            if(matrix[i][j]<=mid){
                //该数比mid小,则该数所在列的前面几列都小
                count += i+1;
                j++;
            }else{
                i--;
            }
        }
        return count;
    }
}
  • 大根堆
class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        Queue<Integer> queue = new PriorityQueue<>((x,y) -> (y-x));

        for(int i=0;i<matrix.length;i++){
            for(int j=0;j<matrix[0].length;j++){
                queue.add(matrix[i][j]);
                if(queue.size() > k){
                    queue.poll();
                }
            }
        }
        return queue.peek();
    }
}

前k个高频元素

题目描述:
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

提示:
1 <= nums.length <= 105
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //统计出现次数
        Map<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }

        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>(){
            public int compare(int[] m,int[] n){
                //根据count来排序
                return m[1] - n[1];
            }
        });
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            int num = entry.getKey() , count = entry.getValue();
            if(queue.size() == k){
                //若堆顶小于新的值则替换
                if(queue.peek()[1] < count){
                    queue.poll();
                    queue.add(new int[]{num,count});
                }
            }else{
                queue.add(new int[]{num,count});
            }
        }
        int[] ans = new int[k];
        for(int i=0;i<k;i++){
            ans[i] = queue.poll()[0];
        }
        return ans;
    }
}
  • 快排
class Solution {
    private final static Random random = new Random(); 
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }

        List<int[]> list = new ArrayList<>();
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            int num = entry.getKey(),count = entry.getValue();
            list.add(new int[]{num,count});
        }
        int[] ans = new int[k];
        qsort(list,0,list.size()-1,ans,0,k);
        return ans;
    }

    private void qsort(List<int[]> list,int left,int right,int[] ans,int ansIndex,int k){
        //取随机数防止极端情况
        int randomIndex = left + random.nextInt(right-left+1);
        //应对极端情况,防止递归树加深,开始前调换第一个与后面随机的一个数的位置
        Collections.swap(list,left,randomIndex);

        int pivot = list.get(left)[1];
        int index = left;
        for(int i = left+1;i<=right;i++){
            if(list.get(i)[1] > pivot){
                Collections.swap(list,index+1,i);
                index++;
            }
        }
        Collections.swap(list,index,left);

        if(index-left >= k){
            qsort(list,left,index-1,ans,ansIndex,k);
        }else{
            for(int i=left;i<=index;i++){
                ans[ansIndex++] = list.get(i)[0];
            }
            if(k > index - left +1){
                qsort(list,index+1,right,ans,ansIndex,k- (index-left+1));
            }
        }

    }
}

滑动窗口最大值

题目描述:

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

示例:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

  • 队列
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length <= 1){
            return nums;
        }
        //用队列存放滑动窗口内可能为最大值的下标
        LinkedList<Integer> queue = new LinkedList<>();
        int[] ans = new int[nums.length-k+1];
        int index = 0;

        for(int i=0;i<nums.length;i++){
            //队尾元素小于此时的数则删除
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            queue.add(i);

            //判断此时队首元素是否还在滑动窗口内
            //若队尾下标-k=队首下标,则表示队首在滑动窗口外
            if(queue.peekLast() - k == queue.peek()){
                queue.poll();
            }
            //队首元素永远是最大的
            if(i+1 >= k){
                ans[index++] = nums[queue.peek()];
            }
        }
        return ans;

    }
}
  • 分块+预处理

    类似于稀疏表,稀疏表解决的是区间最值问题

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        int[] prefixMax = new int[n];
        int[] suffixMax = new int[n];
        for (int i = 0; i < n; ++i) {
            if (i % k == 0) {
                prefixMax[i] = nums[i];
            }
            else {
                prefixMax[i] = Math.max(prefixMax[i - 1], nums[i]);
            }
        }
        for (int i = n - 1; i >= 0; --i) {
            if (i == n - 1 || (i + 1) % k == 0) {
                suffixMax[i] = nums[i];
            } else {
                suffixMax[i] = Math.max(suffixMax[i + 1], nums[i]);
            }
        }

        int[] ans = new int[n - k + 1];
        for (int i = 0; i <= n - k; ++i) {
            ans[i] = Math.max(suffixMax[i], prefixMax[i + k - 1]);
        }
        return ans;
    }
}

基本计算器Ⅱ

题目描述:
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例:

输入:s = "3+2*2"
输出:7

输入:s = " 3/2 "
输出:1

提示:
1 <= s.length <= 3 * 105
s 由整数和算符 (‘+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数

  • 代码实现
class Solution {
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<>();
        //将符号先设为+,方便第一个数存储
        char symbol = '+';
        int num = 0;
        int n = s.length();

        for(int i=0;i<n;i++){
            //如果该元素是数字
            if(Character.isDigit(s.charAt(i))){
                //数字可能不止只有一位
                num = num * 10 + s.charAt(i) - '0';
            }
            //i==n-1,当他是最后一个元素时,进行这个数与它前面的运算符的运算
            if(!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n-1){
                switch(symbol){
                    case '+':
                        stack.push(num);
                        break;
                    case '-':
                        stack.push(-num);
                        break;
                    case '*':
                        //将结果存入
                        stack.push(stack.pop() * num);
                        break;
                    default:
                        stack.push(stack.pop() / num);
                }
                num = 0;
                symbol = s.charAt(i);
            }
        }
        int ans = 0;
        while(!stack.isEmpty()){
            ans += stack.pop();
        }
        return ans;
    }
}
  • 匹配+ -*/%^()
class Solution {
    //使用一个Map来维护运算符优先级
    Map<Character,Integer> map = new HashMap<>(){{
        put('+',1);
        put('-',1);
        put('*',2);
        put('/',2);
        put('%',2);
        put('^',3);
    }};

    public int calculate(String s) {
        //去除所有空格
        s = s.replaceAll(" ","");
        char[] cs = s.toCharArray();
        int n = s.length();
        //存放数字
        Deque<Integer> nums = new ArrayDeque();
        //为了防止第一个数是负数,将第一个数设置为0
        nums.addLast(0);
        //存放符号(也就是非数字操作)
        Deque<Character> ops = new ArrayDeque<>();
        for(int i=0;i<n;i++){
            char c = cs[i];
            if(c == '('){
                ops.addLast(c);
            }else if(c == ')'){
                //计算到最近的(左括号
                while(!ops.isEmpty()){
                    if(ops.peekLast() != '('){
                        calc(nums,ops);
                    }else{
                        ops.pollLast();
                        break;
                    }
                }
            }else{
                if(Character.isDigit(c)){
                    int num = 0;
                    int j=i;
                    //防止有些数不只有一位
                    while(j<n && Character.isDigit(cs[j])){
                        num = num * 10 + (cs[j++] - '0');
                    }
                    nums.addLast(num);
                    i = j-1;
                }else{
                    if(i>0 && (cs[i-1] == '(' || cs[i-1] == '+' || cs[i-1] == '-')){
                        nums.addLast(0);
                    }

                    //有新符号前先比较与前一个的优先级,先把前面能先计算的算
                    while(!ops.isEmpty() && ops.peekLast() != '('){
                        char pre = ops.peekLast();
                        if(map.get(pre) >= map.get(c)){
                            calc(nums,ops);
                        }else{
                            break;
                        }
                    }
                    ops.addLast(c);
                }
            }
        }
        //将剩余的算完
        while(!ops.isEmpty()){
            calc(nums,ops);
        }
        return nums.peekLast();
    }

    private void calc(Deque<Integer> nums,Deque<Character> ops){
        if(nums.isEmpty() || nums.size()<2)return;
        if(ops.isEmpty()) return;
        int b = nums.pollLast(),a = nums.pollLast();
        char op = ops.pollLast();
        int ans = 0;
        if(op == '+') ans = a+b;
        else if(op == '-') ans = a-b;
        else if(op == '*') ans = a*b;
        else if(op == '/') ans = a/b;
        else if(op == '%') ans = a%b;
        else if(op == '^') ans = (int)Math.pow(a,b);
        nums.addLast(ans);
    }
}

扁平化嵌套列表迭代器

题目描述:
给你一个嵌套的整数列表 nestedList 。每个元素要么是一个整数,要么是一个列表;该列表的元素也可能是整数或者是其他列表。请你实现一个迭代器将其扁平化,使之能够遍历这个列表中的所有整数。

实现扁平迭代器类 NestedIterator :
NestedIterator(List nestedList) 用嵌套列表 nestedList 初始化迭代器。
int next() 返回嵌套列表的下一个整数。
boolean hasNext() 如果仍然存在待迭代的整数,返回 true ;否则,返回 false 。

你的代码将会用下述伪代码检测:

initialize iterator with nestedList
res = []
while iterator.hasNext()
    append iterator.next() to the end of res
return res

如果 res 与预期的扁平化列表匹配,那么你的代码将会被判为正确。

示例:

输入:nestedList = [[1,1],2,[1,1]]
输出:[1,1,2,1,1]
解释:通过重复调用 next 直到 hasNext 返回 false,next 返回的元素的顺序应该是: [1,1,2,1,1]。
  • 深度优先搜索+ 数组

    时间O(n)空间O(n)

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * public interface NestedInteger {
 *
 *     // @return true if this NestedInteger holds a single integer, rather than a nested list.
 *     public boolean isInteger();
 *  
 *     // @return the single integer that this NestedInteger holds, if it holds a single integer
 *     // Return null if this NestedInteger holds a nested list
 *     public Integer getInteger();
 *
 *     // @return the nested list that this NestedInteger holds, if it holds a nested list
 *     // Return empty list if this NestedInteger holds a single integer
 *     public List getList();
 * }
 */
public class NestedIterator implements Iterator<Integer> {
    private List<Integer> vals;
    private Iterator<Integer> cur;

    public NestedIterator(List<NestedInteger> nestedList) {
        vals = new ArrayList<>();
        dfs(nestedList);
        cur = vals.iterator();
    }

    @Override
    public Integer next() {
        return cur.next();
    }

    @Override
    public boolean hasNext() {
        return cur.hasNext();
    }

    private void dfs(List<NestedInteger> nestedList){
        for(NestedInteger ni:nestedList){
            if(ni.isInteger()){
                vals.add(ni.getInteger());
            }else{
                dfs(ni.getList());
            }
        }
    }
}

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i = new NestedIterator(nestedList);
 * while (i.hasNext()) v[f()] = i.next();
 */
  • 深度优先搜索+队列

    时间O(n)空间O(n)

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * public interface NestedInteger {
 *
 *     // @return true if this NestedInteger holds a single integer, rather than a nested list.
 *     public boolean isInteger();
 *
 *     // @return the single integer that this NestedInteger holds, if it holds a single integer
 *     // Return null if this NestedInteger holds a nested list
 *     public Integer getInteger();
 *
 *     // @return the nested list that this NestedInteger holds, if it holds a nested list
 *     // Return empty list if this NestedInteger holds a single integer
 *     public List getList();
 * }
 */
public class NestedIterator implements Iterator<Integer> {
    Deque<Integer> queue = new ArrayDeque<>();
    public NestedIterator(List<NestedInteger> nestedList) {
        //初始化时便处理元素
        dfs(nestedList);
    }

    @Override
    public Integer next() {
        //保证调用next前调用hasNext
        return hasNext() ? queue.pollFirst() : -1;
    }

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

    private void dfs(List<NestedInteger> nestedList){
        for(NestedInteger ni:nestedList){
            if(ni.isInteger()){
                queue.addLast(ni.getInteger());
            }else{
                dfs(ni.getList());
            }
        }
    }
}

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i = new NestedIterator(nestedList);
 * while (i.hasNext()) v[f()] = i.next();
 */
/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * public interface NestedInteger {
 *
 *     // @return true if this NestedInteger holds a single integer, rather than a nested list.
 *     public boolean isInteger();
 *
 *     // @return the single integer that this NestedInteger holds, if it holds a single integer
 *     // Return null if this NestedInteger holds a nested list
 *     public Integer getInteger();
 *
 *     // @return the nested list that this NestedInteger holds, if it holds a nested list
 *     // Return empty list if this NestedInteger holds a single integer
 *     public List getList();
 * }
 */
public class NestedIterator implements Iterator<Integer> {
    Deque<NestedInteger> stack = new LinkedList<>(); 
    public NestedIterator(List<NestedInteger> nestedList) {
        for(int i=nestedList.size()-1;i>=0;i--){
            NestedInteger ni = nestedList.get(i);
            stack.push(ni);
        }
    }

    @Override
    public Integer next() {
        return hasNext() ? stack.pop().getInteger() : -1;
    }

    @Override
    public boolean hasNext() {
        if(stack.isEmpty()){
            return false;
        }else{
            NestedInteger ni = stack.peek();
            if(ni.isInteger()){
                return true;
            }else{
                ni = stack.pop();
                List<NestedInteger> list = ni.getList();
                for(int i = list.size()-1;i>=0;i--){
                    stack.push(list.get(i));
                }
                return hasNext();
            }
        }
    }
}

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i = new NestedIterator(nestedList);
 * while (i.hasNext()) v[f()] = i.next();
 */

逆波兰表达式求值

题目描述:
根据逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

提示:
1 <= tokens.length <= 104
tokens[i] 是一个算符(“+”、“-”、“*” 或 “/”),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。

逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

  • 时间O(n)空间O(n)

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList<>();
        int n = tokens.length;
        for(int i=0;i<n;i++){
            String token = tokens[i];
            if(isNumber(token)){
                stack.push(Integer.parseInt(token));
            }else{
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch(token){
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);
                        break;
                    case "*":
                        stack.push(num1 * num2);
                        break;
                    default:
                        stack.push(num1 / num2);
                }
            }
        }
        return stack.pop();
    }
    
    private boolean isNumber(String token){
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}
  • 栈的另一个写法,快2ms
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();
    }
}

作者:carlsun-2
链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/solution/by-carlsun-2-a0vh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 数组模拟栈

    对于一个有效的逆波兰表达式,其长度 n 一定是奇数,且操作数的个数一定比运算符的个数多 11 个,即包含(n+1)/2个操作数和(n-1)/2个运算符

    遇到操作数时,栈内元素增加 11 个;遇到运算符时,栈内元素减少 11 个

    最坏情况下,(n+1)/2个操作数都在表达式的前面,(n-1)/2个运算符都在表达式的后面,此时栈内元素最多为(n+1)/2个

    时间O(n)空间O(n)

class Solution {
    public int evalRPN(String[] tokens) {
        int n = tokens.length;
        int[] stack = new int[(n+1)/2];
        int index = -1;
        for(int i=0;i<n;i++){
            String token = tokens[i];
            switch(token){
                case "+":
                    index--;
                    stack[index] += stack[index + 1];
                    break;
                case "-":
                    index--;
                    stack[index] -= stack[index + 1];
                    break;
                case "*":
                    index--;
                    stack[index] *= stack[index + 1];
                    break;
                case "/":
                    index--;
                    stack[index] /= stack[index + 1];
                    break;
                default:
                    index++;
                    stack[index] = Integer.parseInt(token);
                
            }
        }
        return stack[index];
    }
}

你可能感兴趣的:(数据结构与算法,算法,java,开发语言,深度优先)