定义:

  • 栈(stack)是限定仅在表尾进行插入和删除操作的线性表。

  • 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。

实现:

  • 用单链表实现栈:
/**
结点存储值
*/
public class Node<V>{
    public V value ;
    public Node<V> next;
    
    public Node<V v> {
        value = v;
        next = null;
    }
}

public class MyStack<V> {
    private Node<V> head;
    private int size;
    
    public MyStack(){
        head = null;
        size = 0;
    }
    
    public boolean isEmpty() {
		return size == 0;        
    }
    
    public int size(){
        return size;
    }
    
    public void push(V value){
        Node<V> cur = new Node<>(value);
        if(head == null){
            head = cur;
        } else {
            cur.next = head;
            head = cur;
        }
        size++;
    }
    
    public void pop(){
        V ans = null;
        if(head != null){
            ans = head.value;
            head = head.next;
            size--;
        }
        return ans;
    }
    
    public V peek(){
        return head != null? head.value : null;
    }
}

最小栈:

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

思路:

1. 实现两个栈,一个栈-stackData存放数据,另外一个栈-stackMin存放当前每一个元素对应的最小元素
 	1. 有两种放法:
     	1. 第一种:只存放当前栈中两个相邻范围内的最小值--stackData存放6,7,8,9,5,7,8,9这八个元素,则对应的stackMin则存放6,5这两个元素,减少空间
     	2. 第二种:每次存放当前元素对应的最小元素 ,--stackData存放6,7,8,9,5,7,8,9这八个元素,则对应的stackMin则存放6,6,6,6,5,5,5,5这8个元素
//放法一:
public class MyStack1{
    private Stack<Integer> stackData;
    private Stack<Integer> stackMin;
    
    public MyStack1(){
        this.stackData = new Stack<Integer>();
        this.stackMin = new Stack<Integer>();
    }
    
    //获取当前栈中最小值
    public int getMin(){
        if(this.stackMin.isEmpty()){
            throw new RunTimeException("Your stack is empty");
        }
        return this.stackMin.peek();
    }
    
    public void push(int val){
        this.stackData.push(val);
        if(this.stackMin.isEmpty()){
            this.stackMin.push(val);
        } else if (val <= this.getMin()) {
            this.stackMin.push(val);
        }
    }
    
    public int pop(){
        if(this.stackData.isEmpty()){
            throw new RunTimeException("Your stack is empty");
        }
        int val = this.stackData.pop();
        if(val == this.getMin()){
            this.stackMin.pop();
        }
        return val;
    }
}

//放法二
public class MyStack2{
    private Stack<Integer> stackData;
    private Stack<Integer> stackMin;
    
    public MyStack1(){
        this.stackData = new Stack<Integer>();
        this.stackMin = new Stack<Integer>();
    }
    
    //获取当前栈中最小值
    public int getMin(){
        if(this.stackMin.isEmpty()){
            throw new RunTimeException("Your stack is empty");
        }
        return this.stackMin.peek();
    }
    
    public void push(int val){
        this.stackData.push(val);
        if(this.stackMin.isEmpty()){
            this.stackMin.push(val);
        } else if (val <= this.getMin()) {
            this.stackMin.push(val);
        } else {
            this.stackMin.push(this.getMin());
        }
    }
    
    public int pop(){
        if(this.stackData.isEmpty()){
            throw new RunTimeException("Your stack is empty");
        }
        int val = this.stackData.pop();
        this.stackMin.pop();
        return val;
    }
}

队列

定义

  • 队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
  • 队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。

实现

一:用单向链表实现队列

public class MyQueue<V> {
    private Node<V> head;
    private NOde<V> tail;
    private int size;
    
    public MyQueue {
        head = null;
        tail = null;
        size = 0;
    }
    
    public boolean isEmpty(){
        return size == 0;
    }
    public int siez(){
        return size;
    }
    
    public void offer(V value) {
        NOde<V> cur = new NOde<V>(value);
        if(tail == null){
            head = cur;
            tail = cur;
        } else {
            tail.next =cur;
            tail = cur;
        }
        size++;
    }
    
    public V poll(){
        V ans = null;
        if(head != null){
            ans = head.value;
            head = head.next;
            size--;
        }
        if(head == null){
            tail = null;
        }
        return ans;
    }
    
    public V peek(){
        V ans = null;
        if(head != null){
            ans = head.value;
        }
        return ans;
    }
}

二:数组实现队列

思路:

  1. 声明两个指针,分别存储当前数组中pushi和popi的位置,声明一个变量size,存储当前队列的元素总个数
  2. pushi存放当前可以插入元素的存储位置,通过size判断,size = limit(arr.length),数组满了,则不能插入
    1. 插入完成后,pushi++,如果pushi再arr.length-1位置,则回到0位置
  3. popi存放当前可以弹出元素的位置,通过size判断,size=0,没有元素,则不能弹出
public class MyQueue{
    private int[] arr;
    private int size;
    private int pushi;
    private int popi;
    private final int limit;
    
    public MyQueue(int limit){
        arr[] = new int[limit];
        pushi = 0;
        popi = 0;
        size = 0;
        this.limit = limit;
    }
    
    //返回当前下标的下一个位置,数组末尾则直接到arr[0]的位置
    public nextIndex(int i){
        return i < limit - 1? i+1 : 0;
    }
    
    public void push(int val){
        if(size == limit){
            throw new RuntimeException("队列满了,不能再加了");
        }
        size++;
        arr[pushi] = val;
        pushi = nextIndex(pushi);
    }
    
    public int pop(){
        if(size == 0){
            throw new RuntimeException("队列满了,不能再加了");
        }
        size--;
        int val = arr[popi];
        popi = nextIndex(popi);
        return popi;
    }
    
