草庐IT

代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素

nnn~ 2023-03-28 原文

第一题150. 逆波兰表达式求值

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

ψ(`∇´)ψ 我的思路

  • 题目上提示的已经很清晰了

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

package stackandqueue;

import java.util.Stack;

public class EvalRPN {

    public int evalRPN(String[] tokens) {
        int m,n;
        Stack<String> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            //如果是运算符的话,取出栈顶的两个元素,和运算符计算,结果入栈
            if(tokens[i].equals("+")){
                stack.push(String.valueOf(Integer.parseInt(stack.pop())+Integer.parseInt(stack.pop())));
            } else if (tokens[i].equals("-")) {
                m = Integer.parseInt(stack.pop());
                n = Integer.parseInt(stack.pop());
                stack.push(String.valueOf(n-m));
            } else if (tokens[i].equals("*")) {
                stack.push(String.valueOf(Integer.parseInt(stack.pop())*Integer.parseInt(stack.pop())));
            }else if (tokens[i].equals("/")) {
                m = Integer.parseInt(stack.pop());
                n = Integer.parseInt(stack.pop());
                stack.push(String.valueOf(n/m));
            } else {
                stack.push(tokens[i]);//如果不是运算符的话,入栈
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

时间复杂度O(n)

  • 在力扣上debug也太方便了吧?



第二题239. 滑动窗口最大值

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

ψ(`∇´)ψ 我的思路

  • 暴力暴力!一个for循环确定滑动窗口的位置,另一个for循环确定滑动窗口内的最大值
package stackandqueue;

public class MaxSlidingWindow {

    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length==1){
            return nums;
        }
        int[] res = new int[nums.length - k + 1];//结果数组
        for (int i = 0; i < nums.length - k + 1; i++) {
            // 确定滑动窗口的位置[i,i+k-1]
            int x = Integer.MIN_VALUE;
            for (int j = i; j < i + k; j++) {
                // 遍历滑动窗口内的值,寻找最大值
                x = nums[j] > x ? nums[j] : x;
            }
            res[i] = x;
        }
        return res;
    }
}

时间复杂度O(n^2)

  • 然后就。。。超时了?

  • 就很乖的去看了正经解法?

package stackandqueue;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;

public class MaxSlidingWindow2 {

    static  Deque<Integer> que = new LinkedList<>();
    public static int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length - k + 1];
        for (int i = 0; i < k; i++) {
            push(nums[i]);
        }
        res[0] = getMax();
        for (int i = 1; i < nums.length - k + 1; i++) {
            pop(nums[i-1]);//弹出元素
            push(nums[i + k-1]);//加入元素
            res[i] = getMax();//取最大值
        }
        return res;
    }
    public static void pop(int x) {
        if (!que.isEmpty() && que.peek() == x) {
            //如果队列不为空,并且队列出口的值=要弹出的值
            que.pop();
        }
    }
    public static void push(int x) {
       while (!que.isEmpty()&&x>que.getLast()){
           que.removeLast();//当队列不空且要加入的元素大于队列最后一个元素时,把最后的元素去掉(保证队列倒序)
        }
        que.addLast(x);//在把元素追加至队尾

    }
    public static int getMax() {
        // 最大值一直是队列出口的元素
        return (int) que.getFirst();
    }

    public static void main(String[] args) {
        int[] ints = maxSlidingWindow(new int[]{1,3,1,2,0,5}, 3);
        for (int i = 0; i < ints.length; i++) {
            System.out.println(ints[i]);
        }
    }
}

时间复杂度O(n)

  • 尽管听了思路,实现起来还是有点复杂的,关键是push的时候要从队尾把元素移除,再加入元素,这样可以保证队列里的元素倒序。




第三题347.前 K 个高频元素

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

ψ(`∇´)ψ 我的思路

  • 这题我想着是用map,出现的次数为key,出现的元素为value,for循环一次遍历将数组存入map,对key排序,取前k个返回其value。今天不太舒服就不写了,直接看代码随想录的新方法。
package stackandqueue;

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class TopKFrequent {

    public int[] topKFrequent(int[] nums, int k) {

        // 先把数组中的元素装入map
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])) {
                map.put(nums[i], 1);
            } else {
                map.put(nums[i],map.get(nums[i])+1);
            }
        }
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair1[1]-pair2[1]);
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆只需要维持k个元素有序
            if(pq.size()<k){//小顶堆元素个数小于k个时直接加
                pq.add(new int[]{entry.getKey(),entry.getValue()});
            }else{
                if(entry.getValue()>pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                    pq.poll();//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
                    pq.add(new int[]{entry.getKey(),entry.getValue()});
                }
            }
        }
        int[] ans = new int[k];
        for(int i=k-1;i>=0;i--){//依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
            ans[i] = pq.poll()[0];
        }
        return ans;
    }
}
  • 思路听了下,但并不是很理解PriorityQueue这个东西,感觉我对队列的理解还是差点儿



总结


  • 基本可以用栈来解题,但是队列我还是没有完全弄明白,大顶堆小顶堆也只是懂一点儿,先欠下来,等6号,7号的时候再做个总结。

  • 今天终于做到了一道困难题,还是很开心的,期待二叉树

补充

队列

  • 实现类挺多的,一般用LinkedList

  • 堆是一个完全二叉树
  • 大顶堆:头部为堆中最大的值
  • 小顶堆:头部为队中最小的值
  • PriorityQueue:一个具有优先级的队列,该优先级使得队列始终按照自然顺序进行排序,队列的头部为最小值。

构造小顶堆:
PriorityQueue small=new PriorityQueue<>();

构造大顶堆:
PriorityQueue small=new PriorityQueue<>(Collections.reverseOrder());

有关代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  4. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  8. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  9. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  10. ruby - 这两段代码有什么区别? - 2

    打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性

随机推荐