堆:堆常见的二叉堆,这种数据结构有大根堆和小根堆。对于大根堆来说,每个父节点是大于他的两个孩子节点的。也就是最大值在根节点。小根堆与之相反。如果堆用数组实现的话,如果从1开始计数(因为0的位置可以在上虑或者下虑的时候做个暂存的位置),那么一个孩子的父节点是i/2;如果知道了父节点,而左孩子的节点位置为 ix2,右孩子节点的位置为 ix2+1。如果想加入一个节点,只需要加入到最后一个位置,然后向上组件大根堆。我们称之为上虑。判断的结束条件就是 没有达到最上层和孩子节点是大于父亲节点的,可以证明,执行一次插入平均需要2.607次,因此平均insert操作上移元素1.607层,是个常数时间。移除最大值的做法是把最后一个值替换到第一个位置,然后向下组建大根堆,我们称之为下虑。结束的条件就是 节点的左孩子大于了size或者出现了父节点是大于两个孩子就结束循环,过程是在孩子中先选择出最大值并记录位置,然后和父节点比较,再判断需不需要交换,时间复杂度为O(logN)。对于如何创建一个堆,如果是一个个值的插入,那么时间复杂度将会是O(nlogn)。而如果通过整个数组同时创建堆,时间复杂度是O(n),方法就是先找到最后一个非叶子节点,然后一个个的利用上虑的方法。
优先队列:有了大根堆后,优先队列就可以基于堆这种数据结构进行创建了。比如优先队列是根据数的大小进行出队的,数越大等级越高(或者越小等级越高)。那么我们就可以用大根堆的移除第一个元素的方法进行一个个的移除就可以实现优先队列的要求出队列的顺序。时间复杂度是O(nlogn)。
public class BinaryHeap {
//默认的存储的数组的大小
private static final int defaultSize = 20;
//声明一个数组
private int[] array;
//记录目前已经存了多少元素,是从1开始的。
private int currentSize = 0;
public BinaryHeap(int size) {
array = new int[size+1];
}
public BinaryHeap() {
this(defaultSize+1);
}
//还可以直接对一个数组进行堆排序
public BinaryHeap(int[] nums) {
this(nums.length+1);
currentSize = nums.length;
for(int i = 1;i<=nums.length;i++) {
array[i] = nums[i-1];
}
for(int i = currentSize/2;i>0;i--) {
sort(i);
}
}
//获取存入了多少元素
public int getCurrentSize() {
return currentSize;
}
//整个数组有多大
public int getArraySize() {
return array.length-1;
}
//判断是否为空
public boolean isEmpty(){
return currentSize == 0;
}
//插入元素,上虑的过程。。。此方法如果上升到D层,那么用到的赋值次数为D+1次。
public void insert(int val) {
if(currentSize >= array.length-1) {
enlargeSize();
}
int temp = ++currentSize;
for(array[0] = val; array[0] < array[temp/2];temp = temp/2) {
array[temp] = array[temp/2];
}
array[temp] = array[0];
}
//扩大容量
public void enlargeSize() {
int temp = currentSize *2 +1;
int[] nums = new int[temp];
for(int i = 1;i<=currentSize;i++) {
nums[i] = array[i];
}
array = nums;
}
//删除最小元素,采用下虑的方法,循环到已经排序或者大于了currentSize为止。此时一定要判断有没有左孩子。
public int deletdMin() {
if(isEmpty()) {
throw new IllegalArgumentException("数组已空");
}
else {
int min = array[1];
array[1] = array[currentSize--];
sort(1);
return min;
}
}
private void sort(int index) {
int child;
int temp = array[index]; //先把这个元素赞存起来,为其找合适的位置。以后都是假设这个暂存值在父节点位置
for(;index*2<=currentSize;index = child) {
child = index *2;
if(child < currentSize && array[child] > array[child+1]) {
child +=1;
}
if(temp > array[child]) {
array[index] = array[child];
}
else {
break;
}
}
array[index] = temp; //最后一定为用这个暂存的值为空位置赋值
}
//重写输出toString方法
public String toString() {
StringBuilder res = new StringBuilder();
res.append("binaryHeap:");
for(int i = 1;i<=currentSize;i++) {
res.append(array[i]+",");
}
return res.toString();
}
public static void main(String[] args) {
int[] nums = {21,13,16,68,19,31,24,65,32,26};
BinaryHeap heap = new BinaryHeap(nums);
heap.insert(14);
System.out.println(heap);
System.out.println(heap.getArraySize());
}
}