    public boolean isEmpty(){
        return size == 0;
    }
}

双端队列

定义

双端队列(deque,即double-ended queue的缩写)是一种具有队列和栈性质的数据结构,即可(也只能)在线性表的两端进行插入和删除。

实现

​ 思路

1. 用双向链表实现双端队列,声明头指针-head和尾指针-tail,分别指向双向链表的头和尾节点
2. 两端插入数据,则是先根据head==null,判断当前队列有没有值
 	1. 没有,则直接将head和tail指向当前数据
 	2. 有数据,就在头节点的head节点之前插入一个节点cur,并将head指向cur
3. 两端删除数据,则是先根据head == null,判断当前队列有没有值
 	1. 没有,则返回null
 	2. 有,则将头节点的值返回,同时将head -> head.next,并将涉及到的节点的地址信息删除
public class Node<T>{
    public T value;
    public Node<T> next;
    public Node<T> last;
    
    public Node(T value){
        this.value = value
    };
}

public class DoubleEndsQueue<T>{
    private Node<T> head;
    private Node<T> tail;
    
    /**
    1.生成一个节点cur,如果当前队列只有一个节点,头尾两个节点都指向他
    2.cur.next -> head,head.last -> cur,head -> cur
    */
    public void addFromHead(T val){
        Node<T> cur = new Node<>(val);
        if(head == null){
            head = cur;
            tail = cur;
        } else {
            cur.next = head;
            head.last = cur;
            head = cur;
        }
    }
    
    public void addFromBottom(T val){
        Node<T> cur = new Node<>(val);
        if(tail == null){
            head = cur;
            tail = cur;
        } else {
            cur.last = tail;
            tail.next = cur;
            tail = cur;
        }
    }
    
    public T popFromHead(){
        if(head == null){
            return null;
        }
        Node<T> cur = head;
        if(head == tail){
            head = null;
            tail = null;
        } else {
            head = head.next;
            head.last = null;
            cur.next = null;
        }
        return cur.value;
    }
    
    public T popFromBottom(){
        if(head == null){
            return null;
        }
        Node<T> cur = tail;
        if(head == tail){
            head = null;
            tail = null;
        } else {
            tail = tail.last;
            cur.last = null;
            tail.next = null;
        }
        return cur.value;
    }
    
    public boolean isEmpty(){
        return head == null;
    }
}

双端队列实现栈和队列

栈:

思路:

  1. 栈因为后进先出的特性,只需要调用队列的addFromBottom和popFromBottom就可以完成相关功能
public class MyStack<T>{
    private DoubleEndsQueue<T> queue;
    
    public MyStack(){
        this.queue = new DoubleEndsQueue<T>();
    }
    
    public void push(T val){
        this.queue.addFromBottom(val);
    }
    
    public T pop(){
        return this.queue.popFromBottom();
    }
    
    public boolean isEmpty(){
        return this.queue.isEmpty();
    }
}

队列

思路:

  1. 直接用addFromBottom和popFromHead完成功能
public class MyQueue<T>{
	private DoubleEndsQueue<T> queue;
    
    public MyQueue(){
        this.queue = new DoubleEndsQueue<T>();
    }
    
    public void push(T val){
        this.queue.addFromHead(val);
    }
    
    public T pop(){
        return this.queue.popFromBottom();
    }
    
    public boolean isEmpty(){
        return this.queue.isEmpty();
    }
}

栈实现队列

思路:

  1. 栈是后进先出,队列是先进先出,可以先声明两个栈,一个栈-stackPush,一个栈-stackPop,
  2. 将stackPush中的元素全部倒入stackPop中,原本push栈中bottom中的元素就在pop栈中head的位置,就可以像队列一样弹出数据
public class MyQueue{
    private Stack<Integer> stackPush;
    private Stack<Integer> stackPop;
    
    //把push栈中的元素倒入pop栈中,将这个方法抽取出来,以后其他方法调用该方法后,都能确保stackPop有值,且不会叠加
    private void pushToPop(){
        if(stackPop.isEmpty()){
            while(!stackPush.isEmpty){
                stackPop.push(stackPush.pop());
            }
        }
    }
    
    public void add(int val){
        this.stackPush.push(val);
        this.pushToPop();
    }
    
    public int pop(){
        if(this.stackPush.isEmpty() && this.stackPop.isEmpty()){
            throw new RunTimeException("Your Queue is Empty");
        }
        this.pushToPop();
        return stackPop.pop();
    }
    
    public int peek(){
        if(this.stackPush.isEmpty() && this.stackPop.isEmpty()){
            throw new RunTimeException("Your Queue is Empty");
        }
        this.pushToPop();
        return stackPop.peek();
    }
}

队列实现栈

思路:

  1. 声明两个队列,一个队列-queue,一个队列-help
  2. 队列queue存放当前元素
  3. 返回当前栈的head元素,则将queue的size-1个元素全部导入到help,然后将queue的最后一个元素弹出,并将help和size互换
public class TwoQueueStack<T>{
    public Queue<T> queue;
    public Queue<T> help;
    
    public TwoQueueStack(){
        this.queue = new LinkedList();
        this.help = new LinkedList();
    }
    
    public void push(T val){
        queue.offer(val);
    }
    
    public T poll(){
        while(queue.size() > 1){
            help.offer(queue.poll());
        }
        T ans = queue.poll();
        Queue<T> tmp = queue;
        queue = help;
        help = tmp;
        return ans;
    }
    
    public T peek(){
        while(queue.size() > 1){
            help.offer(queue.poll());
        }
        T ans = queue.poll();
        help.offer(ans);
        Queue<T> tmp = queue;
        queue = help;
        help = tmp;
        return ans;
    }
}