设计一个有getMin功能的栈 & 由两个栈组成的队列
设计一个有getMin功能的栈
《程序员代码面试指南》第1题 P1 难度:士★☆☆☆
该题说是入门难度题,但实际上一上来想半天都想不出来o(╥﹏╥)o
随后去牛客网上在线编程,看到标签有个单调栈。好家伙,一上来就整个我不会的知识点
于是我去百度了一下,这篇CSDN博客讲的还不错(https://blog.csdn.net/lucky52529/article/details/89155694)
结合单调栈的原理,我又想了一下,终于想出来了。
核心是需要2个栈,一个栈用来正常保存数据(stackData),另一个栈用来保存最小值(stackMin,单调栈)。
我的思路是,push时:stackData正常保存数据,但是stackMin分两种情况:为空时,直接保存数据;不为空时,判断当前数据比stackMin的栈顶元素小(应该是小于等于),则入栈,否则不入栈。
pop时:stackData正常弹出数据,并与stackMin的栈顶元素作比较,相等则stackMin的栈顶元素出栈,否则不出栈。
getMin时直接调用stackMin的peek方法返回栈顶元素即可。
以下是我自己写的代码:
import java.util.*;
public class Main{
private static Stack<Integer> stack = new Stack();
private static Stack<Integer> minStack = new Stack();
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
Integer cnt = Integer.parseInt(scan.nextLine());
for(int i=0; i<cnt; i++) {
String line = scan.nextLine();
if(line.startsWith("push")) {
push(Integer.parseInt(line.substring(5)));
} else if(line.startsWith("pop")) {
pop();
} else if(line.startsWith("getMin")) {
System.out.println(getMin());
}
}
}
private static void push(Integer i) {
stack.push(i);
if(minStack.empty()) {
minStack.push(i);
} else if(i < minStack.peek()) {
minStack.push(i);
}
}
private static void pop() {
Integer i = stack.pop();
if(i == minStack.peek()) {
minStack.pop();
}
}
private static Integer getMin() {
return minStack.peek();
}
}
但是在看了标答后,发现还是有点小问题:
- push时,当前数据与stackMin的栈顶元素作比较时,如果相等也应该入栈(举例子,假如放3 2 1 1,按照我先前的思路则stackMin的数据为3 2 1。那么stackData的第一个1出栈后,stackMin就成了3 2,但是stackData的Min目前还是1,显然不对了)
- 调用pop()和getMin()方法最好加个判断是否为空,为空就抛异常(写代码还是得考虑全面一点嘛)
于是,牛客网上题解代码如下:
import java.util.Stack;
import java.util.Scanner;
public class Main{
private Stack<Integer> numStack;
private Stack<Integer> minStack;
public Main(){
numStack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
public void push(int num){
if(minStack.isEmpty()){
minStack.push(num);
}else{
int min = Math.min(minStack.peek(),num);
minStack.push(min);
}
numStack.push(num);
}
public int pop(){
if(numStack.isEmpty()){
throw new RuntimeException("stack is empty!");
}
minStack.pop();
return numStack.pop();
}
public int getMin(){
if(minStack.isEmpty()){
throw new RuntimeException("stack is empty!");
}
return minStack.peek();
}
public static void main(String[] args){
Main myClass = new Main();
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
scanner.nextLine();
String[] line;
for(int i = 0; i < N; i++){
line = scanner.nextLine().split(" ");
if(line[0].equals("getMin")){
System.out.println(myClass.getMin());
}else if(line[0].equals("push")){
myClass.push(Integer.valueOf(line[1]));
}else{
myClass.pop();
}
}
}
}
这还只是第一种方法。书上还描述了第二种方法,具体原理参照书P3,这里不做过多赘述。
由两个栈组成的队列
题目:由两个栈组成的队列
《程序员代码面试指南》第2题 P5 难度:尉★★☆☆
该题在做之前就无意中看到了题目,然后想了一会,大概有了思路。等到真正做的时候,思路就很清晰了。(以后还是尽量不提前看题目,现看现想)
牛客网简单难度,感觉比第1题入门难度还简单一点,毕竟只用到了栈的知识点(不像第一题还用单调栈)
主要思路是,一个栈用来push(stackPush),一个栈用来pop和peek(stackPop);
push时:正常将数据push到stackPush栈中
pop和peek时:判断stackPop栈是否为空,为空则需要将stackPush栈中的元素全部pop出来并push到stackPop里。然后再pop或peek
但是我一开始想的是pop或peek后第一次push还要压回stackPush栈。看了解答后,发现完全是多此一举,还浪费时间。
主要原因是前一次stackPush到stackPop,只要stackPop不为空,反正之前压入的数据从上到下是按照时间先后排列的,stackPop再次pop还是比较早的数据。此时stackPush再push也不受影响。等stackPop中数据pop空了,可以再次把stackPush中的数据反过来压进去,也同样是按照先后顺序从上到下的排列。所以是不需要回压的。总而言之,核心在于必须在stackPop为空时才能将stackPush的数据压入stackPop,满足这个条件,pop出的数据的顺序是不可能乱的。
我自己的代码(有点小问题)如下:
import java.util.*;
public class Main {
private static Stack<Integer> stack1 = new Stack();
private static Stack<Integer> stack2 = new Stack();
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = Integer.parseInt(scan.nextLine());
for(int i=0; i<n; i++){
String line = scan.nextLine();
if(line.startsWith("add")) {
add(Integer.parseInt(line.substring(4)));
} else if(line.startsWith("poll")) {
poll();
} else if(line.startsWith("peek")) {
System.out.println(peek());
}
}
}
private static void add(Integer i) {
while(!stack2.empty()) {
stack1.push(stack2.pop());
}
stack1.push(i);
}
private static void poll() {
while(!stack1.empty()) {
stack2.push(stack1.pop());
}
stack2.pop();
}
private static Integer peek() {
while(!stack1.empty()) {
stack2.push(stack1.pop());
}
return stack2.peek();
}
}
然后牛客网上题解代码如下:
import java.util.*;
public class Main{
static Stack<Integer> stack1 = new Stack<Integer>();
static Stack<Integer> stack2 = new Stack<Integer>();
public static void add(int a){
stack1.push(a);
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
}
public static int poll(){
if(stack2.isEmpty() && !stack1.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
if(stack2.isEmpty()) throw new RuntimeException("队列空");
return stack2.pop();
}
public static int peek(){
if(stack2.isEmpty() && !stack1.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
if(stack2.isEmpty()) throw new RuntimeException("队列空");
return stack2.peek();
}
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = Integer.valueOf(scan.nextLine());
for(;n>0;n--){
String str = scan.nextLine();
String[] splits = str.split(" ");
if("add".equals(splits[0])){
add(Integer.parseInt(splits[1]));
}else if("peek".equals(splits[0])){
System.out.println(peek());
}else{
poll();
}
}
}
}
(评论说这里面重复代码可以抽取出来。看来牛客网上题解的代码可能也有小瑕疵。因此完美的解答还是参照书本吧,毕竟也花钱买了书的(#^.^#)
此外我自己的思路描述可能也不标准,详细的思路还是参照书上大佬的解释吧)

浙公网安备 33010602011771号