一 . 最大堆
1. 定义
1.首先是一颗完全二叉树,使用数组来存储
2.父子节点之间通过下标来表示,此时树中某个节点的索引为x时: 父节点 :(x - 1) /2
左孩子节点 :2x + 1
右孩子节点 :2x + 2
3.堆中树根节点值 >= 子树中所有节点值(当前所有子树都满足此规则)
2 . 添加元素
向堆中添加一个新元素val时:
1.在数组末尾添加元素----此时仍是一颗完全二叉树
2.添加完元素后可能会破坏最大堆的定义,因此需要进行元素上浮操作,直到把当前元素上浮到合适位置(如果上浮到树根,此时新元素就是最大值;或者上浮到某个位置时,当前元素
<= 父节点值)
向树中插入新元素52 :
52 > 16,进行上浮操作 :
52 > 41,进行上浮操作 :
此时52 < 62,上浮操作完成,此时已经变成了最大堆。
3.在最大堆中取出最大值
1.最大堆的最大值一定是在根节点处,直接取出根即可
2.将堆中最后一个元素覆盖到堆顶,删除最后一个元素,然后进行元素下沉操作
第一步,取出根节点元素,然后用最后一个元素覆盖堆顶元素:
第二步进行下沉操作 :
当前节点左右两棵子树值最大的是52,交换52和当前节点:
此时41是左右子树中最大的,同样交换41和当前节点 :
当前节点的左右子树的值都小于16,下沉操作完成,此时对已经变成最大堆.
二 . 堆化 ---- heapify
1 . 概念
所谓堆化就是给定一个数组都可以按照层序遍历的方式看做成一个完全二叉树。
2 . 原地堆排序(O(n))
方法:
从最后一个非叶子节点开始进行元素的下沉操作,直到根节点结束(不断调整子树为最大堆,直到根节点时左右子树都为最大堆,此时只需要将根节点进行下沉操作就可以得到最大堆)
从62开始进行下沉操作,62 >17 >5 ,无需交换:
再看52,52 >31>14,也不需要交换 :
到根节点,11和左右子树中最大的交换 :
交换完再看11的左右子树,11 < 17,交换 :
交换完成之后,此时就变成了最大堆.
三 . 最大堆的代码实现
package binTree.heap;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* 最大堆的实现
*/
public class MaxHeap {
private List<Integer> elementData;
private int size;
public MaxHeap(){
this(10);
}
public MaxHeap(int size){
elementData = new ArrayList<>();
}
public MaxHeap(int[] arr){
elementData = new ArrayList<>(arr.length);
for (int i : arr) {
elementData.add(i);
size++;
}
//从最后一个非叶子节点开始下沉操作
for (int i = parent(size - 1); i >= 0; i--) {
siftDown(i);
}
}
public void add(int val){
elementData.add(val);
size++;
siftUp(size - 1);
}
public int extractMax() {
if(size == 0){
throw new NoSuchElementException("heap is empty,can not extract!");
}
int max = elementData.get(0);
elementData.set(0,elementData.get(size - 1));
elementData.remove(size - 1);
size--;
siftDown(0);
return max;
}
public int peekMax(){
return elementData.get(0);
}
private void siftUp(int k) {
while (k > 0 && elementData.get(k) > elementData.get(parent(k))){
swap(k,parent(k));
k = parent(k);
}
}
private void siftDown(int k) {
while (leftChild(k) < size) {
int j = leftChild(k);
if(j + 1 < size && elementData.get(j + 1) > elementData.get(j)){
j++;
}
if(elementData.get(k) >= elementData.get(j)){
break;
}else{
swap(k,j);
k = j;
}
}
}
private void swap(int k, int parent) {
int child = elementData.get(k);
int parentVal = elementData.get(parent);
elementData.set(k,parentVal);
elementData.set(parent,child);
}
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
private int parent(int k) {
return (k - 1) >> 1;
}
private int leftChild(int k){
return (k << 1) + 1;
}
private int rightChild(int k){
return (k << 1) + 2;
}
public String toString(){
return elementData.toString();
}
}
四 . 基于最大堆的优先级队列
首先得实现队列接口,然后内部创建最大堆得引用,这样优先级队列的添加、删除、查看等操作就是最大堆的相关操作
package binTree.heap;
import queue.Queue;
public class PriorityQueue implements Queue<Integer> {
private MaxHeap heap;
public PriorityQueue(){
heap = new MaxHeap();
}
@Override
public void offer(Integer val) {
heap.add(val);
}
@Override
public Integer pool() {
return heap.extractMax();
}
@Override
public Integer peek() {
return heap.peekMax();
}
@Override
public boolean isEmpty() {
return heap.isEmpty();
}
}
五 . 最小堆
和最大堆思路一样,就是在进行下沉和上浮操作时,把根、左子树和右子树中最小的一个元素往上面换而已
代码实现如下:
package binTree.heap;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
public class MinHeap {
private List<Integer> elementData;//基本组成元素
private int size;//有效元素个数
public MinHeap(){
this(10);
}
public MinHeap(int size){
elementData = new ArrayList<>();
}
public MinHeap(int[] arr){
elementData = new ArrayList<>(arr.length);
for (int i = 0; i < arr.length; i++) {
elementData.add(arr[i]);
size ++;
}
//从第一个非叶子节点开始下沉操作
for (int i = parent(size - 1);i >= 0;i --) {
siftDown(i);
}
}
/**
* 下沉操作
* @param index
*/
public void siftDown(int index){
//当这个结点还有子树
while (leftChild(index) < size){
int i = leftChild(index);
//当右孩子存在且,右孩子的值小于左孩子的值
if(i + 1 < size && elementData.get(i) > elementData.get(i + 1)){
i = i + 1;
}
//此时i为左右孩子值最小的索引
//如果index的值小于左右孩子的值就结束,若大于则交换
if(elementData.get(index) <= elementData.get(i)){
break;
}else{
swap(index,i);
index = i;
}
}
}
/**
* 添加操作
* @param val
*/
public void add(int val){
elementData.add(val);
size ++;
siftUp(size - 1);
}
/**
* 查看最小值
* @return
*/
public int peekMin(){
return elementData.get(0);
}
/**
* 上浮操作
* @param i
*/
private void siftUp(int i) {
//当当前节点的索引大于0,且值小于父节点的值交换
while (i > 0 && elementData.get(i) < elementData.get(parent(i))){
swap(i,parent(i));
}
}
/**
* 删除并返回最大值
* @return
*/
public int extractMin(){
if(size == 0){
throw new NoSuchElementException("heap is empty,can not extract!");
}
int min = elementData.get(0);//索引为0的位置是最小值
elementData.set(0,elementData.get(size - 1));
elementData.remove(size - 1);
siftDown(0);
return min;
}
/**
* 交换操作
* @param k
* @param parent
*/
public void swap(int k,int parent){
int childval = elementData.get(k);
int parentval = elementData.get(parent);
elementData.set(k,parentval);
elementData.set(parent,childval);
}
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 返回父节点索引
* @param index
* @return
*/
public int parent(int index){
return (index - 1) / 2;
}
/**
* 返回左孩子索引
* @param index
* @return
*/
public int leftChild(int index){
return (index << 1) + 1;
}
/**
* 返回右孩子索引
* @param index
* @return
*/
public int rightChild(int index){
return (index << 1) + 2;
}
/**
* toString方法
* @return
*/
public String toString(){
return elementData.toString();
}
}
六 . 基于最小堆的优先级队列
和基于最大堆的优先级队列一样
代码如下:
package binTree.heap;
import queue.Queue;
public class PriorityQueue1 implements Queue<Integer> {
private MinHeap heap;
public PriorityQueue1(){
heap = new MinHeap();
}
@Override
public void offer(Integer val) {
heap.add(val);
}
@Override
public Integer pool() {
return heap.extractMin();
}
@Override
public Integer peek() {
return heap.peekMin();
}
@Override
public boolean isEmpty() {
return heap.isEmpty();
}
}