ARTS习惯(3)

米罗说

  • 做难事必有所得

Algorithm

每周至少做一个Leetcode算法题

【题目来源】

左程云《程序员代码面试指南:IT名企算法与数据结构题目最优解(第2版)》单调栈结构

【题目】

给定一个不含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比arr[i]小的位置。返回所有位置相应的信息。

【举例】

输入:{3,4,1,5,6,2,7}

返回:{

{-1,2}
{0,2}
{-1,-1}
{2,5}
{3,5}
{2,-1}

}

注:-1表示不存在。

【要求】

如果arr的长度为N,解法的时间复杂度为O(N)

【解答】

单调栈是面试中常问的一种数据结构,读者需要多做练习掌握这个题型。

我们先创建1个Stack<Integer>用来存放数组的下标,开始时Stack为空。如果找到i位置左边或者右边离i最近且值小于arr[i]的元素,那么栈从栈顶到栈底元素在数组中的值必须严格递减

遍历arr,将下标放入Stack栈,检查是否满足严格递减原则,假设遍历到x下标时破坏了严格递减原则,那么x下面就是x左边最近的小于arr[x]的值a,当前值就是x右边最近的小于arr[x]的值b。下面证明上述的推理的正确性。

i位置(1)---------x位置(5)---------j位置(4)

假设x位置是被弹出的元素,j位置为当前值,i位置为x位置下面的元素。用假设法来证明:

1)假设(x,j)之间存在值小于5的数,那么轮不到j将x弹出栈,存在的这个数就将x弹出栈了,所以(x,j)之间要么没有数,要么值比5大,所以j位置是x右侧距离x最近,且值小于5的位置

2)假设(i,x)之间存在值小于1的数,那么该数会把i位置弹出,与题目假设矛盾;假设存在大于1且小于5的位置,那么二者不会相邻,与题目假设矛盾;假设存在大于5的位置,那么5的出现将会把该位置弹出,从而i和x位置相邻,中间没有其他数。所以说,i=x-1位置是x位置左边距离x最近的且值小于5的位置。

综上证毕。

【参考代码】

 public static int[][] getNearAndLessNoRepeat(int[] arr){
        Stack<Integer> stack = new Stack<Integer>();
        int[][] res = new int[arr.length][2];
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
                index = stack.pop();
                int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
                res[index][0] = leftLessIndex;
                res[index][1] = i;
            }
            stack.push(i);
        }
        // 弹出剩余的栈元素,这些元素没有右边最近小于位置
        while (!stack.isEmpty()) {
            index = stack.pop();
            int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
            res[index][0] = leftLessIndex;
            res[index][1] = -1;
        }
        return res;
    }

【思考讨论】

  • 若数组arr中含有重复值的元素,算法该怎么写
  • 用单调栈结构实现:返回i位置左右最近大于arr[i]的集合

Review

阅读并点评至少1篇英文技术文章

【原文】:Head First Java(2nd Editon)CH9 Life and Death of an Object

【译文】:英文版原汁原味,语义准确,本书的写作风格幽默,容易理解。读者应重点关注作者是如何一步一步带你带你分析问题的,不必纠结细枝末节的东西。读者也可移步Head First Java(第二版·中文版)

【点评】:

  • 位置

    • 栈:方法调用和局部变量存放的地方

    • 堆:所有的对象呆的地方,实例变量待在对象里

    • Methods are stacked:调用的method1方法被压入栈,method1方法中调用method2方法,继续将method2压入栈。栈顶的方法总是当前执行的方法,method2方法执行完后,弹出,继续执行method1方法。

    • 非基本类型变量持有对象的引用,待在栈,真正的对象待在堆

  • 创建对象

    • 通过new ClassName调用构造器来实现,构造器内部初始化重要的状态,构造器可以overloaded(重载),构造器和方法有两点区别:1)构造器没有返回值;2)构造器的名字必须和类名一样
    • super()只能用在构造器里,并且只能是构造器的第一行代码。this()和super()一样,因此二者不能共存,因为他们必须都要是构造器的第一行代码。
    • Constructor Chaining:subclass extends superclass ,那么创建子类对象的同时,根据构造器的连锁效应,父类的构造器也会自动调用,执行过程满足栈的规律,父类构造器先完成,子类构造器后完成。
  • 回收对象

    • 对象的回收取决于它的引用变量,当引用变量失效时,对象就没有可以被回收了。

    • 下面3中情形,对象满足被GC的条件

      • 引用永久的离开作用域了

        // 方法结束时,d失效,Life对象失去了引用,
        void go() {
            Life d = new Life();
        }
        
      • 引用指向了一个新的对象

        Life d = new Life();
        d = new Life();	// d指向了新的对象,原来的对象失去了指向,不可达
        
      • 引用被赋值为null

        Life d = new Life();
        d = null; //显示的赋值为null,原来的对象失去了指向,不可达
        

Tip

学习至少一个技术技巧

SpringBoot的自动装配原理:从以下2个角度来认识自动装配

  • 加载AutoConfiguration类

    Spring启动依靠main方法启动的,会调用SpringApplication.run()方法,执行run()方法时会有个刷新容器的过程,通过解析注解和解析属性配置文件(application.yml)的方式,来把bean注入到容器里边。解析@SpringBootApplication注解的时候,@SpringBootApplication = @SpringBootConfiguration +@ComponentScan+@EnableAutoConfiguration,分别起到配置、组件扫描、开启自动配置的作用,@EnableAutoConfiguration注解是核心注解,会开启自动装配,这个注解会@Import()进来一个AutoConfigurationSelector核心的类,这个selector类里有个selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描的META-INF/spring.factories文件,加载key为EnableAutoConfiguration的好多的AutoConfiguration的类,这些自动配置类里有条件注解,他会根据我们引入的jar包和容器中的bean自动的把bean注入到容器里边,于是实现了自动装配。

  • 参数绑定

    AutoConfiguration类通过Properties结尾的类来从属性配置文件(application.yml)中获取类似于server.port的信息,是通过@ConfigurationProperties注解来实现属性绑定的

Share

分享一篇有观点和思考的技术文章

推荐《修改代码的艺术》(美)Michael C. Feathers,一部小块头的著作,需要好好啃才能体会到作者的超强洞察力和功夫。

这里放上电子版,试看之后觉得对你帮助很大,还是支持版权,买上一本书吧!

链接:https://pan.baidu.com/s/1piclbVvEu9kGRQK8KQ-JIQ
提取码:nzdw

posted @ 2020-12-08 18:11  米罗{mirror}  阅读(103)  评论(0编辑  收藏  举报