- 二叉树的顺序存储
- 堆
- 堆的应用
二叉树的顺序存储
使用数组保存二叉树结构,方式即将二叉树用
层序遍历
方式放入数组中。 一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费,这种方式的主要用法就是堆的表示。
下标关系:
已知双亲
(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、堆排序问题*
从小到大排序 ==》大堆,从大到小 ==》小堆