一、栈的使用
栈在使用过程中,算法的运算过程应该是从后向前计算的
在栈的不仅可以存放值还可以存放下标
计算每日温度
当遇到比栈顶高的温度时,开始标记结果,然后继续出栈直到计算到比当前值大的温度,然后入栈
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/daily-temperatures
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1.关键点在,栈的存储的是小标而不是值,每向前一步,就操作一遍stack
/**
* 根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/daily-temperatures
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1.关键点在,栈的存储的是小标而不是值,每向前一步,就操作一遍stack
*/
public class EverdayTemp {
public static void main(String[] args) {
int[] temp = {73, 74, 75, 71, 69, 72, 76, 73};
int[] result = new int[8];
Stack<Integer> stack = new Stack<>();
stack.push(0);
for (int i = 1; i < temp.length; i++) {
while(!stack.isEmpty() && temp[i] > temp[stack.peek()]){
result[stack.peek()]=(i-stack.pop());
}
stack.push(i);
}
System.out.println(Arrays.toString(result));
}
}
2.计算柱状图最大面积
同样是倒着计算的顺序,当遇到比栈顶元素低的柱状时开始计算,直到栈顶原始比当前柱状高,如果栈为空,特殊处理宽度是为当前下标而不是两个小标相减
/**
* LeetCode 第 84 题:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1。
* 求在该柱状图中,能够勾勒出来的矩形的最大面积。
*
* 1.当栈空时,宽度应该是游标i
*/
public class HistogramArea {
public static void main(String[] args) {
int[] nums = {2,1,5,6,2,3};
int max = new HistogramArea().maxArea(nums);
System.out.println(String.format("最大值:%s",max));
}
private int maxArea(int[] nums){
int maxAera = 0;
Stack<Integer> stack = new Stack<>();
for(int i=0;i<=nums.length;i++){
while (!stack.isEmpty() && (i== nums.length||nums[i] < nums[stack.peek()])){
int height = nums[stack.pop()];
int width = stack.isEmpty()?i:i-1-stack.peek();
maxAera = Math.max(maxAera,height*width);
}
stack.push(i);
}
return maxAera;
}
}
二、队列的使用
队列使用比较简单,以下介绍一个特殊的队列,双端队列。
它可以在两端都可以进出,在算法中合理的运用时可以将时间复杂度降低
1.滑动窗口最大值
不断向队列中加元素,保持最左端的数是最大的数,如果小于这个数就进入队列,如果大于这个数,清空队列并将当前的数入队列。注意:每次只于最左端的数进行比较
/**
- 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
- 返回滑动窗口中的最大值。
- 示例:
- 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
- 输出: [3,3,5,5,6,7]
- 解释:
- 滑动窗口的位置 最大值
- [1 -3 3] -3 -1 3 1 7 3
- 1 [3 -1 -3] 5 3 6 7 3
- 1 3 [-1 -3 5] 3 6 7 5
- 1 3 -1 [-3 5 3] 6 7 5
- 1 3 -1 -3 [5 3 6] 7 6
- 1 3 -1 -3 5 [3 6 7] 7
- 提示:
- 你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
- 来源:力扣(LeetCode)
- 链接:https://leetcode-cn.com/problems/sliding-window-maximum
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 1.优先队列 nlogn ,而且解决不了元素重复的问题,除非用hash
- 2.双端队列 在插入的过程中比较大小,由于在双端队列的两端查询,插入都是O(1),
- 所以将总的复杂度降低到了o(n),如果单向队列,复杂度就升级为n^2
*/
public class SlideWindow {
public static void main(String[] args) {
twoHeaderQuene();
}
public static void priQueneMethod(){
int k = 3;
Queue<Integer> priorityQueue = new PriorityQueue<Integer>((x,y) -> (y-x));
int[] temp = {1,3,-1,-3,5,3,6,7};
int[] result = new int[temp.length-k+1];
for (int i = 0; i < temp.length; i++) {
if(i < k){
priorityQueue.offer(temp[i]);
if(i==k-1)
result[i-k+1]=priorityQueue.peek();
}else {
priorityQueue.remove(temp[i-k]);
priorityQueue.offer(temp[i]);
result[i-k+1]=priorityQueue.peek();
}
}
System.out.println(Arrays.toString(result));
}
public static void twoHeaderQuene(){
int k = 3;
Deque<Integer> deque = new LinkedList<>();
int[] temp = {1,-3,-1,-3,5,3,6,7};
int[] result = new int[temp.length-k+1];
deque.offer(0);
for (int i = 1; i < temp.length; i++) {
if(temp[deque.peekFirst()] < temp[i]){
deque.clear();
deque.offerFirst(i);
}else {
deque.offer(i);
}
if(i>k-2){
result[i-k+1]=temp[deque.peekFirst()];
}
}
System.out.println(Arrays.toString(result));
}
三、栈和队列的同时使用
简单计算功能(只有加减乘除)
注意:需要一个暂时变量存在符号,当遇到符号后的数时候再运算,乘除优先级大于加减,如遇到括号使用递归进行计算。
/**
* 1.使用sign符号,先赋值在计算的方式就不必回头
* 2.队列最后一定要加一个+加号触发最后一个符号的运算
* 3.使用递归计算括号内的值
* 4.判断右括号要再赋值后,才能避免内层的递归队列多出一个符号
*/
public class CaculateMachine {
public static void main(String[] args) {
Queue<Character> queue = new LinkedList<>();
String temp = "10/(20+1-2)*2";
for (int i =0;i< temp.length();i++){
Character c = temp.charAt(i);
queue.offer(c);
}
queue.offer('+');
System.out.println(new CaculateMachine().caculate(queue));
}
private double caculate(Queue<Character> characters){
double num =0;
Character sgin='+';
Stack<Double> stack = new Stack<>();
while (!characters.isEmpty()){
Character c = characters.poll();
if(Character.isDigit(c)){
String s = "0.1";
System.out.println("==="+num*10);
System.out.println("==="+(num*10+c));
System.out.println("==="+(num*10+c-'0'));
// System.out.println("=="+ (s-'0'));
num=num*10+c-'0';
}else if(c.equals('(')){
num=caculate(characters);
}
else {
if(sgin.equals('+')){
stack.push((double)num);
}else if(sgin.equals('-')){
stack.push((double)-num);
}else if(sgin.equals('*')){
stack.push(stack.pop()*num);
}else if(sgin.equals('/')){
stack.push(stack.pop()/num);
}
sgin=c;
num=0;
//写在赋值的后面,才能避免内层的递归队列多出一个符号
if(sgin.equals(')'))
break;
}
}
double sum = 0;
while (!stack.isEmpty()){
double v = stack.pop();
sum+=v;
}
return sum;
}
}
网友评论