【Java】几道常见的秋招面试题 (3)

我现在需要实现一个栈,这个栈除了可以进行普通的push、pop操作以外,还可以进行getMin的操作,getMin方法被调用后,会返回当前栈的最小值,你会怎么做呢?你可以假设栈里面存的都是int整数

解决方案:

使用一个min变量来记住最小值,每次push的时候,看看是否需要更新min。

如果被pop出去的是min,第二次pop的时候,只能遍历一下栈内元素,重新找到最小值。

总结:pop的时间复杂度是O(n),push是O(1),空间是O(1)

使用辅助栈来存储最小值。如果当前要push的值比辅助栈的min值要小,那在辅助栈push的值是最小值

总结:push和pop的时间复杂度都是O(1),空间是O(n)。典型以空间换时间的例子。

import java.util.ArrayList; import java.util.List; public class MinStack { private List<Integer> data = new ArrayList<Integer>(); private List<Integer> mins = new ArrayList<Integer>(); public void push(int num) { data.add(num); if (mins.size() == 0) { // 初始化mins mins.add(num); } else { // 辅助栈mins每次push当时最小值 int min = getMin(); if (num >= min) { mins.add(min); } else { mins.add(num); } } } public int pop() { // 栈空,异常,返回-1 if (data.size() == 0) { return -1; } // pop时两栈同步pop mins.remove(mins.size() - 1); return data.remove(data.size() - 1); } public int getMin() { // 栈空,异常,返回-1 if (mins.size() == 0) { return -1; } // 返回mins栈顶元素 return mins.get(mins.size() - 1); } }

继续优化:

栈为空的时候,返回-1很可能会带来歧义(万一人家push进去的值就有-1呢?),这边我们可以使用Java Exception来进行优化

算法的空间优化:上面的代码我们可以发现:data栈和mins栈的元素个数总是相等的,mins栈中存储几乎都是最小的值(此部分是重复的!)

所以我们可以这样做:当push的时候,如果比min栈的值要小的,才放进mins栈。同理,当pop的时候,如果pop的值是mins的最小值,mins才出栈,否则mins不出栈!

上述做法可以一定避免mins辅助栈有相同的元素!

但是,如果一直push的值是最小值,那我们的mins辅助栈还是会有大量的重复元素,此时我们可以使用索引(mins辅助栈存储的是最小值索引,非具体的值)!

最终代码:

import java.util.ArrayList; import java.util.List; public class MinStack { private List<Integer> data = new ArrayList<Integer>(); private List<Integer> mins = new ArrayList<Integer>(); public void push(int num) throws Exception { data.add(num); if(mins.size() == 0) { // 初始化mins mins.add(0); } else { // 辅助栈mins push最小值的索引 int min = getMin(); if (num < min) { mins.add(data.size() - 1); } } } public int pop() throws Exception { // 栈空,抛出异常 if(data.size() == 0) { throw new Exception("栈为空"); } // pop时先获取索引 int popIndex = data.size() - 1; // 获取mins栈顶元素,它是最小值索引 int minIndex = mins.get(mins.size() - 1); // 如果pop出去的索引就是最小值索引,mins才出栈 if(popIndex == minIndex) { mins.remove(mins.size() - 1); } return data.remove(data.size() - 1); } public int getMin() throws Exception { // 栈空,抛出异常 if(data.size() == 0) { throw new Exception("栈为空"); } // 获取mins栈顶元素,它是最小值索引 int minIndex = mins.get(mins.size() - 1); return data.get(minIndex); } }

参考资料:

作者:channingbreeze 出处: 互联网侦察

五、多线程下的HashMap

众所周知,HashMap不是一个线程安全的类。但有可能在面试的时候会被问到:如果在多线程环境下使用HashMap会有什么现象发生呢??

结论:

put()的时候导致的多线程数据不一致(丢失数据)

resize()操作会导致环形链表

jdk1.8已解决环链的问题(声明两对指针,维护两个连链表)

fail-fast机制,对当前HashMap同时进行删除/修改会抛出ConcurrentModificationException异常

参考资料:

谈谈HashMap线程不安全的体现:

jdk1.8 hashmap多线程put不会造成死循环:https://blog.csdn.net/qq_27007251/article/details/71403647

六、Spring和Springboot区别

一、SpringBoot是能够创建出独立的Spring应用程序的

二、简化Spring配置

Spring由于其繁琐的配置,一度被人成为“配置地狱”,各种XML、Annotation配置,让人眼花缭乱,而且如果出错了也很难找出原因。

Spring Boot项目就是为了解决配置繁琐的问题,最大化的实现convention over configuration(约定大于配置)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywpgs.html