剑指Offer-栈和队列

用数组实现栈

public class ArrayStack {

    private Integer[] arr;
    private Integer size;
	
	// 初始化
    public ArrayStack(int initSize) {
        if (initSize < 0) {
            throw new IllegalArgumentException("the init size is less than 0");
        }
        arr = new Integer[initSize];
        size = 0;
    }
	// 顶
    public Integer peek() {
        if (size == 0) {
            return null;
        }
        return arr[size-1];
    }
	// 添加元素
    public void push(int obj) {
        if (size == arr.length) {
            throw new ArrayIndexOutOfBoundsException("the queue is full");
        }
        arr[size++] = obj;
    }
	// 弹出元素
    public Integer pop() {
        if (size == 0) {
            throw new ArrayIndexOutOfBoundsException("the queue is empty");
        }
        return arr[--size];
    }
}

用数组实现队列

public class ArrayQueue {

    private Integer[] arr;
    // 用三个变量就能很快写出来,而不需要扣细节
    // start变量表示如果弹出一个元素,应该是哪一个位置上的
    private Integer start;
    // end变量表示如果添加一个元素,应该放在哪一个位置上
    private Integer end;
    // 利用size来约束start和end, start和end之间是解耦的,size的作用是在start和end之间跳转维护整个长度的作用
    private Integer size;

    public ArrayQueue(Integer initSize) {
        if (initSize < 0) {
            throw new IllegalArgumentException("the init size is less than 0");
        }
        arr = new Integer[initSize];
        start = 0;
        end = 0;
        size = 0;
    }

    public Integer peek() {
        if (size == 0) {
            return null;
        }
        return arr[start];
    }

    public void push (Integer obj) {
        if (size == arr.length) {
            throw new ArrayIndexOutOfBoundsException("the queue is full");
        }
        size++;
        arr[end] = obj;
        // end只要到底了,就会跳到开头
        end = end == arr.length-1 ? 0 : end + 1;
    }

    public Integer poll() {
        if (size == 0) {
            throw new ArrayIndexOutOfBoundsException("the queue is empty");
        }
        size--;
        Integer tmp = start;
        start = start == arr.length - 1 ? 0 : start + 1;
        return arr[tmp];
    }
}

两个队列实现栈

public class TwoQueueStack {
	
	private Queue<Integer> queueData;
	private Queue<Integer> help;

	public TwoQueueStack() {
		queueData = new LinkedList<Integer>();
    	help = new LinkedList<Integer>();
	}
    public void push(int value) {
		queueData.add(value);
    }
	public int peek() {
		while (queueData.size() !=1 ) {
			help.add(queueData.poll());
		}
		int res = queueData.poll();
		help.add(res);
		swap();
		return res;
	}
    public int pop() {
    	while (queueData.size() > 1) {
			help.add(queueData.poll());
		}
		int res = queueData.poll();
		swap();
		return res;
    }
    // 交换引用,因为add的时候,只在queueData中
    public void swap() {
		Queue<Integer> temp = help;
		help = queueData;
		queueData = temp;
	}	
}

单调栈

单调队列

在这里插入图片描述

public int[] maxSlidingWindow(int[] nums, int k ){
    if( nums.length == 0 &&  k == 0 ) return new int[0];
    int[] res = new int[nums.length - k +1];
    Deque<Integer> deque = new LinkedList<>();
    // 这里是将未形成窗口和形成窗口两个循环的过程放在一起执行
    for( int j = 0, i = 1 - k; j < nums.length; i++, j++){
    	// deque仅包含窗口元素,这一步是移动操作,弹出操作
        if( i > 0 && deque.peekFirst() == nums[i-1]) {
            deque.removeFirst();
        }
        // 队列放入元素规则,保证队首是最大的(保持队里递减)
        while(!deque.isEmpty() && deque.peekLast() < nums[j]) {
             deque.removeLast();
        }
        deque.addLast(nums[j]);
        // 结果
        if( i >= 0 ){
            res[i] = deque.peekFirst();
        }
    }
    return res;
}

剑指09-用两个栈实现队列

public class _09_两个栈实现队列 {
	// 输入栈,输出栈
    private Stack<Integer> inStack;
    private Stack<Integer> outStack;

