1 队列
1 概念
- 队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。
- 图解:
2 代码实现(链表)
-
原理:
head为头节点,last指向最后一个节点,每次进行尾插法即插到last的后面,这样子就可以保证先进先出原则。 -
图解:
-
代码:
import java.util.Iterator; /** * @author JIN * @description 队列 * @createTime 2020-08-17 08:15 **/ public class Queue<T> implements Iterable<T> { //头节点 private Node head; //指向最后一个节点 private Node last; //记录最后一个节点 private int N; public Queue() { this.head = new Node(null, null); this.N = 0; this.last = null; } public boolean isEmpty() { return N == 0; } public int size() { return N; } public void add(T t) { //插入 分两种情况 //第一种 队列本身为空,需要给头节点的next赋值 //第二种 队列不为空,因此只需要在最后节点上插入 if (head.next == null) { head.next = last = new Node(t, null); } else { last = last.next = new Node(t, null); } N++; } public T get() { //判断队列本身是否为空,若是,直接返回null if (isEmpty()) { return null; } T temp = head.next.data; head.next = head.next.next; N--; return temp; } //节点类 class Node { private T data; private Node next; public Node(T data, Node next) { this.data = data; this.next = next; } } //下面的代码只是为了实现 foreach遍历 不重要 @Override public Iterator<T> iterator() { return new Iterator<T>() { private Node n = head; @Override public boolean hasNext() { return n.next != null; } @Override public T next() { n = n.next; return n.data; } }; } public static void main(String[] args) { Queue<String> queue = new Queue<>(); queue.add("a"); queue.add("b"); queue.add("c"); queue.add("d"); for (String s : queue) { System.out.println(s); } System.out.println("-----------"); System.out.println(queue.get()); System.out.println(queue.size()); } }
3 代码实现(数组)
-
图解:
-
代码实现
import java.util.Iterator; /** * @author JIN * @description 数组实现 队列 * @createTime 2020-08-17 08:52 **/ public class Queue2<T> implements Iterable<T>{ //数组用于存储数据 private T[] data; //记录实际存储的个数 private int N; //记录数组的容量 private int capacity; //指向队列头,用于记录那个数组先出列 private int front; //指向队列尾部,用于记录加入数据放在哪里 private int rear; public Queue2(){ this(8); } public Queue2(int size){ //因为泛型没办法直接创建数组,即 new T[],是不可以通过的, //因此需要先创建Object数组 再强转 this.data = (T[]) new Object[size]; this.N = 0; this.front = 0; this.rear = 0; this.capacity = size; } public boolean isEmpty(){ return N == 0; } public int size(){ return N; } public int getCapacity(){ return capacity; } //判断数组是否满了。 public boolean isFull(){ return (rear + 1) % capacity == front; } public void add(T t){ //若满了,进行扩容 if(isFull()){ int newCapacity = capacity << 1; resize(newCapacity); capacity = newCapacity; } data[rear] = t; rear = (rear + 1) % capacity; N++; } public T get(){ if(rear == front){ return null; } N--; T value = data[front]; front = (front + 1) % capacity; return value; } //扩容的思想很简单,创建一个容量是用来数组容量的2倍的新数组 //将旧数组的值 copy到新数组需要调整位置, //因为我们是环,具体在下面有个图说明。 public void resize(int size){ T[] olddata = data; this.data = (T[]) new Object[size]; int temp = front; int index = 0; while((temp % capacity) != rear){ data[index++] = olddata[temp]; temp = (temp + 1) % capacity; } front = 0; rear = index; } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int n = front; @Override public boolean hasNext() { return n % capacity != rear; } @Override public T next() { T value = data[n]; n = (n + 1) % capacity; return value; } }; } public static void main(String[] args) { Queue2<String> queue2 = new Queue2<>(5); queue2.add("a"); queue2.add("b"); queue2.add("c"); queue2.add("d"); System.out.println(queue2.get()); System.out.println(queue2.get()); queue2.add("e"); queue2.add("f"); queue2.add("g"); queue2.add("h"); queue2.add("i"); queue2.add("j"); System.out.println("-------------"); //System.out.println(queue2.get()); System.out.println("----------"); for(String s : queue2){ System.out.println(s); } } }
-
图解说明扩容方法:
- 若只是简单的复制,则会出现下面的情况,此时我需要再加入一个元素,注意,此时的maxcapacity是8,(rear + 1) % maxcapacity = 3 ,front 也为3,无法插入数据,此外,如果我们不插入数据,但是我们读数据呢,先读出a,(front + 1)%maxcapacity = 4,那读出来的是null,跟我们实际不符合,因此我们需要调整。
- 若只是简单的复制,则会出现下面的情况,此时我需要再加入一个元素,注意,此时的maxcapacity是8,(rear + 1) % maxcapacity = 3 ,front 也为3,无法插入数据,此外,如果我们不插入数据,但是我们读数据呢,先读出a,(front + 1)%maxcapacity = 4,那读出来的是null,跟我们实际不符合,因此我们需要调整。