LeetCode(剑指offer) Day1

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

解题过程记录:本题就是用两个栈,其中一个作为输入栈,另外一个作为输出栈,由于栈的特点是先进后出,有序数字全部进栈然后再出栈会使这个数字序列逆序,然后逆序数字序列再一次经过进栈出栈操作会再次逆序,经过这两次逆序,原本的数字序列的顺序会保持不变,符合队列先进先出的特点。

第一次提交未通过:出队列时,直接把输入栈的数字压入输出栈,忽略了此时输出栈中可能还会有数据,如果输出栈中有数据,序列顺序前后并不符合先进先出的特点。

class CQueue {
     Stack<Integer>s1=null,s2=null;
    public CQueue() {
         s1=new Stack<>();
         s2=new Stack<>();
    }
    
    public void appendTail(int value) {
         s1.push(value);
    }
    
    public int deleteHead() {
         while(!s1.isEmpty()){
            s2.push(s1.pop());
        }
        if (s2.isEmpty()){
            return -1;
        }
        return s2.pop();
    }
}

第二次提交未通过:调用appendTail函数出队列,先判断s1输入栈中是否有数据,如果有并且s2输出栈中为空,将s1的数据全部压入s2输出栈,两栈全为空返回-1。思路混乱错误

class CQueue {
     Stack<Integer>s1=null,s2=null;
    public CQueue() {
         s1=new Stack<>();
         s2=new Stack<>();
    }
    
    public void appendTail(int value) {
         s1.push(value);
    }
    
    public int deleteHead() {
        while(!s1.isEmpty()){
            if(s2.isEmpty()){
                s2.push(s1.pop());
            }
        }
        if (s2.isEmpty()){
            return -1;
        }
        return s2.pop();
    }
}

第三次提交通过: 只有出stack为空的时候,才将进stack的数据全部倒入出stack。

class CQueue {
     Stack<Integer>s1=null,s2=null;
    public CQueue() {
         s1=new Stack<>();
         s2=new Stack<>();
    }
    
    public void appendTail(int value) {
         s1.push(value);
    }
    
    public int deleteHead() {
       if (s2.isEmpty()){
            if(s1.isEmpty()){
                return -1;
            }
            while(!s1.isEmpty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
}

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

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

解法一:使用主栈s存储数字序列,用一个辅助栈stackTemp同步记录主栈中的最小值。具体来说,当一个元素要入栈时,我们取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中;当一个元素要出栈时,我们把辅助栈的栈顶元素也一并弹出;在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中。

class MinStack {
    /** initialize your data structure here. */
    Stack<Integer> s=null;
    Stack<Integer>stackTemp=null;
    public MinStack() {
        s=new Stack<>();
        stackTemp=new Stack<>();
    }
    
    public void push(int x) {
        s.push(x);
        if(stackTemp.isEmpty()){
            stackTemp.push(x);
        }else{
            stackTemp.push(Math.min(x,stackTemp.peek()));
        }
    }
    
    public void pop() {
        s.pop();
        stackTemp.pop();
    }
    
    public int top() {
        return s.peek();
    }
    
    public int min() {
        return stackTemp.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

解法二:不使用辅助栈,保留当前最小值和差值,这种方法称为差值法。

解法思路讲解:差值法就是在向栈中插入数据的同时,同步记录并更新栈中的最小值,向栈中插入值x,栈中保存的不再是原值,而是x与最小值的差值即x-min。按照这种思路,push函数如下:

public void push(int x) {
        if (stack.isEmpty()) {
            stack.push(x);
            min = x;//栈为空,插入的第一个值作为当前最小值
        } else {
            stack.push(x - min);//栈不为空,后续插入栈中的值是x-min
            min = Math.min(min, x);//每插入一个新值,需要同步更新最小值
        }
    }
入栈序列523-21
栈实际保存的值5-31-43
当前最小值522-2-2

如果用min记录当前栈中的最小值,用min与栈中的元素进行运算就很容易复原原序列的真实值。通过上面的表格举个例子,入栈序列:5 2 3 -2 1 经过x-min运算存入栈中实际值为 5 -3 1 -4 3。此时栈顶元素为3,是一个整数即x-min>0,也即x>min,说明x不是最小值,如果此时查看栈顶元素,直接返回min+栈顶元素即可复原真实值;但是如果栈顶元素为负数,说明入栈的元素比当前栈中的最小值还要小,那么最小值min就是原值x;还有一种特殊情况,由于我们向栈中插入第一个元素的时候,没有做x-min操作而是直接将真实值插入栈中,并且将它作为最小值,所以当栈中仅有一个元素的时候,栈顶元素=min=真实值。综合上述三种情况,故top函数:

 public int top() {
        if (stack.peek() < 0 || stack.size()==1) {
            return min;
        } else {
            return (min + stack.peek());
        }
    
    }

前面我们分析过,如果当前栈顶元素为正数,说明该元素不与最小值对应,可以直接pop,无需更新min;如果栈顶元素为负数,说明该元素与最小值对应,而且pop出的元素就是最小值,pop出后我们需要确定栈中剩余元素的最小值,假设push该元素的时候插入栈中的值为value,那么value=x-min,公式变形得min=x-value。经过上面的分析,pop函数为:

 public void pop() {
        if (stack.peek() < 0) {
            min = min - stack.pop();
        } else {
            stack.pop();
        }
    }

 我们回看push函数中的一行代码  stack.push(x - min)

 这里题目要求传入的值x是int类型,假设x为Integer.MAX_VALUE,min为Integer.MIN_VALUE,这样x-min运算势必会溢出,因此min需要改为long 类型,相关的算数运算也要在long类型下进行,因此代码优化为:

import java.util.Stack;

public class MinStack {
    Stack<Long> stack = null;
    long min;

    public MinStack() {
        stack = new Stack<>();
    }
    
    public void push(int x) {
        if (stack.isEmpty()) {
            stack.push((long)x);
            min = (long)x;
        } else {
            stack.push((long)(x - min));
            min = Math.min(min, x);
        }
    }
    
    public void pop() {
        if (stack.peek() < 0) {
            min = min - stack.pop();
        } else {
            stack.pop();
        }
    }

    public int top() {
        if (stack.peek() < 0 || stack.size()==1) {
            return (int)min;
        } else {
            return (int)(min + stack.peek());
        }
    
    }

    public int min() {
        return (int)min;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

城南皮卡丘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值