栈和队列的相关算法题

栈和队列是两种非常重要的数据结构,都是逻辑结构,也就是说,栈(Stack),先进后出(后进先出),只要符合这种逻辑的数据结构都可以称为栈,队列(Queue),先进先出(后进后出),只要符合这种逻辑结构的数据结构,都可以称为队列。java中,有Stack这个类,但是很少用,因为它底层使用的是Vector(和ArrayList基本一样,唯一不同的是,里面的方法是synchronized修饰的,因此是线程安全的,但是效率低,所以很少用),大部分情况都是使用LinkedList作为栈和队列,具体用法可以看LinkedList用作栈和队列

1 用数组实现栈

定义一个top,指向栈顶元素,size,记录栈中元素个数,当往栈中放一个元素,top++,size++,从栈中取出一个元素,- -top,并返回arr[top],size- -。

代码

//用数组结构实现大小固定的队列和栈
public  class Stack{
    public Integer[] arr;
    public Integer size;
    public Integer top;
    public Stack(int initSize){
        if(initSize < 0){
            throw  new IllegalArgumentException("The init size is less than 0");
        }
        arr = new Integer[initSize];
        size = 0;
        top = 0;
    }
    public void push(int i){
        if(size > arr.length - 1){
            throw  new ArrayIndexOutOfBoundsException("this stack is full");
        }
        arr[top++] = i;
        size++;
    }
    public Integer pop(){
        if(size == 0){
            throw new ArrayIndexOutOfBoundsException("this stack is empty");
        }
        size--;
        return arr[--top];
    }
    public Integer peek(){
        if(size == 0){
            return null;
        }
        return arr[top-1];
    }
}

2 用数组实现队列

定义一个first、一个last指针,first指向队首元素,last指向队尾元素。向队列中添加元素时,将元素添加到数组索引为last的位置,之后,判断last是否等于arr.length - 1,等于的话就让last等于0,不等于就加1。从队列中取出元素时,直接取出arr[first],之后,判断first是否等于arr.length-1,等于的话就让first等于0,不等于的话,就让first加1。frist、last互不干扰。

