其他栈相关问题

本文深入探讨了如何使用数据结构设计常数时间的栈与队列,包括最小元素栈、两个栈实现队列、两个队列实现栈、数组实现两个栈等技巧,并介绍了递归思想在栈上的应用,如反转栈元素和对栈内元素进行排序。通过实例和解释,展示了这些数据结构在实际问题解决中的高效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 包含min函数的栈

来源
leetcode155 https://leetcode.com/problems/min-stack/
剑指offer21

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.
注:这几个函数要在常数时间内完成。

思路:push,pop,top正常来说肯定是常数时间内的,但是如何让getMin也能保持在常数时间呢。
单独维护一个单调递减栈来存储栈中最小值。

class MinStack {
    Deque<Integer> deque = new ArrayDeque<Integer>();
    Deque<Integer> minDeque = new ArrayDeque<Integer>();
    public void push(int x) {
        deque.add(x);
        if(minDeque.size() == 0 || x <= minDeque.peekLast()) minDeque.add(x);
    }
    public void pop() {
        int x = deque.pollLast();
        if(x == minDeque.peekLast()) minDeque.pollLast();
    }
    public int top() {
        return deque.peekLast();
    }
    public int getMin() {
        return minDeque.peekLast();
    }
}

2. 用两个栈实现队列

维护两个栈,每次Push进一个栈中,每次getHead从另一个栈中取,当取栈空了,则将push栈中的弹入取栈中。这样保证了先进的一定比后进的先出。弹出元素的平均时间复杂度O(1)

class QueueImpl {
    Deque<Integer> deque_push = new ArrayDeque<Integer>();
    Deque<Integer> deque_pop = new ArrayDeque<Integer>();

    public void push(int x) {
        deque_push.add(x);
    }
    public int getHead() {
        if(deque_pop.size() == 0){
                while(deque_push.size() != 0) deque_pop.add(deque_push.pollLast());
        }
        return deque_pop.pollLast();
    }
}

3. 用两个队列实现栈

维护两个队列,每次向有元素的队列压入元素(两个队列会交替压入),每次弹出元素时要将有元素队列中的元素放入另一个队列中,留下一个元素在原来的队列弹出。弹出的平均时间复杂度O(n)。

class StackImpl {
    Deque<Integer> deque1 = new ArrayDeque<Integer>();
    Deque<Integer> deque2 = new ArrayDeque<Integer>();

    public void push(int x) {
        Deque<Integer> deque = deque1.size() == 0 ? deque2 : deque1;
        deque.add(x);
    }
    public int pop() {
        Deque<Integer> deque_pop = deque1.size() == 0 ? deque2 : deque1;
        Deque<Integer> deque_push = deque1.size() == 0 ? deque1 : deque2;
        while(deque_pop.size() > 1) deque_push.add(deque_pop.pollLast());
        return deque_pop.pollLast();
    }
}

4. 用一个数组实现两个栈

说明如何用一个数组A[1..n]来实现两个栈,使得两个栈中的元素总和不到n时,两个都不会发生上溯。注意PUSH和POP操作的时间应为O(1)
思路:两个栈从数组的前后开始向中间存储。

class Stack {
    int[] arr;
    int flag1 = -1, flag2  = -1;
    public Stack(n){
        arr = new int[n];
        flag2 = n;
    }
    public void push1(int x) {
        if(flag1 == (flag2 - 1)) return;
        arr[++flag1] = x; 
    }
    public void push2(int x) {
        if(flag1 == (flag2 - 1)) return;
        arr[--flag2] = x; 
    }
    public int pop1() {
        if(flag1 > -1) return arr[flag1--]; 
    }
    public int pop2() {
        if(flag2 < n) return arr[flag2++]; 
    }
}

思考:用一个数组实现三个栈呢。
可以使用交叉索引,像上一题存两个栈的话,一个栈可以存储在0,2,4,6…/另一存储在1,3,5,7…
若三个栈就可以存储成0,3,6,9…./1,4,7,10…/2,5,8,11….
若还沿用上题方法,可以将第三个栈存储为:mid, mid+1, mid-1, mid+2, mid-2这样对称存储。


5. 栈对递归思想的考察

翻转栈中所有元素,要求额外空间复杂度o(1)
用递归来实现(与汉诺塔思想类似)
首先弹出栈顶元素top1,将剩余n-1栈递归反转,反转后弹出当前栈顶元素top2,再递归反转n-2栈。
放入top1元素,反转n-1栈。
最后放入top2元素。

public void reverseStack(Deque<Integer> deque){
    if(deque.size() == 0) return;
    int top1 = deque.pollLast();
    reverseStack(deque);
    if(deque.size() == 0) deque.add(top1);
    else{
        int top2 = deque.pollLast();
        reverseStack(deque);
        deque.add(top1);
        reverseStack(deque);
        deque.add(top2);
    }
}

-给栈中元素排序
类似冒泡排序的递归思想,每次将最小的值弹到栈顶,然后从下一个元素开始进行下轮的冒泡递归

public void sortStack(Deque<Integer> deque){
    if(deque.size() == 0) return;
    int top1 = deque.pollLast();
    sortStack(deque);
    if(deque.size() == 0) deque.add(top1);
    else{
        int top2 = deque.pollLast();
        if(top1 <= top2) {
            deque.add(top2);
            deque.add(top1);
        }
        else{
            deque.add(top1);
            sortStack(deque);
            deque.add(top2);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值