7.栈

一.包含min函数的栈
1.题目描述:
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

2.示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.

3.解:
(1)我的答案:
import java.util.Stack;

class MinStack {
private Stack<integer> stack;</integer>

public MinStack() {
    stack = new Stack<>();
}

public void push(int x) {
    stack.push(x);
}

public void pop() {
    stack.pop();
}

public int top() {
    return stack.peek();
}

public int min() {
    int minValue = stack.firstElement();
    for (int i = 0; i < stack.size(); i++) {
        if (minValue > stack.get(i)) minValue = stack.get(i);
    }
    return minValue;
}

}

(2)网友答案:
private Node head;

public MinStack() {

}

public void push(int x) {

    if (head == null)
        head = new Node(x, x, null);
    else
        head = new Node(x, Math.min(head.min, x), head);
}

public void pop() {

    head = head.next;
}

public int top() {

    return head.val;
}

public int min() {

    return head.min;
}

private class Node {

    int val;
    int min;
    Node next;

    public Node(int val, int min, Node next) {

        this.val = val;
        this.min = min;
        this.next = next;
    }
}

class MinStack:

def __init__(self):
    self.stack = []
    self.minstack = []

def push(self, x: int) -> None:
    self.stack.append(x)
    if not self.minstack or x <= self.minstack[-1]:
        self.minstack.append(x)

def pop(self) -> None:
    if self.stack.pop() == self.minstack[-1]:
        self.minstack.pop()

def top(self) -> int:
    return self.stack[-1]

def min(self) -> int:
    return self.minstack[-1]

直接一个数组,就可以实现,因为调用的次数有限,所以可以先分配一个二倍调用次数大小的数组,其中偶数下标存储之前进栈的最小值,奇数下标存储进栈值,每次每次进栈最小值和进栈值同时添加。
class MinStack {

/** initialize your data structure here. */
int[] stack=new int[40000];
int topIndex=-1;
public MinStack() {
}
public void push(int x) {
    if(topIndex==-1)
    {
        stack[topIndex+1]=x;
    }
    else
    {
        if(x<=stack[topIndex-1])
        {
            stack[1+topIndex]=x;
        }
        else{
            stack[1+topIndex]=stack[topIndex-1];
        }
    }
    stack[2+topIndex]=x;
    topIndex+=2;   
} 
public void pop() {
    topIndex-=2;
}   
public int top() {
    return stack[topIndex];
}
public int min() {
    return stack[topIndex-1];
}

}

4.总结:
我的答案其实并不好,是用栈来实现栈,不符合题目要求。
网友的答案都很好,有的用链表,有的用两个数组,有的用一个数组。但是其本质都是一边添加元素,一边和未入栈之前的最小值比较,然后记录下当前的最小值。用链表的话,每添加一个节点,就拿该节点的值和之前节点里记录的上一轮栈的最小值作比较,然后将新的最小值记录到新添加的节点中。用两个数组的话,就是一个数组添加元素,一个数组添加最新的最小值。用一个数组的话就是奇数位记录元素,偶数位记录最新的最小值。

二.用两个栈实现队列
1.题目描述:
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

2.示例:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
这一行表示每一行代码的操作

[[],[3],[],[]]
这个表示每一行代码操作所需要的参数

举例:
CQueue 表示新建一个CQueue对象,对应的所需参数为[],即此操作不需要参数。
appendTail 表示执行一个appendTail()操作,对应要被操作的元素为3。
deleteHead 表示执行一个deleteHead操作,对应的所需参数为[],即此操作不需要参数。
deleteHead 表示执行一个deleteHead操作,对应的所需参数为[],即此操作不需要参数。

以上的输入其实是一个代码执行的步骤描述与其对应所需参数。

即两个纬度:
1、操作描述
2、此次操作所需参数
3、操作描述与操作所需参数是通过默认顺序一一对应的。

3.解:
(1)我的答案:
import java.util.Stack;

class CQueue {
Stack<integer> stackIn;
Stack<integer> stackOut;
int popValue;</integer></integer>

public CQueue() {
    stackIn = new Stack<>();
    stackOut = new Stack<>();
    popValue = -1;
}

public void appendTail(int value) {
    stackIn.push(value);
}

public int deleteHead() {
    if (stackOut.isEmpty()) {
        if (stackIn.isEmpty()) popValue = -1;
        else {
            while (!stackIn.isEmpty()) stackOut.push(stackIn.pop());
            popValue = stackOut.pop();
        }
    } 
    else {
        popValue = stackOut.pop();
    }
    return popValue;
}

}

(2)网友答案:

class CQueue {
LinkedList<integer> stack1;
LinkedList<integer> stack2;</integer></integer>

public CQueue() {
    stack1 = new LinkedList<>();
    stack2 = new LinkedList<>();
}

public void appendTail(int value) {
    stack1.add(value);
}

public int deleteHead() {
    if (stack2.isEmpty()) {
        if (stack1.isEmpty()) return -1;
        while (!stack1.isEmpty()) {
            stack2.add(stack1.pop());
        }
        return stack2.pop();
    } else return stack2.pop();
}

}

class CQueue {
LinkedList<integer> stack1;
LinkedList<integer> stack2;</integer></integer>

public CQueue() {
    stack1 = new LinkedList<>();
    stack2 = new LinkedList<>();
}

public void appendTail(int value) {
    stack1.add(value);
}

public int deleteHead() {
    if (stack2.isEmpty()) {
        if (stack1.isEmpty()) return -1;
        while (!stack1.isEmpty()) {
            stack2.add(stack1.pop());
        }
        return stack2.pop();
    } else return stack2.pop();
}

}

4.总结
我的答案比网友的第一个答案好,网友的答案并没有真正的把两个栈连接起来,使两个栈的两个顶点分别成为队列的头和尾。我的答案真正的将两个栈连接起来,而且执行删除节点操作时最多只需要执行一次while。其连接两个栈的原理是:当输入输出栈都有元素得时候,就是正常得队列的出队入队。当输出栈里没有元素,即输出完了以后,就会将输入栈的元素“平移”到输出栈中,这里是实现了两个栈的连接的关键点。
而网友2的答案和我的答案的原理一样,只不过它使用的不是java的栈类,而是LinkedList类,使用这个类的优点如该网友所说:“使用java的同学请注意,如果你使用Stack的方式来做这道题,会造成速度较慢; 原因的话是Stack继承了Vector接口,而Vector底层是一个Object[]数组,那么就要考虑空间扩容和移位的问题了。 可以使用LinkedList来做Stack的容器,因为LinkedList实现了Deque接口,所以Stack能做的事LinkedList都能做,其本身结构是个双向链表,扩容消耗少。 但是我的意思不是像100%代码那样直接使用一个LinkedList当做队列,那确实是快,但是不符题意。 贴上代码,这样的优化之后,效率提高了40%,超过97%。”

全部评论

相关推荐

那一天的Java_J...:他本来公司就是做这个的,不就是正常的游戏客户端和服务器开发,软硬件联动,有啥恶心不恶心的,提前告诉你就是怕你接受不了,接受不了就没必要再往后走流程浪费时间,虽然这公司是一坨。
点赞 评论 收藏
分享
程序员小白条:找的太晚,别人都是大三实习,然后大四秋招春招的,你大四下了才去实习,晚1年
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务