代码

 //用数组实现队列
  public class Queue{
    Integer[] arr;
    Integer first;
    Integer last;
    Integer size;
    public Queue(int initSize){
        if(initSize < 0){
            throw new IllegalArgumentException("the init size is less than 0");
        }
        arr = new Integer[initSize];
        first = 0;
        last = 0;
        size = 0;
    }
    public void offer(Integer i){
        if(size >= arr.length){
            throw  new ArrayIndexOutOfBoundsException("this queue is full");
        }
        arr[last] = i;
        size++;
        //当数组满的时候,就让last指针为0
        last = last == arr.length - 1 ? 0 : last + 1;
    }

    public Integer poll(){
        if(size == 0){
            throw new ArrayIndexOutOfBoundsException("this queue is empty");
        }
        int temp = arr[first];
        size--;
        //当first等于arr.length - 1 的时候,让first等于0
        first = first == arr.length - 1 ? 0 : first + 1;
        return temp;
    }

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

3. 实现一个特殊的栈

实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

【要求】

  1. pop、push、getMin操作的时间复杂度都是O(1)。
  2. 设计的栈类型可以使用现成的栈结构。

解题思路
定义两个栈,一个栈用于存放所有数据,另一个辅助栈用来存放最小值,开始的时候,将第一个元素同时压入两个栈,之后,再压入元素,第一个栈直接压入,然后判断这个元素是否比辅助栈中的栈顶元素小,小就压入辅助栈,不小的话,就将辅助栈中的栈顶元素压入。当弹出元素的时候,要同时弹出辅助栈中的栈顶元素。

public class Stack2{
        //定义两个栈,第一个栈用来存放所有数据,第二个栈用来存放,当前数中的最小值
        Deque<Integer> stackData;
        Deque<Integer> stackMin;
        public Stack2(){
            stackData =  new LinkedList<>();
            stackMin = new LinkedList<>();
        }

        public void push(Integer i){
            //当向栈中压入元素时,和最小值栈中的栈顶元素比,如果小于就压入,大于的话
            // 就压入最小值栈中栈顶元素
            if(stackMin.isEmpty()){
                stackMin.push(i);
            }else if(this.getMin() > i){
                stackMin.push(i);
            }else{
                stackMin.push(stackMin.peek());
            }
            stackData.push(i);
        }

        public Integer pop(){
            if(stackData.isEmpty()){
                throw new ArrayIndexOutOfBoundsException("this stack is empty");
            }
            stackMin.pop();
            return stackData.pop();
        }

        public Integer getMin(){
            if(stackMin.isEmpty()){
                throw new ArrayIndexOutOfBoundsException("this stack is Empty");
            }
            return stackMin.peek();
        }
    }

这道题还有一种解法是,只有当压入的元素小于或者等于辅助栈栈顶元素的时候,才压入辅助栈,否则不压入,从栈中取出元素的时候,只有当取出的元素和辅助栈栈顶元素相等时,辅助栈才会弹出栈顶元素。push()方法和pop()方法如下:

public void push(int newNum) {
	if (this.stackMin.isEmpty()) {
		this.stackMin.push(newNum);
	} else if (newNum <= this.getmin()) {
		this.stackMin.push(newNum);
	}
	this.stackData.push(newNum);
}

public int pop() {
	if (this.stackData.isEmpty()) {
		throw new RuntimeException("Your stack is empty.");
	}
	int value = this.stackData.pop();
	if (value == this.getmin()) {
		this.stackMin.pop();
	}
	return value;
}

第三种解法
手写链表,以空间换时间

class MinStack {
    /*
    手写链表实现,实现栈,空间换时间
    */
    private class Node {
        int val;
        Node next;
        int min;
        public Node(int val, int min, Node next) {
            this.val = val;
            this.min = min;
            this.next = next;
        }
    }

    private Node head;
    /** initialize your data structure here. */
    public MinStack() {
        
    }
    
    public void push(int x) {
        if(head == null) {
            head = new Node(x, x, null);
        }else {
            //如果head不为空,就新建一个head,这个head的next指向上一个head(栈)
            //这个head的最小值,指向新head和老head之间的最小值
            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;
    }
}

4. 用栈实现队列结构

用栈实现队列结构,定义两个栈stack1、stack2,stack1负责存数据,stack2负责弹出数据,向队列中放数据时,直接将数据压入stack1,从队列中取出元素时,先判断stack2是否为空,不为空的话,就弹出栈顶元素,为空的话,就将stack1中的元素都压入stack2,如果stack1也为空的话,说明,队列中没有元素。

代码

 public class TwoStackQueue{
   Deque<Integer> stack1;
    Deque<Integer> stack2;
    public TwoStackQueue(){
        stack1 = new LinkedList<>();
        stack2 = new LinkedList<>();
    }
    public void offer(Integer i){
        stack1.push(i);
    }

    public Integer poll(){
        if(stack2.isEmpty()){
            if(stack1 .isEmpty()){
                throw new RuntimeException("this queue is empty");
            }
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    public Integer peek(){
        if(stack2.isEmpty()){
            if(stack1 .isEmpty()){
                return null;
            }
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
}

5. 用队列实现栈结构

队列,先进先出,栈,先进后出。定义两个队列queue1、queue2,向栈中压入数据时,直接将数据存到queue1中,从栈中取数据时,先将queue1中的数据存到queue2,只留最后一个数据在queue1中,弹出。之后,将queue2赋给queue1、将queue1赋给queue2,以便下次存或取数据。peek()操作和pop()操作类似,也是将queue1中的元素除最后一个以外都存到queue2,然后记录这个值,再将最后一个元素存到queue2,交换queue1、queue2,最后返回刚记录的那个值。

代码

 public class TwoQueueStack {
    private Queue<Integer> queue;
    private Queue<Integer> help;
    public TwoQueueStack(){
        queue = new LinkedList<>();
        help = new LinkedList<>();
    }
    public void push(Integer i){
        queue.offer(i);
    }

    public Integer pop(){
        if(queue.isEmpty()){
            throw new RuntimeException("the stack is empty");
        }
        while(queue.size() != 1){
            help.offer(queue.poll());
        }
        int res = queue.poll();
        swap();
        return res;
    }

    public Integer peek(){
        if(queue.isEmpty()){
           return null;
        }
        while(queue.size() != 1){
            help.offer(queue.poll());
        }
        int res = queue.poll();
        help.offer(res);
        swap();
        return res;
    }

    public void swap(){
       Queue<Integer> temp = help;
       help = queue;
       queue = temp;
    }
}

6. 猫狗队列

题目
实现一种猫狗队列的结构,要求如下:
用户可以调用offer方法将cat类或dog类的实例放入队列中;
用户可以调用pollAll方法,将队列中所有的实例按照进队列的先后顺序依次弹出;
用户可以调用pollDog方法,将队列中dog类的实例按照进队列的先后顺序依次弹出;
用户可以调用pollCat方法,将队列中cat类的实例按照进队列的先后顺序依次弹出;
用户可以调用isEmpty方法,检查队列中是否还有dog或cat的实例;
用户可以调用isDogEmpty方法,检查队列中是否有dog类的实例;
用户可以调用isCatEmpty方法,检查队列中是否有cat类的实例。

解题思路
定义两个队列,catQueue、dogQueue,catQueue用于存放Cat、dogQueue用于存放Dog,这样pollDog、pollCat、isDogEmpty、isCatEmpty方法,都可以实现,再定义一个PetEnterQueue类,封装Dog、Cat,并且加一个count属性,来记录dog、cat的顺序,调用pollAll方法时,比较catQueue、dogQueue中的Pet的count属性,谁小就先弹出谁。

代码

public class Code_04_DogCatQueue {
    public static class Pet{
        private String type;
        public Pet(String type){
            this.type = type;
        }
        public String getType(){
            return type;
        }
    }
    public static class Dog extends Pet{
        public Dog(){
            super("dog");
        }
    }
    public static class Cat extends Pet{
        public Cat(){
            super("cat");
        }
    }
    public static class PetEnterQueue {
        private Pet pet;
        private Long count;
        public PetEnterQueue(Pet pet, Long count){
            this.pet = pet;
            this.count = count;
        }

        public Pet getPet() {
            return pet;
        }

        public Long getCount() {
            return count;
        }
    }

    public static class DogCatQue{
        private Deque<PetEnterQueue> dogQue;
        private Deque<PetEnterQueue> catQue;
        private Long count;
        public DogCatQue(){
            dogQue = new LinkedList<>();
            catQue = new LinkedList<>();
            count = 0l;
        }

        public void offer(Pet pet){
            if(pet.getType().equals("dog")){
                dogQue.offer(new PetEnterQueue(pet, count++));
            }else{
                catQue.offer(new PetEnterQueue(pet, count++));
            }
        }

        public Pet pollAll(){
            if(!catQue.isEmpty() && !dogQue.isEmpty()){
                if(dogQue.peek().getCount() > catQue.peek().getCount()){
                    return catQue.poll().getPet();
                }else{
                    return dogQue.poll().getPet();
                }
            } else if(catQue.isEmpty()){
                return dogQue.poll().getPet();
            } else if(dogQue.isEmpty()){
                return catQue.poll().getPet();
            }else{
                throw new RuntimeException("this DogCatQueue is empty");
            }
        }

        public Dog pollDog(){
            if(dogQue.isEmpty()){
                throw new RuntimeException("dog queue is empty");
            }else{
                return (Dog)dogQue.poll().getPet();
            }
        }

        public Cat pollCat(){
            if(catQue.isEmpty()){
                throw new RuntimeException("cat queue is empty");
            }else{
                return (Cat)catQue.poll().getPet();
            }
        }

        public boolean isEmpty(){
            return catQue.isEmpty() && dogQue.isEmpty();
        }

        public boolean isDogEmpty(){
            return dogQue.isEmpty();
        }

        public boolean isCatEmpty(){
            return catQue.isEmpty();
        }
 }  

7. 剑指 Offer 59 - II. 队列的最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

解题思路
定义两个队列,一个是queue,存放所有元素,另一个是helper,只存放最大值。push_back 时直接将元素放到queue中,如果helper为空的话,直接放入,不为空,就要将队列尾部小于此元素的元素全部移出,之后,将此元素放入队列尾部,这样,才能保证最大值。pop_front 时,如果queue中的队首元素和helper中的队首元素相等,就将helper队首元素移出,注意,这里要用equals,具体原因,可以参考。

class MaxQueue {
    Deque<Integer> queue;
    Deque<Integer> helper;
    public MaxQueue() {
        queue = new LinkedList<>();
        helper = new LinkedList<>(); 
    }
    
    public int max_value() {
        if(helper.isEmpty()) return -1;
        return helper.peek();
    }
    
    public void push_back(int value) {
        queue.offer(value);
        //判断队列的另一端是否有小于value的,有的话,全部移出
        while(!helper.isEmpty() && value > helper.peekLast()) {
            helper.pollLast();
        }
        helper.offer(value);
    }
    
    public int pop_front() {
        if(queue.isEmpty()) return -1;
        if(helper.peek().equals(queue.peek())) {
            helper.pop();
        }
        return queue.poll();
    }
}

如有不足之处,欢迎指正,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值