剑指 Offer 31. 栈的压入、弹出序列
题意:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例一:
输入: pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出: true
解释: 我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例二:
输入: pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出: false
解释: 1 不能在 2 之前弹出。
解题思路:
刚开始自己想的时候是想用HashMap存数据在出栈数组中的位置,然后从入栈数组中遍历,如果在入栈序列的前面的数字比入栈顺序在后面的某个数字在出栈队列中的先出栈的话,那么它就要比这个入栈顺序在后面的数字的后面的数字都要先出栈,不然就不能构成一个正确的出栈顺序,例如上例子二:
入栈顺序:[1,2,3,4,5] 出栈顺序:[4,3,5,1,2],我们看到1比2先入栈,但是比2先出栈,那么正确的出入栈顺序的话,1要比2和其之后的所有的数字都出栈早,不然就不是一个正确的顺序
java实现—栈顺序关系的考虑
public boolean validateStackSequences(int[] pushed, int[] popped) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=0;i<popped.length;i++){
map.put(popped[i],i);
}
for(int i=0;i<pushed.length-1;i++){
int count = 0;
int site = -1;
for(int j=i+1;j<pushed.length;j++){
if(map.get(pushed[j])>map.get(pushed[i])){
count++;
if(site<0) site = j;
}
}
if(count>0&&count<(pushed.length-site)) return false;
}
return true;
}
但是这种实现速度比较慢,最坏接近O(n^2),可以通过借助栈模拟进行判断,即遍历入栈队列进行入栈操作,然后判断当前栈顶和出栈顺序数组第一个元素的位置,如果相同就出栈且出栈数组往后移动,最后完成遍历后判断是否是空栈来判断是不是正确的出栈顺序
java—栈模拟
public boolean validateStackSequences(int[] pushed, int[] popped) {
//栈模拟
Stack<Integer> stack = new Stack<Integer>();
int j = 0;
for(int i=0;i<pushed.length;i++){
stack.push(pushed[i]);
while(!stack.isEmpty()&&stack.peek()==popped[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
此处还可以对栈模拟的过程进行优化,即直接在入栈数组上进行栈操作,用一个指针控制入栈数组的下标表示栈顶来控制,最后通过遍历完的下标即栈顶是不是为0表示是不是正确的出栈顺序
java实现—栈模拟,优化
public boolean validateStackSequences(int[] pushed, int[] popped) {
//栈模拟
int i=0,j=0; //i记录栈顶,相当于在原数组上模拟栈操作,j记录出栈顺序
for(int num:pushed){
pushed[i] = num;
while(i>=0&&pushed[i]==popped[j]){
j++;
i--;
}
i++;//栈顶指针往空位置更新
}
return i==0;
}
优化后的栈模拟更快