题目很简单,实现一个最小栈,能够以线形的时间获取栈中元素的最小值

自己的思路如下:

利用数组,以及两个变量, last用于记录栈顶元素的位置,min用于记录栈中元素的最小值;

每一次push,都比较min与x的大小,其次,push操作执行时若数组已满,这需要进行扩容,将数组长度扩大为原来的两倍,并进行数组的迁移。每一次 pop 时,若删除的是最小元素,则遍历数组重新找到最小的元素,然后删除栈顶元素。

时间分析:

push: 最坏情况为O(n),其余为O(1)

pop:最坏情况:O(n),其余为O(1)

top:O(1)

getMin: O(1)

以下是代码:

private int[] array;
private int last;
private int min;
public MinStack() {
array = new int[16];
last=0;
min = 0;
} public void push(int x) {
if(last >= array.length){
this.array = expand(this.array);
}
array[last++] = x;
if(min > x || last==1){
this.min = x;
}
}
public void pop() {
if(last -1 <0){
return;
}
if(this.min == array[last-1]){
this.min = findMin(array,last-1);
}
this.last = this.last - 1;
this.last = this.last < 0 ? 0:this.last;
} public int top() {
if(last -1 <0){
return 0;
}
int result = this.array[last-1];
return result;
} public int getMin() {
return this.min;
} private int[] expand(int[] array){ int[] tempArray = new int[array.length*2];
for (int i=0;i<array.length;i++){
tempArray[i] = array[i];
} return tempArray;
} private int findMin(int array[],int last){ if(last <=0){
return 0;
}
int tempMin = array[last-1];
for (int i=last-1;i>=0;i--){
tempMin = Math.min(array[i],tempMin);
}
return tempMin; }

--------------------------------------------------------我是分割线----------------------------------------------------------------------------

好了,经过讨论学习,学会了一种新的解法:

在上面介绍的基础上在添加一个公式:

首先先介绍一下 一个公式:2*x-minEle_1 < x 其中 x是当前插入的值,minEle_1 是x插入前的最小值

详细思路如下:

入栈:

1.使用一个变量minEle 记录当前栈中的元素 (是真实值)

2.每一次插入一个元素x,比较 x 与 minEle,有如下两种情况:

  1)、若x > = minEle,那么直接push进栈,minEle不变

  2)、若x < minEle,那么执行以下操作:

    a、首先计算要入栈的虚拟值y:y=2*x - minEle ,y入栈(注意此处y不是真实值,但是可以肯定 y < x 原因:因为x < minEle  故 x-minEle < 0  ; x+一个小于0的值一定小于x)

    b、其次更新minEle 为 x

   以上步骤只为保存前一个最小的值。

出栈:

1、出栈前,比较当前出栈的值Y,存在两种情况:

  1)、若Y > minEle 那么直接出栈

  2)、若 Y < minEle,那么要执行如下操作:

      a、X = 2*minEle - Y ,

      b、讲 minEle出栈,同事更新minEle= X

    以上步骤用于还原前一次的最小值

根据此算法可以保证 更新minEle的时间复杂度为O(1),弥补了之前自己想法上的不足,同时又不需要使用另一个辅助栈,节约了空间(虽然空间复杂度都是O(N),但是O(N) 与 O(2N)还是有差别的)

