隊列與棧兩種數據結構是老生常談的問題了,其中隊列是先進先出(FIFO),棧是先進後出(FILO),明白這兩個特性,就很簡易理解了。但是有一種特殊的隊列也是我們常用的數據結構——優先級隊列。
優先隊列的作用是能保證每次取出的元素都是隊列中權值最小(大)的,Java中PriorityQueue實現了Queue接口,不允許放入null元素;其通過堆實現,具體說是通過完全二叉樹(complete binary tree)實現的小頂堆(任意一個非葉子節點的權值,都不大於其左右子節點的權值),也就意味着可以通過數組來作爲PriorityQueue的底層實現。如下圖所式:
下面着重兩個方法說明,添加元素與移除元素。
添加元素時,通過siftUp(int k, E x);方法維持特性。
1public boolean offer(E e) {
2 if (e == null)//不允許放入null元素
3 throw new NullPointerException();
4 modCount++;
5 int i = size;
6 if (i >= queue.length)
7 grow(i + 1);//自動擴容
8 size = i + 1;
9 if (i == 0)//隊列原來爲空,這是插入的第一個元素
10 queue[0] = e;
11 else
12 siftUp(i, e);//調整
13 return true;
14}
15
16private void siftUp(int k, E x) {
17 while (k > 0) {
18 int parent = (k - 1) >>> 1;//parentNo = (nodeNo-1)/2
19 Object e = queue[parent];
20 if (comparator.compare(x, (E) e) >= 0)//調用比較器的比較方法
21 break;
22 queue[k] = e;
23 k = parent;
24 }
25 queue[k] = x;
26}
k爲數組最大下標值。 看下圖所示:
1//代碼運行過程
2-> x = 4;
3-> k = 10;
4-> parent = (k-1)/2 = 4.5 === 4;
5-> e = 9;
6-> compare(4, 9) = -1 < 0;
7-> queue[10] = 9;
8-> k = 4;
9-> parent = (k-1)/2 = 1.5 === 1;
10-> e = 5;
11-> compare(4,5) = -1 < 0;
12-> queue[4] = 5;
13-> k=1;
14-> parent = (k - 1) / 2 = 0 / 2 === 0;
15-> e = 3;
16-> compare(4, 3) = 1 > 0;
17//跳出while循環
18-> queue[1] = x => queue[1] = 4;
至此,添加元素的過程結束。
再看一下poll過程,poll過程是通過siftDown(int k , E e);來維持特性。
1public E poll() {
2 if (size == 0)
3 return null;
4 int s = --size;
5 modCount++;
6 E result = (E) queue[0];//0下標處的那個元素就是最小的那個
7 E x = (E) queue[s];
8 queue[s] = null;
9 if (s != 0)
10 siftDown(0, x);//調整
11 return result;
12}
13
14private void siftDown(int k, E x) {
15 int half = size >>> 1;
16 while (k < half) {
17 //首先找到左右孩子中較小的那個,記錄到c裏,並用child記錄其下標
18 int child = (k << 1) + 1;//leftNo = parentNo*2+1
19 Object c = queue[child];
20 int right = child + 1;
21 if (right < size &&
22 comparator.compare((E) c, (E) queue[right]) > 0)
23 c = queue[child = right];
24 if (comparator.compare(x, (E) c) <= 0)
25 break;
26 queue[k] = c;//然後用c取代原來的值
27 k = child;
28 }
29 queue[k] = x;
30}
k的初始值爲0,x爲最後一個元素。
過程如圖所示:
1//代碼運行過程
2-> k = 0;
3-> x = 9;
4-> half = 10 / 2 = 5;
5-> child = 0 + 1 = 1;
6-> c = 4;
7-> right = child + 1 = 2;
8// 2<10 && compare(4, 10)<0第一個判斷不成立
9-> compare(9, 4) > 0;
10-> queue[0] = 4;
11-> k = 1;
12-> child = 2 * 1 + 1 = 3;
13-> c = 7;
14-> right = 4;
15-> 4 < 10 && compare(7, 5)>0
16-> child = 4;
17-> c = 5;
18-> compare(9, 5)>0
19-> queue[1] = 5;
20-> k = 4;
21-> child = 2 * 4 + 1 = 9;
22-> c = 12;
23-> right = 10;
24// 10<10第一個判斷不成立
25-> compare(9, 12)<0;
26//跳出while循環
27-> queue[4] = 9
至此,poll操作過程結束。高質量編程視頻shangyepingtai.xin