在之前我们使用了二分搜索树实现了集合和映射,
从本节起将介绍4个不同树结构的例子
- 堆
- 线段树
- 字典树
- 并查集
优先队列本质就是队列,
所以优先队列可以使用队列的接口,只是在实现接口时,与普通队列有两处区别,
一处在于优先队列出队的元素应该是优先级最高的元素,另一处在于队首元素也是优先级最高的元素。
对于上面的2种线性结构,可以使用动态数组或链表这样的底层实现。
- 本节优先队列底层实现采用堆。
- 从上图可以看出,使用"堆"这种数据结构来实现优先队列是比较高效的。
8-2 堆的基础表示
- 二叉堆就是一棵满足特殊性质的二叉树
- 首先,二叉堆是一棵完全二叉树,"完全二叉树",不一定是满二叉树,不满的部分一定位于整棵树的右下侧。
- 其次,堆中根节点的值是最大的称为最大堆;相应的,堆中根节点的值是小于等于孩子节点的值的称为最小堆。
- 节点值的大小与其所处的层次没有必然联系,即,最大堆中,只需保证每个节点不大于其父节点即可,至于大不大于其父节点的兄弟节点,没有任何关系。
- 可以用数组来存储二叉堆,如下图所示:
8-3 向堆中添加元素和Sift Up
添加的52不满足堆的性质,要进行调整。(52大于16)
出问题只可能出现在52的父亲节点和52的父亲节点的父亲节点这一路上。
所以,要做的是:从52开始,依次和它的父亲节点做比较。
调整节点位置后,如下
这个过程就叫做Sift Up,节点的上浮过程。
1 // 实现add方法,向堆中添加元素 2 public void add(E e) { 3 data.addLast(e);//向整个数组的末尾添加上元素,由于data是我们之前实现的动态数组Array,所以不需要管容积的问题 4 SiftUp(data.getSize() - 1);//维护堆的性质,传入的参数表示:希望上浮的元素对应的索引是谁, 5 //这里是:新加入的元素e所在的索引,也就是整个数组中最后一个元素所在的索引. 6 //getSize()获取数组长度,在Array类中实现 7 } 8 9 // 实现元素的上浮 10 private void SiftUp(int k) { //k是索引,k=0表示根节点的索引 11 while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {//get()获取index索引位置的元素,parent(k)表示父亲节点的索引 12 data.swap(k, parent(k));//如果父亲节点的元素值小于我们要加入的元素值,交互两个数,实现上浮。 13 k = parent(k);//更新要加入元素的索引值。 14 } 15 }
8-4 从堆中取出元素和Sift Down
从堆中取出元素只能取出堆中最大的元素,
取出最大元素62,把最底部元素放入索引为0的位置,变成:
再调整,执行sift Down(下沉),下沉元素每次都和它的孩子元素比较,和2个孩子中最大的元素交换位置(如果两个孩子比它大)。
下沉代码
1 // 实现findMax方法,查看堆中的最大元素 2 public E findMax() { 3 if (data.getSize() == 0) { 4 throw new IllegalArgumentException("Can not findMax when heap is empty."); 5 } 6 return data.get(0); 7 } 8 9 // 实现extractMax方法,取出堆中的最大元素 10 public E extractMax() { 11 E ret = findMax(); 12 //删除堆中最大元素 13 data.swap(0, data.getSize() - 1);//将索引为0的元素和末尾元素交换 14 data.removeLast(); 15 SiftDown(0);//下沉 16 //--删除操作结束--- 17 return ret;//取出堆中的最大元素,返回 18 } 19 20 // 实现元素的下沉 21 private void SiftDown(int k) { 22 while (leftChild(k) < data.getSize()) {//k的左孩子的索引如果越界了,就跳出while。即K所在的节点都已经没有孩子了, 23 //肯定要结束循环。注:右孩子对应的索引比左孩子要大。 24 int j = leftChild(k);//左孩子所在的索引。j+1:右孩子所对应的索引 //get()获取index索引位置的元素 25 if (j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j)) > 0) { 26 j = rightChild(k);//如果右孩子存在,并且对应的节点值大于左孩子的值,执行该步,此时j存储的是右孩子对应的索引 27 // data[j]是leftChild和rightChild中的对大值 28 } 29 if (data.get(k).compareTo(data.get(j)) >= 0) { 30 break;//比较索引k的元素值大于它孩子的值,结束。 31 } else { //比较索引k的元素值小于它孩子的值, 32 data.swap(k, j);//元素互换 33 k = j;//更新下沉元素索引 34 } 35 } 36 }
- 总的用动态数组实现二叉堆的业务逻辑如下:
1 public class MaxHeap<E extends Comparable<E>> { 2 3 private Array<E> data = new Array<>(); 4 5 // 构造函数 6 public MaxHeap(int capacity) { 7 data = new Array<>(capacity); 8 } 9 10 // 无参数构造函数 11 public MaxHeap() { 12 data = new Array<>(); 13 } 14 15 // 接收参数为数组的构造函数 16 public MaxHeap(E[] arr) { 17 data = new Array<>(arr); 18 for (int i = parent(arr.length - 1); i >= 0; i--) { 19 SiftDown(i); 20 } 21 } 22 23 // 实现getSize方法,返回堆中的元素个数 24 public int getSize() { 25 return data.getSize(); 26 } 27 28 // 实现isEmpty方法,返回堆是否为空 29 public boolean isEmpty() { 30 return data.isEmpty(); 31 } 32 33 // 返回完全二叉树的数组表示中,一个索引所表示的元素的父节点的索引 34 private int parent(int index) { 35 if (index == 0) { 36 throw new IllegalArgumentException("Index-0 doesn\'t have parent."); 37 } 38 return (index - 1) / 2; 39 } 40 41 // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子的索引 42 private int leftChild(int index) { 43 return index * 2 + 1; 44 } 45 46 // 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子的索引 47 private int rightChild(int index) { 48 return index * 2 + 2; 49 } 50 51 // 实现add方法,向堆中添加元素 52 public void add(E e) { 53 data.addLast(e);//向整个数组的末尾添加上元素,由于data是我们之前实现的动态数组Array,所以不需要管容积的问题 54 SiftUp(data.getSize() - 1);//维护堆的性质,传入的参数表示:希望上浮的元素对应的索引是谁, 55 //这里是:新加入的元素e所在的索引,也就是整个数组中最后一个元素所在的索引. 56 //getSize()获取数组长度,在Array类中实现 57 } 58 59 // 实现元素的上浮 60 private void SiftUp(int k) { //k是索引,k=0表示根节点的索引 61 while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {//get()获取index索引位置的元素,parent(k)表示父亲节点的索引 62 data.swap(k, parent(k));//如果父亲节点的元素值小于我们要加入的元素值,交互两个数,实现上浮。 63 k = parent(k);//更新要加入元素的索引k的值。 64 } 65 } 66 67 // 实现findMax方法,查看堆中的最大元素 68 public E findMax() { 69 if (data.getSize() == 0) { 70 throw new IllegalArgumentException("Can not findMax when heap is empty."); 71 } 72 return data.get(0); 73 } 74 75 // 实现extractMax方法,取出堆中的最大元素 76 public E extractMax() { 77 E ret = findMax(); 78 //删除堆中最大元素 79 data.swap(0, data.getSize() - 1);//将索引为0的元素和末尾元素交换 80 data.removeLast(); 81 SiftDown(0);//下沉 82 //--删除操作结束--- 83 return ret;//取出堆中的最大元素,返回 84 } 85 86 // 实现元素的下沉 87 private void SiftDown(int k) { 88 while (leftChild(k) < data.getSize()) {//k的左孩子的索引如果越界了,就跳出while。即K所在的节点都已经没有孩子了, 89 //肯定要结束循环。注:右孩子对应的索引比左孩子要大。 90 int j = leftChild(k);//左孩子所在的索引。j+1:右孩子所对应的索引 //get()获取index索引位置的元素 91 if (j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j)) > 0) { 92 j = rightChild(k);//如果右孩子存在,并且对应的节点值大于左孩子的值,执行该步,此时j存储的是右孩子对应的索引 93 // data[j]是leftChild和rightChild中的对大值 94 } 95 if (data.get(k).compareTo(data.get(j)) >= 0) { 96 break;//比较索引k的元素值大于它孩子的值,结束。 97 } else { //比较索引k的元素值小于它孩子的值, 98 data.swap(k, j);//元素互换 99 k = j;//更新下沉元素索引k的值 100 } 101 } 102 } 103 104 105 }
- 测试用动态数组实现的二叉堆
1 import java.util.Random; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 7 int n = 1000000; 8 MaxHeap<Integer> maxHeap = new MaxHeap<>(); 9 Random random = new Random(); 10 for (int i = 0; i < n; i++) { 11 maxHeap.add(random.nextInt(Integer.MAX_VALUE));//添加在(0,Integer的最大值)范围内的n个随机数到maxHeap中 12 } 13 14 int[] arr = new int[n]; 15 for (int i = 0; i < n; i++) { 16 arr[i] = maxHeap.extractMax();//将n个元素按最大堆的extractMax()方法取出,相当于对n个数进行从大到小排序, 17 } 18 19 for (int i = 1; i < n; i++) {//验证extractMax()方法 20 if (arr[i - 1] < arr[i]) { 21 throw new IllegalArgumentException("Error"); 22 } 23 } 24 25 System.out.println("Test MaxHeap completed."); 26 } 27 }
8-5 Heapify 和 Replace
一、replace
定义:取出最大元素后,放入一个新元素【堆中总数没有变化】
实现方法:1.可以先 extractMax,再 add,两次O(log n)的操作;
2.可以直接将堆顶元素替换以后 Sift Down,一次 O(log n)的操作;
1 // 实现replace方法,取出堆中的最大元素,并替换为元素e 2 public E replace(E e) { 3 E ret = findMax();//取出堆中的最大元素ret 4 data.set(0, e);//将堆顶元素换成要替换的元素 5 SiftDown(0);//新的堆顶元素可能违背堆的性质,进行SiftDown 6 return ret;//查看堆中的最大元素ret 7 }
定义:将任意数组整理成堆的形状;
方法:将当前数组看做完全二叉树,从当前最后一个非叶子节点开始;即图中的 22 ;【最后一个非叶子节点的索引:拿到最后一个叶子节点,根据这个叶子节点来计算其父亲节点的索引即可】
从 22 开始,不断进行下沉操作
索引为3 的是13,对其进行下沉操作,交换 41 和 13;
索引为 2 的是19,对其进行下沉操作
对索引为 1 、0 的继续进行下沉,得到最终的二叉树
实现
public class Array<E> {}中添加
1 public Array(E[] arr){ //本节新添加,Array支持用户传来一个数组arr,根据arr,生成一个动态数组 2 data = (E[])new Object[arr.length]; 3 for(int i = 0 ; i < arr.length ; i ++) 4 data[i] = arr[i]; 5 size = arr.length; 6 }
public class MaxHeap<E extends Comparable<E>> {}中添加
1 public MaxHeap(E[] arr){//传入数组arr,将arr数组转化成堆的形状,并且放入data对应的动态数组中 2 data = new Array<>(arr);//Array支持用户传来一个数组arr,根据arr,生成一个动态数组,data中存放arr所有元素 3 for(int i = parent(arr.length - 1) ; i >= 0 ; i --)//parent(arr.length - 1):最后一个节点的父节点。即从最后一个非叶子节点开始 4 siftDown(i);//每次对i个节点进行下沉操作。 5 }
8-6 基于堆的优先队列
首先我们的队列仍然需要继承我们之前讲队列时候声明的那个接口Queue,然后实现这个接口中的方法就可以了,之类简单写一下:
Queue.java
1 public interface Queue<E> { 2 3 int getSize(); 4 boolean isEmpty(); 5 void enqueue(E e); 6 E dequeue(); 7 E getFront(); 8 }
实现优先队列的业务逻辑如下
1 public class PriorityQueue<E extends Comparable<E>> implements Queue<E> { //E:泛型,优先队列必须可比较,要实现Comparable接口。 2 //PriorityQueue实现了Queue接口 3 4 private MaxHeap<E> maxHeap; 5 6 public PriorityQueue(){ 7 maxHeap = new MaxHeap<>(); 8 } 9 10 @Override 11 public int getSize(){ 12 return maxHeap.size(); 13 } 14 15 @Override 16 public boolean isEmpty(){ 17 return maxHeap.isEmpty(); 18 } 19 20 @Override 21 public E getFront(){//获取队首元素 22 return maxHeap.findMax();//实际上,就是获取最大堆堆顶的元素 23 } 24 25 @Override 26 public void enqueue(E e){//入队操作,添加元素e 27 maxHeap.add(e); 28 } 29 30 @Override 31 public E dequeue(){//出队操作 32 return maxHeap.extractMax();//提出最大值 33 } 34 }
8-8 Java中的PriorityQueue
二、优先队列的经典问题
在100 0000 个元素中选出前 100 个元素【在N个元素中选出前M个元素,M<<N】
解决方法:
1.使用高级排序(归并等)时间复杂度为 NlogN;
2.使用优先队列时间复杂度为 NlogM;(M<<N)
使用优先队列方法思路:【使用最小堆】
使用优先队列维护当前看到的前M个元素,将N个元素中的前M个元素放入优先队列中,之后每看到一个新的元素,如果这个新的元素比优先队列中最小的元素还要大的话,就用这个新的元素取代优先队列中的最小元素;直到遍历完所有N个元素,这时留在其中的M个元素就是我们所要找的M个元素。
题目链接
https://leetcode-cn.com/problems/top-k-frequent-elements/description/
求频次,之前将映射,可以使用底层基于二分搜索树的Map类来完成,这里我们采用
TreeMap类
1 /// 347. Top K Frequent Elements 2 /// https://leetcode.com/problems/top-k-frequent-elements/description/ 3 4 import java.util.LinkedList; 5 import java.util.List; 6 import java.util.TreeMap; 7 8 class Solution { 9 10 private class Array<E> { 11 12 private E[] data; 13 private int size; 14 15 // 构造函数,传入数组的容量capacity构造Array 16 public Array(int capacity){ 17 data = (E[])new Object[capacity]; 18 size = 0; 19 } 20 21 // 无参数的构造函数,默认数组的容量capacity=10 22 public Array(){ 23 this(10); 24 } 25 26 public Array(E[] arr){ 27 data = (E[])new Object[arr.length]; 28 for(int i = 0 ; i < arr.length ; i ++) 29 data[i] = arr[i]; 30 size = arr.length; 31 } 32 33 // 获取数组的容量 34 public int getCapacity(){ 35 return data.length; 36 } 37 38 // 获取数组中的元素个数 39 public int getSize(){ 40 return size; 41 } 42 43 // 返回数组是否为空 44 public boolean isEmpty(){ 45 return size == 0; 46 } 47 48 // 在index索引的位置插入一个新元素e 49 public void add(int index, E e){ 50 51 if(index < 0 || index > size) 52 throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size."); 53 54 if(size == data.length) 55 resize(2 * data.length); 56 57 for(int i = size - 1; i >= index ; i --) 58 data[i + 1] = data[i]; 59 60 data[index] = e; 61 62 size ++; 63 } 64 65 // 向所有元素后添加一个新元素 66 public void addLast(E e){ 67 add(size, e); 68 } 69 70 // 在所有元素前添加一个新元素 71 public void addFirst(E e){ 72 add(0, e); 73 } 74 75 // 获取index索引位置的元素 76 public E get(int index){ 77 if(index < 0 || index >= size) 78 throw new IllegalArgumentException("Get failed. Index is illegal."); 79 return data[index]; 80 } 81 82 // 修改index索引位置的元素为e 83 public void set(int index, E e){ 84 if(index < 0 || index >= size) 85 throw new IllegalArgumentException("Set failed. Index is illegal."); 86 data[index] = e; 87 } 88 89 // 查找数组中是否有元素e 90 public boolean contains(E e){ 91 for(int i = 0 ; i < size ; i ++){ 92 if(data[i].equals(e)) 93 return true; 94 } 95 return false; 96 } 97 98 // 查找数组中元素e所在的索引,如果不存在元素e,则返回-1 99 public int find(E e){ 100 for(int i = 0 ; i < size ; i ++){ 101 if(data[i].equals(e)) 102 return i; 103 } 104 return -1; 105 } 106 107 // 从数组中删除index位置的元素, 返回删除的元素 108 public E remove(int index){ 109 if(index < 0 || index >= size) 110 throw new IllegalArgumentException("Remove failed. Index is illegal."); 111 112 E ret = data[index]; 113 for(int i = index + 1 ; i < size ; i ++) 114 data[i - 1] = data[i]; 115 size --; 116 data[size] = null; // loitering objects != memory leak 117 118 if(size == data.length / 4 && data.length / 2 != 0) 119 resize(data.length / 2); 120 return ret; 121 } 122 123 // 从数组中删除第一个元素, 返回删除的元素 124 public E removeFirst(){ 125 return remove(0); 126 } 127 128 // 从数组中删除最后一个元素, 返回删除的元素 129 public E removeLast(){ 130 return remove(size - 1); 131 } 132 133 // 从数组中删除元素e 134 public void removeElement(E e){ 135 int index = find(e); 136 if(index != -1) 137 remove(index); 138 } 139 140 public void swap(int i, int j){ 141 142 if(i < 0 || i >= size || j < 0 || j >= size) 143 throw new IllegalArgumentException("Index is illegal."); 144 145 E t = data[i]; 146 data[i] = data[j]; 147 data[j] = t; 148 } 149 150 @Override 151 public String toString(){ 152 153 StringBuilder res = new StringBuilder(); 154 res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length)); 155 res.append(\'[\'); 156 for(int i = 0 ; i < size ; i ++){ 157 res.append(data[i]); 158 if(i != size - 1) 159 res.append(", "); 160 } 161 res.append(\']\'); 162 return res.toString(); 163 } 164 165 // 将数组空间的容量变成newCapacity大小 166 private void resize(int newCapacity){ 167 168 E[] newData = (E[])new Object[newCapacity]; 169 for(int i = 0 ; i < size ; i ++) 170 newData[i] = data[i]; 171 data = newData; 172 } 173 } 174 175 private class MaxHeap<E extends Comparable<E>> { 176 177 private Array<E> data; 178 179 public MaxHeap(int capacity){ 180 data = new Array<>(capacity); 181 } 182 183 public MaxHeap(){ 184 data = new Array<>(); 185 } 186 187 public MaxHeap(E[] arr){ 188 data = new Array<>(arr); 189 for(int i = parent(arr.length - 1) ; i >= 0 ; i --) 190 siftDown(i); 191 } 192 193 // 返回堆中的元素个数 194 public int size(){ 195 return data.getSize(); 196 } 197 198 // 返回一个布尔值, 表示堆中是否为空 199 public boolean isEmpty(){ 200 return data.isEmpty(); 201 } 202 203 // 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引 204 private int parent(int index){ 205 if(index == 0) 206 throw new IllegalArgumentException("index-0 doesn\'t have parent."); 207 return (index - 1) / 2; 208 } 209 210 // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引 211 private int leftChild(int index){ 212 return index * 2 + 1; 213 } 214 215 // 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引 216 private int rightChild(int index){ 217 return index * 2 + 2; 218 } 219 220 // 向堆中添加元素 221 public void add(E e){ 222 data.addLast(e); 223 siftUp(data.getSize() - 1); 224 } 225 226 private void siftUp(int k){ 227 228 while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){ 229 data.swap(k, parent(k)); 230 k = parent(k); 231 } 232 } 233 234 // 看堆中的最大元素 235 public E findMax(){ 236 if(data.getSize() == 0) 237 throw new IllegalArgumentException("Can not findMax when heap is empty."); 238 return data.get(0); 239 } 240 241 // 取出堆中最大元素 242 public E extractMax(){ 243 244 E ret = findMax(); 245 246 data.swap(0, data.getSize() - 1); 247 data.removeLast(); 248 siftDown(0); 249 250 return ret; 251 } 252 253 private void siftDown(int k){ 254 255 while(leftChild(k) < data.getSize()){ 256 int j = leftChild(k); // 在此轮循环中,data[k]和data[j]交换位置 257 if( j + 1 < data.getSize() && 258 data.get(j + 1).compareTo(data.get(j)) > 0 ) 259 j ++; 260 // data[j] 是 leftChild 和 rightChild 中的最大值 261 262 if(data.get(k).compareTo(data.get(j)) >= 0 ) 263 break; 264 265 data.swap(k, j); 266 k = j; 267 } 268 } 269 270 // 取出堆中的最大元素,并且替换成元素e 271 public E replace(E e){ 272 273 E ret = findMax(); 274 data.set(0, e); 275 siftDown(0); 276 return ret; 277 } 278 } 279 280 private interface Queue<E> { 281 282 int getSize(); 283 boolean isEmpty(); 284 void enqueue(E e); 285 E dequeue(); 286 E getFront(); 287 } 288 289 private class PriorityQueue<E extends Comparable<E>> implements Queue<E> { 290 291 private MaxHeap<E> maxHeap; 292 293 public PriorityQueue(){ 294 maxHeap = new MaxHeap<>(); 295 } 296 297 @Override 298 public int getSize(){ 299 return maxHeap.size(); 300 } 301 302 @Override 303 public boolean isEmpty(){ 304 return maxHeap.isEmpty(); 305 } 306 307 @Override 308 public E getFront(){ 309 return maxHeap.findMax(); 310 } 311 312 @Override 313 public void enqueue(E e){ 314 maxHeap.add(e); 315 } 316 317 @Override 318 public E dequeue(){ 319 return maxHeap.extractMax(); 320 } 321 } 322 323 private class Freq implements Comparable<Freq>{//创建类Freq,Freq必须是可以比较的,所以要加上Comparable<Freq> 324 325 public int e, freq;//元素e,元素e对应的频次freq 326 327 public Freq(int e, int freq){ 328 this.e = e; 329 this.freq = freq; 330 } 331 332 @Override //上面采用了implements Comparable<Freq>,就必须定义compareTo()方法 333 public int compareTo(Freq another){//Freq another指另一个元素对应的类对象 334 if(this.freq < another.freq)//如果this.freq的频次低 335 return 1;//compareTo()的返回值,表示this.freq的优先级高 336 else if(this.freq > another.freq) 337 return -1; 338 else 339 return 0; 340 } 341 } 342 343 public List<Integer> topKFrequent(int[] nums, int k) {//传入int型数组nums和k,将前k个频次最高的元素放进一个List里,然后返回这些元素 344 //-------------统计频次---------------// 345 TreeMap<Integer, Integer> map = new TreeMap<>();//TreeMap这个类,统计数组中各个元素出现的频次。<Integer是元素, Integer是频次> 346 for(int num: nums){ 347 if(map.containsKey(num))//如果containsKey已经包含了num, 348 map.put(num, map.get(num) + 1);//get(num)获取num频次,再加1. 349 else 350 map.put(num, 1);//否则,我们是第一次见到num,给定频次1. 351 } 352 //--------统计频次结束-----------// 353 //------------利用优先队列求出前k个元素----------------------// 354 PriorityQueue<Freq> pq = new PriorityQueue<>();//pq是Freq类型的优先级队列,Freq类型有构造元素e,元素e对应的频次freq 355 for(int key: map.keySet()){//对所有的键进行遍历, 356 if(pq.getSize() < k)//如果当前的优先队列元素数是小于k的,也就是还没有存够k个元素 357 pq.enqueue(new Freq(key, map.get(key)));//入队一个Freq对象,map.get(key)获取元素Key的频率。 358 else if(map.get(key) > pq.getFront().freq){//当前新遍历到的元素频次大于队首元素的频次 359 pq.dequeue();//优先队列队首元素出队。 360 pq.enqueue(new Freq(key, map.get(key)));//再进队一个元素key, map.get(key) 361 } 362 } 363 //-----------利用优先队列求出前k个元素----结束-------------// 364 365 //-----------然后将优先队列中的元素放入队列中---------------// 366 LinkedList<Integer> res = new LinkedList<>();//初始化一个LinkedList对象,里面存放的是Integer 367 while(!pq.isEmpty()) 368 res.add(pq.dequeue().e);//添加一个新的元素,这个元素就是优先队列出队的队首元素的e。 369 return res; 370 } 371 372 private static void printList(List<Integer> nums){ 373 for(Integer num: nums) 374 System.out.print(num + " "); 375 System.out.println(); 376 } 377 378 public static void main(String[] args) { 379 380 int[] nums = {1, 1, 1, 2, 2, 3}; 381 int k = 2; 382 printList((new Solution()).topKFrequent(nums, k)); 383 } 384 }
参考:https://www.cnblogs.com/wmyskxz/p/9301021.html
https://blog.csdn.net/jianghao233/article/details/82752826