- 二叉樹的順序存儲
- 堆
- 堆的應用
二叉樹的順序存儲
使用數組保存二叉樹結構,方式即將二叉樹用層序遍歷方式放入數組中。 一般只適合表示完全二叉樹,因爲非完全二叉樹會有空間的浪費,這種方式的主要用法就是堆的表示。
下標關係:
已知雙親(parent)的下標,則: 左孩子(left)下標 = 2 * parent + 1; 右孩子(right)下標 = 2 * parent + 2;
已知孩子(不區分左右)(child)下標,則: 雙親(parent)下標 = (child - 1) / 2;
堆:
堆的一些定義:
1. 堆邏輯上是一棵完全二叉樹
2. 堆物理上是保存在數組中
3. 滿足任意結點的值都大於其子樹中結點的值,叫做大堆,或者大根堆,或者最大堆; 反之,則是小堆,或者小根堆,或者最小堆
5. 堆的基本作用是,快速找集合中的最值
6.粗略估算,建堆可以認爲是在循環中執行向下調整,爲 O(n * log(n)) ,實際上是 O(n)
堆的創建及操作:向下調整
1.前提:左右子樹必須已經是一個堆,才能調整。
思路分析:
1)首先要有一個數組,數組進行賦值
2)進行向下調整(先找到最後一個父節點)
定義一個child指向子節點中較大的元素,當child中的元素大於root中的元素,兩者進行交換。注意不僅要比較子樹,還有比 較子樹的子樹
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap() {
this.elem = new int[10];
this.usedSize = 0;
}
//創建一個最大堆
public void creatHeap(int[] array) {
//賦值
for (int i = 0; i < array.length; i++) {
this.elem[i] = array[i];
this.usedSize++;
}
//向下調整的方式進行調整 i代表每一棵樹的根節點
for (int i = (this.usedSize-1-1)/2; i >= 0 ; i--) {
//是每一棵樹都按照向下調整的方式進行調整
AdjustDown(i,this.usedSize);
}
}
public void AdjustDown(int root, int length) {
//root是向下調整的父節點 調整child是向下調整的子節點的最大值
int child = 2*root+1;
while (child < length) {
if (child+1 < length && this.elem[child] < this.elem[child+1]) {
child = child+1;
}
if (this.elem[child] > this.elem[root]) {
int tmp = this.elem[child];
this.elem[child] = this.elem[root];
this.elem[root] = tmp;
root = child; //調整子樹以下所有的子樹
child = 2*root+1;
} else {
break;
}
}
}
public void show() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i] +" ");
}
System.out.println();
}
}
堆的應用
1、優先級隊列
思路分析:
a.找到最後一個父節點
b.進行比較,如果左節點和右節點中較大的值大於父節點,父節點和較大的子節點進行交換
c.注意要比較子節點和子節點的子節點 child = root root = (child-1)/2;
入隊列:
//入隊列
public boolean isFull() {
return this.usedSize == this.elem.length;
}
//開始向上調整
public void AdjustUp(int child) {
int root = (child-1)/2;
while (child > 0) {
if(this.elem[child] > this.elem[root]) {
int tmp = this.elem[child];
this.elem[child] = this.elem[root];
this.elem[root] = tmp;
child = root;
root = (child-1)/2;
}else {
break;
}
}
}
public void pushHeap(int val) {
if (isFull()) {
this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
}
this.elem[usedSize] = val;
this.usedSize++;
//開始向上調整
AdjustUp(this.usedSize-1);
}
出隊列:
思路分析:
a.把隊頭元素和隊尾元素交換
b.usedSize--;
c.進行向上調整
//出隊列
public boolean isEmpty() {
return usedSize == 0;
}
public void popHeap() {
if (isEmpty()) {
return;
}
int tmp = this.elem[0];
this.elem[0] = this.elem[usedSize-1];
this.elem[usedSize-1] = tmp;
this.usedSize--;
AdjustDown(0, this.usedSize);
}
得到隊列第一個元素:
public int getHeapTop() {
if(isEmpty()) {
return -1;
}
return this.elem[0];
}
堆排序:
public void heapSort() {
int end = this.usedSize-1;
while (end > 0) {
int tmp = this.elem[end];
this.elem[end] = this.elem[0];
this.elem[0] = tmp;
AdjustDown(0,end);
end--;
}
java當中的優先級隊列:PriorityQueue implements Queue
Queue<Integer> queue = new PriorityQueue<>(); //默認爲最小根堆
queue.offer(1);
queue.offer(2);
System.out.println(queue.poll()); //1
System.out.println(queue.peek()); //2
2、TOP-K問題
Top-K問題又稱海量數據問題:eg有一億個數據,找前k個最大的/前k個最小的???
注意如果找前k個最大的,建立大小爲k的小堆。找前k個最小的,建立大小爲k的大堆。
思路分析:將待找元素序列前k個元素建立大堆/小堆(爲例),每次和堆頂元素進行比較。如果數組元素大於堆頂元素,進行替換,接着進行向下調整(先出棧頂元素,然後進入堆尾)。
3、堆排序問題*
從小到大排序 ==》大堆,從大到小 ==》小堆