java - 算法 - 大顶堆、小顶堆 排序

一、完全二叉树的数组表示形式特性
最后一个父节点下标为 (len/2)-1
若当前节点的下标为i
 父节点的下标为      (i-1)/2
 左子节点的下标为    (i*2)+1 或 (i<<1)+1
 右子节点的下标为    (i*2)+2 或 (i<<1)+2
排序后特性
 大顶堆,排序后为正序;
 小顶堆,排序后为倒序;

二、运行结果

arr
1 0 2 9 3 8 4 7 5 6
maxHeap
9 7 8 5 6 2 4 0 1 3
父节点 [左子树,右子树]
9 [7,8]
7 [5,6]
8 [2,4]
5 [0,1]
6 [3,x]
sort
0 1 2 3 4 5 6 7 8 9
minHeap
0 1 2 5 3 8 4 7 9 6
sort
9 8 7 6 5 4 3 2 1 0
父节点 [左子树,右子树]
9 [8,7]
8 [6,5]
7 [4,3]
6 [2,1]
5 [0,x]
xxx

Process finished with exit code 0

三、排序代码

package algorithm;

import utils.TeachBase;

import java.text.MessageFormat;

public class MaxHeap extends TeachBase {
public static void main(String[] args) {
int[] arr = new int[]{1, 0, 2, 9, 3, 8, 4, 7, 5, 6};
print("arr", arr);
//初始化大顶堆
Heap max = new Heap(arr);
max.maxHeapInit();
print("maxHeap", max.data);
max.printTree();
max.maxHeapSort();
print("sort", max.data);
//初始化小顶堆
Heap min = new Heap(arr);
min.minHeapInit();
print("minHeap", min.data);
min.minHeapSort();
print("sort", min.data);
System.out.println("xxx");

}

public static class Heap {
public int[] data;

public Heap(int[] arr) {
data = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
data[i] = arr[i];
}
}

/**
* 初始化大顶堆
*/
public void maxHeapInit() {
int len = data.length;
int lastP = len / 2 - 1 ;
//从最后一个父节点向上遍历
for (int i = lastP; i >= 0; i--) {
shiftDown(i,len);
}
}

/**
* 父节点小于左右子节点
*/
private void shiftDown(int i,int len) {
/**
* 二叉树特性(若当前节点的下标为 i)
* 最后一个父节点下标为 (len/2)-1
* 父节点的下标为 (i-1)/2
* 左子节点的下标为 (i*2)+1 或 (i<<1)+1
* 右子节点的下标为 (i*2)+2 或 (i<<1)+2
*/
//有左子节点
int l = 0;//左子
int r = 0;//右子
int max = 0;
while ((l = (i * 2 + 1)) < len) {
//这轮循环,data[l]和data[r]交换位置
r = l + 1;
max = l;
//有右子节点,且右边的更大
if (r < len && (data[r] > data[l])) {
max = r;
}
//如果父节点大于等于左子节点,则停止循环
if (data[i] >= data[max])
break;
//左子节点比父节点值大,交换位置
swap(data, i, max);
//设置左子节点为父节点
i = max;
}
}

/**
* 大顶堆排序后,正序
*/
public void maxHeapSort(){
for(int i= data.length -1;i>=0;i--){
swap(data,i,0);
shiftDown(0,i);
}
}

/**
* 初始化小顶堆
*/
private void minHeapInit() {
int len = data.length;
int lastP = len / 2 - 1 ;
//从最后一个父节点向上遍历
for (int i = lastP; i >= 0; i--) {
shiftUp(i,len);
}
}

/**
* 父节点小于左右子节点
*/
private void shiftUp(int i, int len) {
/**
* 二叉树特性(若当前节点的下标为 i)
* 最后一个父节点下标为 (len/2)-1
* 父节点的下标为 (i-1)/2
* 左子节点的下标为 (i*2)+1 或 (i<<1)+1
* 右子节点的下标为 (i*2)+2 或 (i<<1)+2
*/
//有左子节点
int l = 0;//左子
int r = 0;//右子
int min = 0;
while ((l = (i * 2 + 1)) < len) {
//这轮循环,data[l]和data[r]交换位置
r = l + 1;
min = l;
//有右子节点,且右边的更小
if (r < len && (data[r] < data[l])) {
min = r;
}
//如果父节点大于等于左子节点,则停止循环
if (data[i] <= data[min])
break;
//左子节点比父节点值大,交换位置
swap(data, i, min);
//设置左子节点为父节点
i = min;
}
}

/**
* 小顶堆排序后,倒序
*/
public void minHeapSort(){
for(int i= data.length -1;i>=0;i--){
swap(data,i,0);
shiftUp(0,i);
}
}


private void swap(int[] arr,int a,int b){
int c=arr[a];
arr[a]=arr[b];
arr[b]=c;
}

/**
* 二叉树特性(若当前节点的下标为 i)
* 最后一个父节点下标为 (len/2)-1
* 父节点的下标为 (i-1)/2
* 左子节点的下标为 (i*2)+1 或 (i<<1)+1
* 右子节点的下标为 (i*2)+2 或 (i<<1)+2
*/
public void printTree(){
int last = data.length/2-1;
int l,r;
int len = data.length;
System.out.println("父节点 [左子树,右子树]");
for(int i=0;i<=last;i++){
l = (i*2) + 1;
r = l + 1;
System.out.println(MessageFormat.format("\t{0} [{1},{2}]"
,data[i]
,l<len?data[l]:"x"
,r<len?data[r]:"x"));
}
}
}
}