1.堆的定义:
堆是一种特别的二叉树,完全二叉树。
完全二叉树;
每一个节点的值都必须 大于等于或者小于等于 其孩子节点的值。
2.1>如果遇到一个结点,左孩子为空,右孩子不为空,则该树一定不是完全二叉树;
2.2>如果遇到一个结点,左孩子不为空,右孩子为空;或者左右孩子都为空,且则该节点之后的队列中的结点都为叶子节点,该树才是完全二叉树,否则就不是完全二叉树;
堆适用于动态元素添加,使用动态数组实现堆。
用链表很难实现堆,因为siftUp无法通过链表实现。
2.最大堆与最小堆:
- 最大堆:root 节点最大
最小堆:root 节点最小 ```java public class MaxHeap
>{ private Array data; public MaxHeap(int capacity){ data = new Array<>(capacity);
} public MaxHeap(){
data = new Array<>();
}
// 将传入的数组进行堆化 public MaxHeap(E[] arr){
data = new Array<>(arr); if(arr.length != 1){ // 把每层节点遍历,然后进行下移操作,最后形成堆 for(int j = parent(data.size() - 1); j >=0; j--){ siftDown(j); } }
}
// 获取左子节点 public int leftChild(int k){
return 2*k+1;
}
// 获取右子节点 public int leftChild(int k){
return 2*k+2;
}
// 获取父节点 public int parent(int k){
// root 节点无父亲节点 if (index == 0) { throw new IllegalArgumentException("Node has no parent Node"); } return (k - 1) / 2;
}
//添加元素 public void add(E value){
data.addLast(value); // 将尾部元素上移,直到到一个正确位置 siftUp(data.getSize() - 1);
}
// 元素上浮 public void siftUp(int k){
while(k > 0 && data[k] > data[parent[k]]){ data.swap(k, parent[k]); k = parent[k]; }
}
//获取最大值 public E findMax(){
if(data.length() == 0){ throw new exception("Heap is empty"); } return data.get(0);
}
// 删除元素,每次删除顶部元素(即最大值) public E remove(){
E res = findMax(); data[0] = data[arr.length - 1]; data.removeLast(); swiftDown(0); return res;
}
// 将元素下沉 public void swiftDown(int k){
// 看 k 是否为叶子节点 // 记录 j 为要交换的位置 if(leftchild(k) < data.getSize()){ int j = leftchild(k); if(rightchild(k) <= data.size() - 1 && arr[rightchild[k]] > arr[leftchild[k]]){ j = rightchild(k); } if(arr[k].compareTo(arr[j]) >= 0){ break; } swap(j, k); k = j; }
}
// 替换最大元素
public E replace(E e) {
// 取出最大元素
E res = findMax();
// 将最大元素替换为e, 设置
data.set(0, e);
// 然后进行 SiftDown
siftDown(0);
return res;
}
}
<a name="AaC41"></a>
## 3.堆排序:(是利用堆root 节点最大进行排序,而不是把数组仅仅堆化)
```java
// 原地排序 : 不断获取最大元素与最小元素,使之在 top 和 data.length - 1 位置, 然后互换
// 最大元素放在最后,之后进行交换
// 优化了空间复杂度
public static <E extends Comparable<E>> sort(E[] arr){
// 对于 arr 每层节点均为下沉操作
if(arr.lengt == 1) return
// 遍历整个数组, 将整个数组堆化
// 将数组进行 siftDown 操作
for(int i = (arr.length - 2) / 2; i >=0; i--){
// 参数 : 数组 , 节点, 数组大小
siftDown(arr, i, arr.length);
}
// 此时由于堆的性质,数组的第一个元素为最大元素
// 整个数组从小到大的顺序排列
// 此时将元素与最后一个元素进行交换,再进行siftDown操作,直到数组遍历完成
for(int i = data.length() - 1; i >= 0; i++ ){
swap(arr, 0, i);
// siftDown中的 i 表示数组长度, 在实现堆的时候用了 i ,是为了判断是否为叶子节点
// [0, i -1] 进行 siftDown 操作
siftDown(arr, 0, i);
}
}
// i 需要进行下沉操作的节点
public void siftDown(E[] arr, int i, int n){
// 确认不是叶子节点
if( (2 * i + 1) < n ){
int j = (2 * i + 1);
if( j + 1 < n && arr[j + 1] > arr[j]){
j++;
}
if(arr[i] > arr[j]){
break;
}
swap(i, j);
i = j;
}
}