主要参考左神的算法课程和《算法导论》。
堆介绍
堆结构是一种非常重要的结构,就算被火车撞了也不能忘(引用自左程云哈哈哈)。重要是因为其特殊的结构(叶子节点和根节点满足一定关系)在很多实际场合可以很好适用。
堆主要包括两个操作:(利用数组实现)
* 堆的建立
* 堆的调整
直接上代码:注释基本上完整,参考的左神的思路,但是有个问题,左神在heapsort中 的while(size > 0)貌似会导致最后多进行了一次堆排,不知道有没有人解答一下,当然大概率是我看错了。
/**
* Heap工具类,主要包括建立大(小)根堆的过程和插入新的数据后
* 进行调整的过程
* 堆是建立在数组结构上的
* 形成的树(完全二叉树)是逻辑上的,或者说是我们脑海中的,实际是不存在的。
* 在基于数组的树结构上,通过计算下标来定位每个节点的父和子节点。设当前的节点的下标为 i
* 当子节点的下标越界认为该节点没有子节点(叶子节点) 左孩子的节点为 2 * i + 1 右孩子为 2 * i + 2
* 当子节点的父节点的父节点等于本身,则该节点为根节点 (i - 1) / 2
*/
public class Heap {
// 建立大根堆的过程, 堆的范围从0----heapSize - 1
//每次确地一个节点在大根堆中的位置
public static void heapInsert(int[] arr,int index)
{
while(arr[index] > arr[(index - 1)/ 2])
{
swap(arr, index, ((index - 1) / 2));
index = (index - 1) / 2;
}
}
// 当堆中有个数发生了变化,调整堆结构使得其继续维持大根堆
public static void heapify(int[] arr, int index,int heapSize)
{
//如果没有孩子没有必要调整结构,这些是废操作
// if((2 * index + 1) > heapSize)
// return;
int left = (2 * index + 1);
while(index < heapSize)
{
int largest = arr[left] < arr[left + 1] && left + 1 < heapSize? left + 1 : left;
if(arr[index] >= arr[largest])
break;
swap(arr, index, largest);
index = largest;
left = (2 * index) / 2;
}
}
public static void swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public static void heapSort(int[] arr)
{
if(arr.length <= 1)
return;
int size = arr.length;
//先建立一个大根堆 其实可以每次都建立一个大根堆,然后调整顺序,这种方式是可以得出有序数组的
//但是效率低,因为每次都要遍历整个结构,由于堆的结构特点,没有必要全部遍历。
for(int i = 0; i < size; i++)
{
//每次确定一个数在堆中的位置
heapInsert(arr,i);
}
//开始堆排的过程,每次将堆顶(index = 0)调整到堆的最后(index = size),之后size--,相当于
//改变了堆的规模,使得堆减小了,也就确定了该数在最后有序数组中的位置了。
while(size > 1)
{
heapify(arr,0,size);
//可以每次打印下数组看看堆排的过程
//for(int i = 0; i < arr.length; i++)
// System.out.print(arr[i]+" ");
size--;
swap(arr,0,size);
}
}
public static void main(String[] args) {
int[] arr = {9, 5, 4, 6, 3,7};
heapSort(arr);
for(int i = 0; i < arr.length; i++)
{
System.out.println(arr[i]);
}
}
}
结果: