数据结构-栈和队列

栈和队列是一种更高级的线性数据结构。

栈的特性是后进先出,底层可以基于数组实现,也可以基于链表实现。

基于数组实现的栈

public class EArrayStack<T> {

    private T[] data;
    private int size;

    public EArrayStack(int capacity) {
        data = (T[])new Object[capacity];
        this.size = 0;
    }

    public void push(T e) {
        if (size == data.length) {
            throw new InternalException("stack has been full.");
        }
        data[size] = e;
        size ++;
    }

    public T pop() {
        if (size == 0) {
            throw new InternalException("stack empty");
        }
        T popElem = data[size - 1];
        size --;
        return popElem;
    }

    public T peek() {
        if (size == 0) {
            throw new InternalException("stack empty");
        }
        return data[size - 1];
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[ ");
        for (int i = 0;i < size;i ++) {
            sb.append(data[i]);
            if (i != size - 1) {
                sb.append(" -> ");
            }
        }
        sb.append(" ]");
        return sb.toString();
    }

    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(10);
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack);
        System.out.println(stack.peek());
        System.out.println(stack.pop());
        System.out.println(stack);
    }

}

基于链表实现的栈

在基于链表实现的栈中,利用dummyHead在头节点出进行操作,就很容易实现O(1)的入栈和出栈。

public class ELinkedStack <T>{
    
    private Node<T> dummyHead;
    private int size;

    public ELinkedStack() {
        dummyHead = new Node<>();
        this.size = 0;
    }

    //将新增节点插入到头节点的位置
    public void push(T e) {
        Node<T> newNode = new Node(e);
        newNode.next = dummyHead.next;
        dummyHead.next = newNode;
        size ++;
    }

    //从头节点弹出数据
    public T pop() {
        if (size == 0) {
            throw new InternalException("stack is empty");
        }
        Node<T> delNode = dummyHead.next;
        dummyHead.next = delNode.next;
        delNode.next = null;
        size --;
        return delNode.data;
    }

    public T peek() {
        if (size == 0) {
            throw new InternalException("stack is empty");
        }
        return dummyHead.next.data;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        Node<T> cur = dummyHead.next;
        while (cur != null) {
            sb.append(cur.data);
            if (cur.next != null) {
                sb.append(" -> ");
            }
            cur = cur.next;
        }
        sb.append(" ]");
        return sb.toString();
    }

    private static class Node<T> {
        T data;
        Node<T> next;

        public Node() {
            this(null);
        }

        public Node(T data) {
            this(data, null);
        }
        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
    }

    public static void main(String[] args) {
        LinkedStack<Integer> stack = new LinkedStack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);
        System.out.println(stack);
        System.out.println(stack.peek());
        System.out.println(stack);
        System.out.println(stack.pop());
        System.out.println(stack);
    }
}

队列

队列也是一个线性数据结构,所以底层也可以通过数组或链表实现。

基于循环数组实现的队列

/**
 * 用数组实现一个固定长度的队列
 * 空:front == end
 * 满:(end + 1) % length = front
 * 入队:data[(end + 1) % length] = element
 * 出队:data[(front + 1) % length]
 * */
public class EArrayRQueue<E> {

    private E[] data;
    private int size;
    
    private int front;
    private int end;
    
    //初始化
    public EArrayRQueue(int capacity) {
        data = (E[]) new Object[capacity];
        front = 0;
        end = 0;
    }
    
    public void enqueue(E e) {
        if ((end + 1) % data.length == front) {
            throw new IllegalArgumentException("queue is full");
        }
        data[end] = e;
        end = (end + 1) % data.length;
        size ++;
    }

    public E dequeue() {
        if (end == front) {
            throw new IllegalArgumentException("queue is empty.");
        }
        E dequeueEle = data[front];
        front = (front + 1) % data.length;
        size --;
        return dequeueEle;
    }

    public boolean isEmpty() {
        return front == end;
    }

    public int getSize() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        for (int i = front;
             i != end; i = (i + 1) % data.length) {
            sb.append(data[i]);
            sb.append(" <- ");
        }
        sb.append("]");
        return sb.toString();
    }

    public static void main(String[] args) {
        /**

         [ 1 <- 2 <- 3 <- ]
         1
         [ 2 <- 3 <- ]
         [ 2 <- 3 <- 10 <- ]

         * */
        EArrayRQueue<Integer> queue = new EArrayRQueue(4);
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);
        System.out.println(queue);
        System.out.println(queue.dequeue());
        System.out.println(queue);
        queue.enqueue(10);
        System.out.println(queue);
        System.out.println(queue.isEmpty()  );
    }
}

基于链表实现的队列

由于队列是一边进另一边出,所以利用dummyHead在头节点执行出对,在尾节点利用一个tail指针进行入队操作,这样入队和出队都可以将时间复杂度控制在O(1)。

/**
 * 基于链表实现的队列
 * 操作:入队、出队、遍历、打印
 * 思路:
 *  定义一个dummyHead
 *  定义一个tail,指向最后一个元素
 *  出队从链表头出
 *  入队从链表尾入,这样时间复杂度能够保证都是O(1)
 * */
public class ELinkedQueue<T> {

    private Node<T> dummyHead;
    private Node<T> tail;
    private int size;

    public ELinkedQueue() {

        /**
         初始状态
         dummyHead
             ^
            last

         * */
        dummyHead = new Node<>();
        tail = dummyHead;
        //dummyHead.next = tail;
        size = 0;
    }