以下是写的代码:(注意:由于int 最大值为 2147483647 最小值为 -2147483648 ,所以需要针对极限值做一次单独的转换:

入栈:

当 x - Integer.MIN_VALUE < minEle 时:

    int record = last; (此处记录特殊处理的位置,当到此位置时另行处理)

    int reminder = minEle % 10 (保存余数);

    设Y为要存入的值:Y = x/10 *2 - minEle/10 ,然后将Y入栈

出栈:

判断当前位置 last == record:

  若是 : minEle = (x/10 *2 - 当前出栈的值)*10 + reminder

判断当前出栈元素是否小于 minEle

  若是: minEle = (x*2 - 当前出栈的值)

否则直接出栈,无需操作

 package algorithm;
import java.util.Stack; public class MinStack { private int[] array; private int last; private int min; private int record; private int min_remainder; public MinStack() {
array = new int[16];
last=0;
min = 0;
record=-1;
min_remainder = -1;
} public void push(int x) {
if(last >= array.length){
this.array = expand(this.array);
}
if(last==0){
this.min = x;
array[last++] = x;
}else if(this.min > x){
if(x - (Integer.MIN_VALUE+1) < this.min){
array[last++] = createVirtual(x);
}else {
array[last++] = (x<<1) - this.min;
}
this.min = x; }else {
array[last++] = x;
}
} public void pop() {
if(last -1 <0){
return;
}
if(record == last){
this.min = (this.min/10 - array[last-1] + this.min/10)*10+min_remainder;
}else {
if(array[last-1] < this.min){
int temp = this.min<<1 - array[last-1];
this.min = temp;
}
}
this.last = this.last - 1;
this.last = this.last < 0 ? 0:this.last;
} public int top() {
if(last -1 <0){
return 0;
}
int result = 0;
if(record == last || array[last-1] < this.min){
result = this.min;
}else {
result = this.array[last-1];
}
return result;
} public int getMin() {
return this.min;
} private int[] expand(int[] array){
int[] tempArray = new int[array.length*2];
for (int i=0;i<array.length;i++){
tempArray[i] = array[i];
}
return tempArray;
} private int createVirtual(int x){
int temp = 0;
if(x - (Integer.MIN_VALUE+1) < this.min){
record = this.last;
min_remainder = this.min%10;
temp = x/10 - this.min/10 + x/10; return temp;
}else {
temp = x << 1 - this.min;
} return temp;
} public static void main(String[] args){
MinStack min = new MinStack();
min.push(2147483646);
min.push(2147483646);
min.push(2147483647);
min.top();
min.pop();
System.out.println("min:"+min.getMin());
min.pop();
System.out.println("min:"+min.getMin());
min.pop();
min.push(2147483647);
min.top();
min.push(-2147483648);
System.out.println("top"+min.top());
System.out.println("min:"+min.getMin());
min.pop();
System.out.println("min:"+min.getMin()); } }

LeetCode 腾讯精选50题--最小栈的更多相关文章

  1. LeetCode 腾讯精选50题--有效的括号

    根据题意,第一反应就是使用栈,左右括号相匹配,则将左括号出栈,否则将左括号入栈. 这里我用数组配合“指针”模拟栈的入栈与出栈操作,初始时指针位置指向0,表示空栈,凡遇上左括号则直接入栈,若遇上有括号, ...

  2. LeetCode 腾讯精选50题--二叉树中的最大路径和

    二叉树中的最大路径和 题目描述 给定一个非空二叉树,返回器最大路径和,路径指一条从任意节点出发,到达任意节点的序列,该路径至少包含一个节点,且不一定经过根节点 解题思路 树这一类数据结构我还不是很熟悉 ...

  3. LeetCode 腾讯精选50题--二叉树的最大深度

    求二叉树的最大深度, 基本思路如下: 设定一个全局变量记录二叉树的深度,利用递归,没遍历一层都将临时深度变量+1,并在每一节点递归结束后判断深度大小. 具体代码如下: package algorith ...

  4. LeetCode 腾讯精选50题--2的幂

    在二进制中,2的幂的数字用二进制表示时只会有一位表示为1,其余都为0,基于这个前提,可以有两种方案: 1. 做位移操作 2. 与数值取反并与原数值做与操作,判断是否与原来的数值相同 对于方案1,我的想 ...

  5. LeetCode 腾讯精选50题--求众数

    由于众数是指数组中相同元素的个数超过数组长度的一半,所以有两种思路,一. 先排序,后取排序后的数组的中间位置的值:二. 统计,设定一个变量统计相同元素出现的次数,遍历数组,若与选定的元素相同,统计变量 ...

  6. LeetCode 腾讯精选50题--只出现一次数字

    事先说明,如果不是评论区的大牛一语点破,我可能还会陷在死胡同里出不来,这道题其实很简单,利用了任何一个学过二进制的人都了解的定理,即: 1. 异或操作满足交换律 : a ^ b ^ c 等价于 a ^ ...

  7. LeetCode 腾讯精选50题--链表排序

    解题思路:归并 先把链表拆开,分为两部分,一直拆到只剩一个元素后,进行合并,利用一个临时节点记录重排后的链表的起始位置 合并不难,困难点在于如何拆分链表,自己的大体思路是利用两个指针,一个一次移动两位 ...

  8. LeetCode 腾讯精选50题--子集

    根据题意,找到几何中的所有子集,说实话子集是没有什么头绪的,因为如果采用遍历的方法,稍有遗漏不说,代码的嵌套循环层数随着数组大小的增加而增加,想了很久没有头绪后就去看了看评论,然后就被点破了解题的关键 ...

  9. LeetCode 腾讯精选50题-- 买卖股票的最佳时机 II

    贪心算法: 具体的解题思路如下: II 的解题思路可以分为两部分, 1. 找到数组中差值较大的两个元素,计算差值. 2. 再步骤一最大的元素的之后,继续遍历,寻找差值最大的两个元素 可以得出的是,遍历 ...

随机推荐

  1. Android8.0新特性总结

    1.通知渠道:开发者可以自定义应用的通知内容类别,为用户提供一个可以对通知精细控制的接口,用户可以对通知    进行精细的掌控 开发指南 创建通知渠道的步骤: 创建 NotificationChann ...

  2. haproxy-负载均衡介绍

    参考:http://www.iyunv.com/thread-252539-1-1.html 负载均衡介绍 四层和七层负载均衡的区别 四层 所谓的四层就是ISO参考模型中的第四层.四层负载均衡也称为四 ...

  3. [Kaggle] How to handle big data?

    上一篇,[Kaggle] How to kaggle?[方法导论] 这里再做一点进阶学习. 写在前面 "行业特征" 的重要性 Ref: Kaggle2017—1百万美金的肺癌检测竞 ...

  4. Java集合(7):散列与散列码

    散列的价值在于速度.我们使用数组来保存键的信息,这个信息并不是键本身,而是通过键对象生成一个数字(散列码),作为数组下标.由于数组的容量是固定的,而散列容器的大小是可变的,所以不同的键可以产生相同的数 ...

  5. spring-boot结合maven配置不同环境的profile

    1.在spring-boot中新建配置文件 spring-boot不同环境配置文件格式为application-{profile}.yml 说明: 如果application.yml中的配置和appl ...

  6. python3速查参考- python基础 4 -> 元组 + 字典 + 字符串 的学习

    元组 元组:特点就是内容不可变,算只读的列表,可以被查询,不能被修改 a = 2, print(a) print(type(a)) b = ('a','b','c') print(b[1]) 运行结果 ...

  7. Metasploit-初篇

    Metasploit metasploit 是一款开源的安全漏洞检测工具 渗透测试的操作系统 无需赘言,kali Linux发行版是目前最流行的安全操作系统;基于Debian的操作系统附带了600多个 ...

  8. Netcat—瑞士军刀

    netcat是网络工具中的瑞士军刀,它能通过TCP和UDP在网络中读写数据.通过与其他工具结合和重定向,你可以在脚本中以多种方式使用它.使用netcat命令所能完成的事情令人惊讶. netcat所做的 ...

  9. Transformer详解

    0 简述 Transformer改进了RNN最被人诟病的训练慢的缺点,利用self-attention机制实现快速并行. 并且Transformer可以增加到非常深的深度,充分发掘DNN模型的特性,提 ...

  10. python列表和if语句的简单结合

    将列表所有元素打印出来 cars = ['toyota', 'honda', 'mazda', 'nissan', 'mitsubishi', 'subaru', 'suzuki', 'isuzu'] ...