堆排序
堆排序的“工具”就是完全二叉树
什么是完全二叉树?
首先普及一个名次:根结点
是指最顶端的那个结点。叶子结点
是没有子结点的结点(也就是说这个结点的后面不会有分叉)。除去根结点和叶子结点剩下的叫做内部结点
。
完全二叉树
按照每层从左至右的顺序依次累加编号,完全二叉树中每个编号的位置和满二叉树的编号位置是一样。
完全二叉树和堆的关系?
如果每一个结点均大于(或者等于)其子结点,那么最终根结点是这个完全二叉树的最大值,那么我们称这个完全二叉树为大顶堆(下图a)。
同理,如果每一个结点均小于(或者等于)其子结点,那么最终根结点是这个完全二叉树的最小值,那么我们称这个完全二叉树为小顶堆(下图b)。
大顶堆排序
可以根据这个链接来理解大顶堆的排序规则,
重点理解怎么排序?怎么替换最大值?替换以后?
堆排序的代码
package 堆排序;
import java.util.Arrays;
public class Dui {
public static void main(String[] args) {
int[] array = new int[] { 2, 1, 4, 3, 6, 5, 8, 7 };
sort(array);//调用排序方法
System.out.println(Arrays.toString(array));//数组输出
}
//排序方法
public static void sort(int[] array) {
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length);
}
//这个for循环就是建成第一个大顶堆的初始状态,
for (int j = array.length - 1; j > 0; j--) {
//交换顶点和最后一层且最右边的叶子结点。
swap(array, 0, j);
//将剩下的重新排序成大顶堆。
adjustHeap(array, 0, j);
}
}
//这个是堆排序的核心部分,一定要深度理解每一行的含义,可以手绘一下
public static void adjustHeap(int[] array, int i, int length) {
// 先把当前元素取出来
int temp = array[i];
int n=2 * i + 1;//先找到其左边的子结点
for (int k = n; k < length;k = 2 * k + 1 ) {//k=2 * k + 1意思是跳跃到和它交换的子结点
if (k + 1 < length && array[k] < array[k + 1]) {
k++;
} //经过if语句 ,k为子节点中最大的那个结点
// 如果发现子节点更大,则进行值的交换
if (array[k] > temp) {
swap(array, i, k);
//下面这一句是最核心的迭代式的代码。
//思考下:如果子节点更换了,那么,以子节点为根的子树会不会受到影响呢?
// 所以,循环对子节点所在的树继续进行判断
i = k;//此时i变成了k,swap(i,k)是不是-----------?自己理解
} else {// 如果不用交换,那么,就直接终止循环了
break;
}
}
}
//这个是数组中的a位置的值和b位置的值的交换
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
总结
堆排序的核心就是将最大值或者最小值放在根结点上。最令人容易忘记的是在自下而上替换期间,替换后的某些内部结点并不是大于或等于其子结点,这点在代码上比较难理解,手绘的话比较容易些。