    public _09_两个栈实现队列() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }

    // 在队列尾部插入元素;
    public void appendTail(int value) {
        inStack.push(value);
    }

    // 在删除队列头部元素;
    public int deleteHead() {
    	// 当输出栈为空的时候,才能进行转换数据操作
        if(outStack.isEmpty()){
            while(!inStack.isEmpty()){
                outStack.push(inStack.pop());
            }
        }
        // 输出栈输出数据
        return outStack.isEmpty() ? -1 : (int)outStack.pop();
    }
}
  • 关键点解析:
  • 两个栈:其中一个做输入栈,另外一个做输入栈
  • push操作:只需要输入栈push就行
  • pop操作:需要将输入栈的元素全部弄到输出栈中;如果这个时候输入栈中又出现新添加元素,需要等输出栈中的元素全部pop完,再弄到输出栈。
    在这里插入图片描述

剑指30- 包含min函数的栈

public class _30_包含min函数的栈 {
    /** initialize your data structure here. */
    // 两个栈:一个做数据栈,一个做最小栈
    // 用额外空间:多维护一个最小栈,来保存最小值在栈顶;最小堆结构也可以实现
    private Stack<Integer> dataStack;
    private Stack<Integer> minStack;
    public _30_包含min函数的栈() {
        minStack = new Stack<Integer>();
        dataStack = new Stack<Integer>();
    }
    public void push(int x) {
        dataStack.push(x);
        // 最小栈存数据,这里有等于号
        if(minStack.isEmpty() || minStack.peek() >= x){
            minS.push(x);
        }
    }
    public void pop() {
        // 比较的是Integer,需要用equals
        // 如果pop正好是最小栈的最小值,应该pop出去,因为这个数据已不存在
        if(dataStack.pop().equals(minStack.peek())){
            minStack.pop();
        }

    }
    public int top() {
        return dataStack.peek();
    }
    public int min() {
        return minStack.peek();
    }
}
  • 关键点解析
  • 借用最小栈维护最小值在栈顶,同样最大值也同理
  • 建立最小栈的时候,比较需要等号
  • 数据栈pop的时候,最小栈栈顶数据如果相同,则需要pop输出
  • equals的使用:Integer的缓存[-128,127],如果使用==,数字超过缓存范围则不相同

剑指59-1-滑动窗口的最大值

public class _59_滑动窗口的最大值 {

    // 暴力法
    //public int[] maxSlidingWindow(int[] nums, int k) {
    //    if (nums == null || nums.length == 0 || k == 0) return new int[0];
    //    int[] res = new int[nums.length-k+1];
    //    for (int i = 0; i <= nums.length-k; i++) {
    //        int temp = nums[i];
    //        for (int j = i; j < i+k; j++) {
    //            temp = Math.max(temp, nums[j]);
    //        }
    //        res[i] = temp;
    //    }
    //    return res;
    //}

    // 单调队列
    // 单调队列
    public int[] maxSlidingWindow(int[] nums, int k) {

        if(nums == null || nums.length == 0) {
            return new int[0];
        }

        int[] res = new int[nums.length - k + 1];
        Deque<Integer> queue = new ArrayDeque<>();

        for(int i = 0, j = 0; i < nums.length; i++) {
            if(!queue.isEmpty() && i - queue.peek() >= k) {
                queue.poll();
            }
            while(!queue.isEmpty() && nums[i] > nums[queue.peekLast()]) {
                queue.pollLast();
            }
            queue.offer(i);
            if(i >= k - 1) {
                res[j++] = nums[queue.peek()];
            }
        }

        return res;
    }
}

剑指59-2- 队列的最大值

public class _59_队列的最大值 {

    private Deque<Integer> queue;
    private Deque<Integer> help;

    public _59_队列的最大值() {
        queue = new ArrayDeque<>();
        help = new ArrayDeque<>();
    }

    public int max_value() {
        return queue.isEmpty() ? -1 : help.peek();
    }

    public void push_back(int value) {
        queue.offer(value);
        while(!help.isEmpty() && value > help.peekLast()) {
            help.pollLast();
        }
        help.offer(value);
    }

    public int pop_front() {
        if(queue.isEmpty()) {
            return -1;
        }
        int val = queue.pop();
        if(help.peek() == val) {
            help.pop();
        }
        return val;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值