介绍队列的定义,队列的构造与方法实现,循环队列以及双端队列的分别手撕实现;
队列定义: 队列是一种比较特殊的线性结构。它只允许在表的前端(front)进行删除 *** 作,而在表的后端(rear)进行插入 *** 作。进行插入 *** 作的端称为队尾,进行删除 *** 作的端称为队头。
队列中最先插入的元素也将最先被删除,对应的最后插入的元素将最后被删除。因此队列又称为“先进先出”(FIFO—first in first out)的线性表,与栈(FILO-first in last out)刚好相反,队列(Queue)是先进先出(FIFO)的,并且我们可以用数组、链表还有 List 的方式来实现自定义队列。
public interface Queueextends Collection { boolean add(E e); boolean offer(E e); E remove(); E poll(); E element(); E peek(); }
Queue接口与List、Set同一级别,都是继承了Collection接口。linkedList实现了Queue接 口。Queue接口窄化了对linkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 linkedList的非Queue的方法),以使得只有恰当的方法才可以使用。
Queue简单实现接口及其方法:先进者先出,这就是典型的“队列”。(First In, First Out,FIFO)。
我们知道,栈支持两个基本 *** 作:入栈push()和出栈pop()。队列跟栈非常相似,支持的 *** 作也很有限,最基本的 *** 作也是两个:入队和出队。入队 enqueue(),让一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。队列跟栈一样,也是一种 *** 作受限的线性表数据结构。跟栈类似,用数组实现的队列叫做顺序队列.用链表实现的队列叫做链式队列
public interface Queueextends Iterable { public void offer(E element); //入队 public E poll(); //出队 public E element(); //查看队首元素 public boolean isEmpty(); //判空 public void clear(); //清空 public int size(); //返回元素个数 }
import p1.接口.Queue; import java.util.Iterator; public class ArrayQueueimplements Queue { private ArrayList list; //通过ArrayList实现Queue //构造 public ArrayQueue() { list = new ArrayList<>(); } //offer方法 @Override public void offer(E element) { list.add(list.size(), element); } //poll方法 @Override public E poll() { return list.remove(0); } //element方法 @Override public E element() { return list.get(0); } //isEmpty方法 @Override public boolean isEmpty() { return list.isEmpty(); } //clear方法 @Override public void clear() { list.clear(); } //size方法 @Override public int size() { return list.size(); } //迭代 @Override public Iterator iterator() { return list.iterator(); } //toString方法 @Override public String toString() { return list.toString(); } //equals方法 @Override public boolean equals(Object o) { if (o == null) { return false; } if (this == o) { return true; } if (o instanceof ArrayQueue) { ArrayQueue other = (ArrayQueue) o; return list.equals(other.list); } return false; } }
Arraylist简易写法,用ArrayList 实现Queue可以进行参考;(代码如下:)
List接口:
import java.util.Comparator; //线性表接口定义 public interface Listextends Iterable { //默认在表尾添加一个元素 public void add(E element); //在指定角标处添加元素 public void add(int index, E element); //删除指定元素 public void remove(E element); //删除指定角标处的元素 并返回原先的值 public E remove(int index); //获取指定角标处的元素 public E get(int index); //修改指定角标index处的值为element 并返回原先的值 public E set(int index, E element); //获取线性表中的元素个数 public int size(); //查看元素第一次出现的角标位置(从左到右) public int indexOf(E element); //判断是否包含元素 public boolean contains(E element); //判断线性表是否为空 public boolean isEmpty(); //清空线性表 public void clear(); //按照比较器的内容进行排序 public void sort(Comparator c); //获取子线性表 原线性表中[fromIndex, toIndex]这个部分 public List subList(int fromIndex, int toIndex); }
ArrayList实现:
import java.util.Iterator; import java.util.List; public class ArrayListimplements List { private E[] data; private int size; private static int DEFAULT_CAPACITY = 10; public ArrayList() { data = (E[]) new Object[DEFAULT_CAPACITY]; size = 0; } public ArrayList(int capacity) { if (capacity <= 0) { throw new IllegalArgumentException("capacity must > 0"); } DEFAULT_CAPACITY = capacity; data = (E[]) new Object[DEFAULT_CAPACITY]; size = 0; } public ArrayList(E[] arr) { if (arr == null || arr.length == 0) { throw new IllegalArgumentException("arr can not be null"); } data = (E[]) new Object[DEFAULT_CAPACITY]; for (int i = 0; i < arr.length; i++) { add(arr[i]); } } @Override public void add(E element) { add(size, element); } @Override public void add(int index, E element) { if (index < 0 || index > size) { throw new IllegalArgumentException("add index out of range"); } if (size == data.length) { resize(2 * data.length); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = element; size++; } private void resize(int newLen) { E[] newData = (E[]) new Object[newLen]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; } @Override public void remove(E element) { int index = indexOf(element); if (index != -1) { remove(index); } } @Override public E remove(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("remove index out of range"); } E ret = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size--; if (size == data.length / 4 && data.length > DEFAULT_CAPACITY) { resize(data.length / 2); } return ret; } @Override public E get(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("get index out of range"); } return data[index]; } @Override public E set(int index, E element) { if (index < 0 || index >= size) { throw new IllegalArgumentException("set index out of range"); } E ret = data[index]; data[index] = element; return ret; } @Override public int size() { return size; } private int getCapacity() { return data.length; } @Override public int indexOf(E element) { for (int i = 0; i < size; i++) { if (data[i].equals(element)) { return i; } } return -1; } @Override public boolean contains(E element) { return indexOf(element) != -1; } @Override public boolean isEmpty() { return size == 0; } @Override public void clear() { data = (E[]) new Object[DEFAULT_CAPACITY]; size = 0; } @Override public void sort(Comparator c) { if (c == null) { throw new IllegalArgumentException("comparator can not be null"); } for (int i = 1; i < size; i++) { E e = data[i]; int j = 0; for (j = i; j > 0 && c.compare(data[j - 1], e) > 0; j--) { data[j] = data[j - 1]; } data[j] = e; } } @Override public List subList(int fromIndex, int toIndex) { if (fromIndex < 0 || toIndex >= size || fromIndex > toIndex) { throw new IllegalArgumentException("must 0 <= fromIndex <= toIndex <= size - 1"); } ArrayList list = new ArrayList<>(); for (int i = fromIndex; i <= toIndex; i++) { list.add(data[i]); } return list; } @Override public boolean equals(Object o) { if (o == null) { return false; } if (this == o) { return true; } if (o instanceof ArrayList) { ArrayList other = (ArrayList ) o; if (size != other.size) { return false; } for (int i = 0; i < size; i++) { if (!data[i].equals(other.data[i])) { return false; } } return true; } return false; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); if (isEmpty()) { sb.append(']'); } else { for (int i = 0; i < size; i++) { sb.append(data[i]); if (i == size - 1) { sb.append(']'); } else { sb.append(','); sb.append(' '); } } } return sb.toString(); } @Override public Iterator iterator() { return new ArrayListIterator(); } class ArrayListIterator implements Iterator { private int cur = 0; @Override public boolean hasNext() { return cur < size; } @Override public E next() { return data[cur++]; } } }
根据先前博客中的栈,以及本次的队列会发现二者有相似之处,即可以通过栈来实现队列;具体实现代码如下;
import 接口.Queue; import java.util.Iterator; //栈实现队列 public class StackToQueue { public static void main(String[] args) { QueueImplByStack使用Queue实现文件夹遍历:queue = new QueueImplByStack<>(); for (int i = 1; i <= 5; i++) { queue.offer(i); } System.out.println(queue); System.out.println(queue.poll()); System.out.println(queue); } } class QueueImplByStack implements Queue { private ArrayStack stackA; private ArrayStack stackB; public QueueImplByStack() { stackA = new ArrayStack<>(); stackB = new ArrayStack<>(); } @Override public void offer(E element) { stackA.push(element); } @Override public E poll() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } while (stackA.size() != 1) { stackB.push(stackA.pop()); } E ret = stackA.pop(); while (!stackB.isEmpty()) { stackA.push(stackB.pop()); } return ret; } @Override public E element() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } while (stackA.size() != 1) { stackB.push(stackA.pop()); } E ret = stackA.peek(); while (!stackB.isEmpty()) { stackA.push(stackB.pop()); } return ret; } @Override public boolean isEmpty() { return stackA.isEmpty(); } @Override public void clear() { stackA.clear(); } @Override public int size() { return stackA.size(); } @Override public Iterator iterator() { return stackA.iterator(); } @Override public String toString() { return stackA.toString(); } }
import java.io.File; //遍历文件 public class CheckFiles { public static void main(String[] args) { File dir = new File("C:\Users\Administrator\Desktop"); ArrayQueuequeue = new ArrayQueue<>(); queue.offer(dir); while(!queue.isEmpty()) { File file = queue.poll(); System.out.println("{" + file.getName() + "}"); File[] files = file.listFiles(); for(File f : files) { if(f.isFile()) { System.out.println( f.getName()); }else { queue.offer(f); } } } } }
对队列进行优化:
元素添加进来后,rear左移;如下图
从头部删除时,元素出对,front右移;如下图
然后再front 与rear的一直遍历下出现了下图所示:
这个时候,rear已经达到了队列的底部,我们会发现在队列前面有大量空出的位置,对队列的空间并没有完整的利用起来;造成了空间的大量浪费。那除了这样的方法之外,还有没有别的方法?答案是有,下面的情况就是一种。我们引出了循环队列的概念。
循环队列:与队列相同,循环队列也是先进新出,但是在java中,给定义一定大小的数组,当在队列中进行删除时,再从队尾进行添加时造成大量空间的浪费,为了减少队列中空间的浪费,我们给定循环队列来,即队列的尾部与队列的前端相连,形成一个类似环的队列。
简单来说:队尾的下一跳指向了队首。
从下标为0的地方新增一个元素入队;rear往右走,来到了队首
从原本的front的位置出队 *** 作时候,front往右移动一位;
在0的位置新增一个元素,rear从7移动到了0号位置;
在3的地方进行删除,front从3移动到4号位置;
对队列一直进行添加,知道满足下面的情况,我们会发现,rear+1 =front;
同时当删除到下图的时候,front=rear的位置;整个队列为空
奇怪的是,当我们的front移动到rear的左边时。同样会出现rear+1=front
列空和列满的条件竟然相同,这不就是逻辑bug嘛?
为了解决这个bug,我们给定一个null空间,让最后近乎满队的时候,rear指向那个null空间;
队列满时,(rear+1)% length =front;
队列空时,front==rear,此时就解决了刚刚的bug;
其中关于循环队列注意的事情:当初始化队列为空时,front = rear = 0;入队,rear+1,指向队尾的下一个存储单元,为了实现循环利用取模运算:rear = (rear+1) % max;出队,front+1,指向下一个队首,实现循环:front = (front+1) % max;判断队满,(rear+1) % length == front
Queue接口实现:
public interface Queueextends Iterable { public void offer(E element); public E poll(); public E element(); public boolean isEmpty(); public void clear(); public int size(); }
import p1.Queue; import java.util.Iterator; //循环队列 public class ArrayLoopQueue双端队列:implements Queue { private E[] data; //存储数据的容器 private int front; //队首指针(实际上就是数组中的索引角标) private int rear; //队尾指针 private int size; //元素的个数 (f < r r-f ; r < f r+L-f) private static int DEFAULT_CAPACITY = 10; //默认容量 public ArrayLoopQueue() { data = (E[]) new Object[DEFAULT_CAPACITY + 1]; front = 0; rear = 0; size = 0; } @Override public void offer(E element) { if ((rear + 1) % data.length == front) { resize(data.length * 2 - 1); } data[rear] = element; rear = (rear + 1) % data.length; size++; } @Override public E poll() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } E ret = data[front]; front = (front + 1) % data.length; size--; if (size <= (data.length - 1) / 4 && data.length - 1 > DEFAULT_CAPACITY) { resize(data.length / 2 + 1); } return ret; } private void resize(int newLen) { E[] newData = (E[]) new Object[newLen]; int index = 0; for (int i = front; i != rear; i = (i + 1) % data.length) { newData[index++] = data[i]; } data = newData; front = 0; rear = index; } @Override public E peek() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } return data[front]; } @Override public boolean isEmpty() { return front == rear; } @Override public void clear() { data = (E[]) new Object[DEFAULT_CAPACITY]; size = 0; front = 0; rear = 0; } @Override public int size() { return size; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); if (isEmpty()) { sb.append(']'); return sb.toString(); } for (int i = front; i != rear; i = (i + 1) % data.length) { sb.append(data[i]); if ((i + 1) % data.length == rear) { sb.append(']'); } else { sb.append(','); sb.append(' '); } } return sb.toString(); } @Override public boolean equals(Object o) { if (o == null) { return false; } if (this == o) { return true; } if (o instanceof ArrayLoopQueue) { ArrayLoopQueue other = (ArrayLoopQueue ) o; if (size != other.size) { return false; } int i = front; int j = other.front; while (i != rear) { if (!data[i].equals(other.data[j])) { return false; } i = (i + 1) % data.length; j = (j + 1) % other.data.length; } return true; } return false; } @Override public Iterator iterator() { return new ArrayLoopQueueIterator(); } class ArrayLoopQueueIterator implements Iterator { private int cur = front; @Override public boolean hasNext() { return cur != rear; } @Override public E next() { E ret = data[cur]; cur = (cur + 1) % data.length; return ret; } } }
一种可以在表头和表尾进行出队和入队的线性表,具有队列的性质,也可以理解为栈结构的变种。双端队列或 Deque 扩展队列以允许元件从两端插入和移除。Deque 类的实例表示双端队列,Deque 接口扩展了 Queue 接口,它声明了方便所有 *** 作的其他方法对于头部以及尾部的队列。它可以用作FIFO队列或LIFO队列。ArrayDeque 和 linkedList类是Deque接口的两个实现类。ArrayDeque 类由数组支持,而 linkedList 类由链表支持。如果您使用Deque作为堆栈,则应该使用 ArrayDeque 作为 Deque 实现。如果使用 Deque 作为FIFO队列, 可以使用linkedList。
同样的,双端队列很类似于我们的循环队列;此时
public interface Queueextends Iterable { public void offer(E element); public E poll(); public E element(); public boolean isEmpty(); public void clear(); public int size(); }
public interface Dequeueextends Queue { public void addFirst(E element); public void addLast(E element); public E removeFirst(); public E reomveLast(); public E getFirst(); public E getLast(); }
import 接口.Dequeue; import 接口.Stack; import java.util.Iterator; public class ArrayDequeimplements Dequeue , Stack { private E[] data; private int front; private int rear; private int size; private static int DEFAULT_CAPACITY = 10; public ArrayDeque() { data = (E[]) new Object[DEFAULT_CAPACITY + 1]; front = 0; rear = 0; size = 0; } @Override public void addFirst(E element) { if ((rear + 1) % data.length == front) { resize(data.length * 2 - 1); } front = (front - 1 + data.length) % data.length; data[front] = element; size++; } private void resize(int newLen) { E[] newData = (E[]) new Object[newLen]; int index = 0; for (int i = front; i != rear; i = (i + 1) % data.length) { newData[index++] = data[i]; } data = newData; front = 0; rear = index; } @Override public void addLast(E element) { if ((rear + 1) % data.length == front) { resize(data.length * 2 - 1); } data[rear] = element; rear = (rear + 1) % data.length; size++; } @Override public E removeFirst() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } E ret = data[front]; front = (front + 1) % data.length; size--; if (size <= (data.length - 1) / 4 && data.length - 1 > DEFAULT_CAPACITY) { resize(data.length / 2 + 1); } return null; } @Override public E reomveLast() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } rear = (rear - 1 + data.length) % data.length; E ret = data[rear]; size--; if (size <= (data.length - 1) / 4 && data.length - 1 > DEFAULT_CAPACITY) { resize(data.length / 2 + 1); } return ret; } @Override public E getFirst() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } return data[front]; } @Override public E getLast() { if (isEmpty()) { throw new IllegalArgumentException("queue is null"); } return data[(rear - 1 + data.length) % data.length]; } @Override public void offer(E element) { addLast(element); } @Override public E poll() { return removeFirst(); } @Override public E element() { return getFirst(); } @Override public E peek() { return getLast(); } @Override public boolean isEmpty() { return size == 0 && front == rear; } @Override public void push(E element) { addLast(element); } @Override public E pop() { return reomveLast(); } @Override public void clear() { E[] data = (E[]) new Object[DEFAULT_CAPACITY]; front = 0; rear = 0; size = 0; } @Override public int size() { return size; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); if (isEmpty()) { sb.append(']'); return sb.toString(); } for (int i = front; i != rear; i = (i + 1) % data.length) { sb.append(data[i]); if ((i + 1) % data.length == rear) { sb.append(']'); } else { sb.append(','); sb.append(' '); } } return sb.toString(); } @Override public Iterator iterator() { return new ArrayDequeIterator(); } class ArrayDequeIterator implements Iterator { private int cur = front; @Override public boolean hasNext() { return cur != rear; } @Override public E next() { E ret = data[cur]; cur = (cur + 1) % data.length; return ret; } } }
基于数组实现的双端队列(Deque)就可以解决上面这些问题,它让我们在数组开头结尾进行插入、删除元素时,平均时间复杂度做到 O(1)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)