    /**
     * 入队.
     * @param e
     * */
    public void enqueue(T e) {
        //利用last指针添加到尾部
        Node<T> newNode = new Node<>(e);
        newNode.next = tail.next;
        tail.next = newNode;
        tail = newNode;
        size ++;
    }

    /**
     * 出队.
     * @return T
     * */
    public T dequeue() {
        if (size == 0) {
            throw new InternalException("queue is empty.");
        }
        //出队,利用dummyHead直接移除头节点
        Node<T> delNode = dummyHead.next;
        dummyHead.next = delNode.next;
        delNode.next = null;
        size -- ;
        return delNode.data;
    }

    public boolean isEmpty() {
        return dummyHead.next == null;
    }

    public T peek() {
        if (size == 0) {
            throw new InternalException("queue is empty.");
        }
        return dummyHead.next.data;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        Node<T> cur = dummyHead.next;
        while (cur != null) {
            sb.append(cur.data);
            if (cur.next != null) {
                sb.append(" <- ");
            }
            cur = cur.next;
        }
        sb.append(" ]");
        return sb.toString();
    }
    
    private class Node<T> {
        T data;
        Node<T> next;

        public Node() {
            this(null);
        }

        public Node(T data) {
            this(data, null);
        }

        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
    }

    public static void main(String[] args) {

        /**

         [ 1 <- 2 <- 3 ]
         1
         2
         [ 2 <- 3 ]
         [ 2 <- 3 <- 100 ]
         2
         3
         100
         true

         * */

        LinkedRQueue<Integer> queue = new LinkedRQueue<>();
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);
        System.out.println(queue);
        System.out.println(queue.dequeue());
        System.out.println(queue.peek());
        System.out.println(queue);
        queue.enqueue(100);
        System.out.println(queue);
        System.out.println(queue.dequeue());
        System.out.println(queue.dequeue());
        System.out.println(queue.dequeue());
        System.out.println(queue.isEmpty());
    }
}

基于链表实现双端队列

利用一个双向链表和一个dummyHead以及一个dummuTail就能够轻松实现一个双端队列,并且在头尾入队和出队的操作的时间复杂度都是O(1)。

/**
 * 基于链表实现的双端队列
 * 双端队列:两边都能够进行入队和出兑
 * 实现:用双向链表实现
 *     持有两个指针 dummyHead  dummyTail
 *     由于是双向链表,所以在头尾操作的时间复杂度都是O(1)
 * 操作:头插、头出
 *      尾插、尾出
 *      遍历打印
 *      判空
 * */
public class ELinkedDeque<T> {

    //定义两个dummyHead
    private Node<T> dummyHead;
    private Node<T> dummyTail;
    private int size;

    public ELinkedDeque() {
        //初始状态,两个dummyNode互指
        dummyHead = new Node<>();
        dummyTail = new Node<>();
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
        size = 0;
    }

    /**
     * 在队头添加.
     * @param e
     * */
    public void addFirst(T e) {
        Node<T> newNode = new Node<>(e);
        newNode.next = dummyHead.next;
        newNode.prev = dummyHead;
        dummyHead.next.prev = newNode;
        dummyHead.next = newNode;
        size ++;
    }

    /**
     * 在队头移除.
     * @return T
     * */
    public T removeFirst() {
        if (dummyHead.next == dummyTail) {
            throw new IllegalArgumentException("queue is empty");
        }
        Node<T> delNode = dummyHead.next;
        dummyHead.next = delNode.next;
        delNode.next.prev = dummyHead;
        delNode.next = null;
        delNode.prev = null;
        size --;
        return delNode.data;
    }

    /**
     * 在队尾添加.
     * @param e
     * */
    public void addLast(T e) {
        Node<T> newNode = new Node<>(e);
        newNode.prev = dummyTail.prev;
        newNode.next = dummyTail;
        dummyTail.prev.next = newNode;
        dummyTail.prev = newNode;
        size ++;
    }

    /**
     * 在队尾移除
     * @return T
     * */
    public T removeLast() {
        if (dummyHead.next == dummyTail) {
            throw new IllegalArgumentException("queue is empty");
        }
        Node<T> delNode = dummyTail.prev;
        dummyTail.prev = delNode;
        delNode.prev.next = delNode.next;
        delNode.prev = null;
        delNode.next = null;
        size --;
        return delNode.data;
    }

    public boolean isEmpty() {
        return dummyHead.next == dummyTail;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        Node<T> cur = dummyHead.next;
        while (cur != dummyTail) {
            sb.append(cur.data);
            if (cur.next != dummyTail) {
                sb.append(" <- ");
            }
            cur = cur.next;
        }
        sb.append(" ]");
        return sb.toString();
    }

    //双向链表节点定义
    private class Node<T> {
        private T data;
        private Node<T> prev;
        private Node<T> next;

        public Node() {
            this(null);
        }

        public Node(T data) {
            this(data, null, null);
        }
        public Node(T data, Node<T> prev, Node<T> next) {
            this.data = data;
            this.prev = prev;
            this.next = next;
        }
    }

    public static void main(String[] args) {
        /**

         [ 2 <- 1 <- -1 ]
         2
         [ 1 <- -1 ]
         -1
         [ 1 ]
         1

         * */
        ELinkedDeque<Integer> deque = new ELinkedDeque<>();
        deque.addFirst(1);
        deque.addFirst(2);
        deque.addLast(-1);
        System.out.println(deque);
        System.out.println(deque.removeFirst());
        System.out.println(deque);
        System.out.println(deque.removeLast());
        System.out.println(deque);
        System.out.println(deque.removeFirst());
        System.out.println(deque.isEmpty());
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

echo20222022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值