Queue
队列(Queue
)是一种经常使用的集合。Queue
实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List
的区别在于,List
可以在任意位置添加和删除元素,而Queue
只有两个操作:
- 把元素添加到队列末尾;
- 从队列头部取出元素。
队列接口Queue
定义了以下几个方法:
int size()
:获取队列长度;boolean add(E)
/boolean offer(E)
:添加元素到队尾;E remove()
/E poll()
:获取队首元素并从队列中删除;E element()
/E peek()
:获取队首元素但并不从队列中删除。
throw Exception | 返回false或null | |
---|---|---|
添加元素到队尾 | add(E e) | boolean offer(E e) |
取队首元素并删除 | E remove() | E poll() |
取队首元素但不删除 | E element() | E peek() |
注意:不要把null
添加到队列中,否则poll()
方法返回null
时,很难确定是取到了null
元素还是队列为空。
LinkedList
即实现了List
接口,又实现了Queue
接口,但是,在使用的时候,如果我们把它当作List,就获取List的引用,如果我们把它当作Queue,就获取Queue的引用:
// 这是一个List:
List<String> list = new LinkedList<>();
// 这是一个Queue:
Queue<String> queue = new LinkedList<>();
Deque
是Queue的子接口 描述的是双端队列的操作 重点操作表的头尾
此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null
或 false
,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque
实现设计的;在大多数实现中,插入操作不能失败。
插入 | addFirst(e) | offerFirst(e) (E)) | addLast(e) (E)) | offerLast(e) (E)) |
---|---|---|---|---|
移除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | [peekFirst() | getLast() ) | peekLast() |
此接口扩展了 Queue
接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue
接口继承的方法完全等效于 Deque
方法,如下表所示:
Queue 方法 | 等效 Deque 方法 |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack
类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque
方法,如下表所示:
堆栈方法 | 等效 Deque 方法 |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
注意,在将双端队列用作队列或堆栈时,peek
方法同样正常工作;无论哪种情况下,都从双端队列的开头抽取元素。
虽然 Deque
实现没有严格要求禁止插入 null 元素,但建议最好这样做。建议任何事实上允许 null 元素的 Deque
实现用户最好不 要利用插入 null 的功能。这是因为各种方法会将 null
用作特殊的返回值来指示双端队列为空。
ArrayDeque
Deque
接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止 null 元素。此类很可能在用作堆栈时快于 Stack
,在用作队列时快于 LinkedList
。
底层数组实现 不同步 禁止null
其他功能和LinkedList一模一样
可以把类理解为数组版的LinkedList
PriorityQueue 优先队列
PriorityQueue
和Queue
的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue
调用remove()
或poll()
方法,返回的总是优先级最高的元素。
要使用PriorityQueue
,我们就必须给每个元素定义“优先级”。
放入PriorityQueue
的元素,必须实现Comparable
接口,PriorityQueue
会根据元素的排序顺序决定出队的优先级。
如果我们要放入的元素并没有实现Comparable
接口怎么办?PriorityQueue
允许我们提供一个Comparator
对象来判断两个元素的顺序。
底层数据结构最小堆结构(最大堆)
本质上就是一个二叉树
二分搜索树特点:任何一个结点的左孩子比你小 右孩子比你大 底层实现 链表实现
最小堆特点:任何一个结点都比左右两个孩子小 完全二叉树 底层实现 数组实现
自然排序 禁止null 不同步
实现最小堆和最大堆
最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。
//最小堆实现
PriorityQueue<Integer> queue = new PriorityQueue<>();
//最大堆实现
PriorityQueue<Integer> queue2 = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
Random random = new Random();
for (int i = 0; i < 10; i++) {
int num = random.nextInt(15);
queue.offer(num);
queue2.offer(num);
}
System.out.println(queue);
System.out.println(queue2);
System.out.println(queue.poll());
System.out.println(queue2.poll());
有一组N个元素的数组 求前K个大的元素
//思路一 全部降序排序 遍历前K个
//思路二 优先队列 先把所有元素放到优先队列(最大堆)中 出队K次
int[] arr = new int[1000];
Random random = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(10000);
}
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int i = 0; i < arr.length; i++) {
queue.offer(arr[i]);
}
for (int i = 0; i < 10; i++) {
System.out.println(queue.poll());
}
PriorityQueue
实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。
PriorityQueue
默认按元素比较的顺序排序(必须实现Comparable
接口),也可以通过Comparator
自定义排序算法(元素就不必实现Comparable
接口)。