LeetCode 腾讯精选50题--最小栈
题目很简单,实现一个最小栈,能够以线形的时间获取栈中元素的最小值
自己的思路如下:
利用数组,以及两个变量, 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题--最小栈的更多相关文章
- LeetCode 腾讯精选50题--有效的括号
根据题意,第一反应就是使用栈,左右括号相匹配,则将左括号出栈,否则将左括号入栈. 这里我用数组配合“指针”模拟栈的入栈与出栈操作,初始时指针位置指向0,表示空栈,凡遇上左括号则直接入栈,若遇上有括号, ...
- LeetCode 腾讯精选50题--二叉树中的最大路径和
二叉树中的最大路径和 题目描述 给定一个非空二叉树,返回器最大路径和,路径指一条从任意节点出发,到达任意节点的序列,该路径至少包含一个节点,且不一定经过根节点 解题思路 树这一类数据结构我还不是很熟悉 ...
- LeetCode 腾讯精选50题--二叉树的最大深度
求二叉树的最大深度, 基本思路如下: 设定一个全局变量记录二叉树的深度,利用递归,没遍历一层都将临时深度变量+1,并在每一节点递归结束后判断深度大小. 具体代码如下: package algorith ...
- LeetCode 腾讯精选50题--2的幂
在二进制中,2的幂的数字用二进制表示时只会有一位表示为1,其余都为0,基于这个前提,可以有两种方案: 1. 做位移操作 2. 与数值取反并与原数值做与操作,判断是否与原来的数值相同 对于方案1,我的想 ...
- LeetCode 腾讯精选50题--求众数
由于众数是指数组中相同元素的个数超过数组长度的一半,所以有两种思路,一. 先排序,后取排序后的数组的中间位置的值:二. 统计,设定一个变量统计相同元素出现的次数,遍历数组,若与选定的元素相同,统计变量 ...
- LeetCode 腾讯精选50题--只出现一次数字
事先说明,如果不是评论区的大牛一语点破,我可能还会陷在死胡同里出不来,这道题其实很简单,利用了任何一个学过二进制的人都了解的定理,即: 1. 异或操作满足交换律 : a ^ b ^ c 等价于 a ^ ...
- LeetCode 腾讯精选50题--链表排序
解题思路:归并 先把链表拆开,分为两部分,一直拆到只剩一个元素后,进行合并,利用一个临时节点记录重排后的链表的起始位置 合并不难,困难点在于如何拆分链表,自己的大体思路是利用两个指针,一个一次移动两位 ...
- LeetCode 腾讯精选50题--子集
根据题意,找到几何中的所有子集,说实话子集是没有什么头绪的,因为如果采用遍历的方法,稍有遗漏不说,代码的嵌套循环层数随着数组大小的增加而增加,想了很久没有头绪后就去看了看评论,然后就被点破了解题的关键 ...
- LeetCode 腾讯精选50题-- 买卖股票的最佳时机 II
贪心算法: 具体的解题思路如下: II 的解题思路可以分为两部分, 1. 找到数组中差值较大的两个元素,计算差值. 2. 再步骤一最大的元素的之后,继续遍历,寻找差值最大的两个元素 可以得出的是,遍历 ...
随机推荐
- C++ STL——常用算法
目录 一 常用查找算法 二 常用遍历算法 注:原创不易,转载请务必注明原作者和出处,感谢支持! 注:内容来自某培训课程,不一定完全正确! 一 常用查找算法 /* find算法 查找元素 @param ...
- <JavaScript>“浏览器模式”和“文档模式”之间的区别
只有IE浏览器中才会有“浏览器模式”和“文档模式”,兼容性视图涉及两个重要的功能便是“浏览器模式[browser mode]”和“文档模式[document mode]”,在IE8/IE9中按F12键 ...
- JVM 内存溢出(转载~)
对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么多大多数在解决OOM的情况,于此,本文就只阐述这个内容,携带一些分析和理解和部分扩展内容,也就是JVM宕机中的一些问题,OK,下面说下O ...
- Linux vi/vim命令
转自:http://www.runoob.com/linux/linux-vim.html Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一 ...
- HDFS的基础与操作
一 HDFS概念 1.1 概念 HDFS,它是一个文件系统,全称:Hadoop Distributed File System,用于存储文件通过目录树来定位文件:其次,它是分布式的,由很多服务器联合起 ...
- Swift3.0项目学习: 创建撰写按钮
import UIKit class PSMainViewController: UITabBarController { override func viewDidLoad() { super.vi ...
- python之selenium元素定位方法
前提: 大家好,今天我们来学习一下selenium,今天主要讲解selenium定位元素的方法,希望对大家有所帮助! 内容: 一,selenium定位元素 selenium提供了8种方法: 1.id ...
- 根据文本内容确定UILabel的高度
NSString *str = @"严重依赖中国的美国公司包括苹果.百胜餐饮集团都感受到了近期人民币贬值带来的痛.这直接导致了苹果股价下跌了5.2%,拥有肯德基.必胜客的百胜也下跌了4.9% ...
- Unity Shader的形式
(1)表面着色器 表面着色器是Unity自身的一种着色器代码类型.它需要的代码量很少,Unity在背后做了很多工作,但渲染的代价比较大.但Unity在背后仍旧把表面着色器转换成对应的顶点/片元着色器. ...
- Crunch黑客神器-创造个性字典
先来看第一个命令: crunch 6 7 123456 -o pass.txt 是什么意思呢?我们打开终端,输入这个命令之后,crunch代表使用crunch这个工具,6代表生成的密码最小是6位数,7 ...