ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

设计:栈和队列相互实现、最小栈、增量更新栈

2021-07-26 20:57:51  阅读:133  来源: 互联网

标签:index val 队列 元素 最小 int 增量 public


文章目录

栈实现队列

栈实现队列
栈和队列共同的特点就是支持基于头或尾的操作,其中队列支持“一侧进,另一侧出”,而栈仅能对一侧进行进出,即栈无法直接删除栈底节点
而此时我们需要一个另外一个辅助栈,让这个辅助栈存储当前栈的倒序序列,那么栈顶不就是辅助栈的栈顶元素了吗

    LinkedList<Integer> stack1 = new LinkedList<>();
    LinkedList<Integer> stack2 = new LinkedList<>();

其中stack1存放正序序列,而stack2在用于存放倒序序列(仅在删除时作为一个辅助栈去使用即可)

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

元素往1放入,每次delete就往2里面导入,寻找的时候先往2中找(因为2充当的实际上是一个队列首部的角色,而1充当的是一个队列尾部的角色

public int deleteHead() {
        if(!stack2.isEmpty()){
            return stack2.pop();    //stack2的栈顶就是队列的头
        }
        if(stack1.isEmpty()){
            return -1;
        }
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }

元素不存在冗余的部分,要么在stack1,要么在stack2,因此删除时先从2中(逻辑上的队头一侧)寻找,否则将1中元素全部导入2后再取出队首元素

队列实现栈

队列实现栈
假如现在由4个元素 1 2 3 4依次进入队列和栈,那么对应[1,2,3,4],其中队列出队的顺序是1 2 3 4,而栈则是 4 3 2 1
因此可以看出来,队列和栈在出队的时候,序列是反的,那么可不可以在将元素入队的时候就执行一个反向操作

    Queue<Integer> queue=new LinkedList<>();

我们使用一个队列去实现栈,每次元素入栈的时候,我们将之前的元素依次弹出,并且再次加入队中

    public void push(int x) {
        queue.offer(x);
        for (int i = 0; i < queue.size() - 1; i++) {//刚加入的x不进行操作
            queue.add(queue.poll());//一边弹一边加入
        }
    }

我们依次加入1 2 3 4,那么第一次加入[1],然后是[2,1],接着[3,2,1],最后的队列为[4,3,2,1]。其实就是相当于每次进行头插来进行一个反序操作,但是根据队列的特性我们只能进行头删和尾插,因为我们通过将队列现有元素依次弹出再插入当新元素的后面,模拟一个新元素头插的操作
因为我们每次push都可以保证“即使底层数据结构是队列,元素顺序依然对应栈的顺序”,因此其他操作都是常规的删除和查看。

public int pop() {
        return queue.poll();
    }
public int top() {
        return queue.peek();
    }
public boolean empty() {
        return queue.peek() == null ? true : false;
    }

最小栈

最小栈
为了能够在O(1)的时间复杂度拿到栈中最小值,我们需要使用一个专门的数据结构去维护栈中元素的最小值。

    LinkedList<Integer> min=new LinkedList<>();
    LinkedList<Integer> arr=new LinkedList<>();

我们可以维护一个递减的单调栈,例如-1 3 -2 0 -3 8,我们使用一个常规的栈维护元素[-1,3,-2,0,-3,8],而最小栈维护一个栈顶保存最小值的结构[-1,-2,-3]

public void push(int val) {
        if(min.isEmpty()||min.peek()>=val){
            min.push(val);
        }
        arr.push(val);
    }

最小值总是单调的,栈顶元素保存当前栈的最小值,并且可以直接在O(1)拿到最小值

    public int getMin() {
        return min.peek();
    }

如果当前删除的值就是最小值的栈顶元素,那么此时的最小值也需要被更新。

    public void pop() {
        Integer p = arr.pop();
        if(p.equals(min.peek())){
            min.pop();
        }
    }

支持增量更新的栈

设计一个支持增量更新的栈
增量更新就是渐进式的更新,更新操作不是一次完成的。例如redis的哈希表扩容就是基于渐进式的,一旦发生扩容行为,增删改查方法都会同时在两个哈希表中进行,并且捎带进行扩容直到扩容完毕。
以inc方法为例:
void inc(int k, int val):栈底的 k 个元素的值都增加 val 。如果栈中元素总数小于 k ,则栈中的所有元素都增加 val 。
我们的最终目标是将inc方法的时间复杂度进行O(1)实现。

    int maxSize=0;
    int[] stack;
    int index=0;	
    //记录增量更新
    int[] add;

	public CustomStack(int maxSize) {
        this.maxSize=maxSize;
        stack = new int[maxSize];
        add = new int[maxSize];
    }

arr[index++]=val,index指向待插入的位置

我们使用一个辅助数组add记录每次inc()的增量,如果inc操作将栈底K个元素增加val,那么我们就在第k-1的位置记录这个增量val(下标从0开始)。基于差分思想当我们需要获取这个值的时候,只需从add数组中获取增量并更新数组中的值即可

    public void increment(int k, int val) {
        int limit = Math.min(k-1,index-1);
        if(limit>0){
            add[limit]+=val;
        }
    }

仅存储增量,不对数组中的元素进行实际的更新操作。

    public void push(int x) {
        if(isFull())return;
        stack[index++]=x;
    }

push的时候,不涉及获取元素的值,因此不需要将增量更新到对应元素。

    public int pop() {
        if(isEmpty()) return -1;
        //仅在pop时才获取具体值
        index--;
        //将增量累加到元素上
        int res = stack[index]+add[index];
        if(index-1>=0){
        	//增量更新
            add[index-1]+=add[index];
        }
        add[index]=0;//增量复原
        return res;
    }

因为pop()调用,我们需要返回元素的值,这个时候将增量更新到元素上,并且将对应位置的增量重置为0
注意:从栈顶到栈底方向,增量是叠加的,而这个叠加也是“懒更新”的,例如add数组记录[0,1,3,0,0],意味着栈底的三个元素需要最终被累加:1+3,1+3和3 。并且一旦更新完毕增量就可以复原为0(累加任务进行完毕了,清除任务列表)

    private boolean isEmpty(){
        return index==0;
    }
    private boolean isFull(){
        return index==maxSize;
    }

标签:index,val,队列,元素,最小,int,增量,public
来源: https://blog.csdn.net/qq_44793993/article/details/118760346

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有