package eightsort;
import java.util.Arrays;
/**
* 堆排序的基本思想
* 1.初始化堆:将数列a[1...n]构造成最大堆。
* 2.交换数据:将a[1]和a[n]交换,使a[n]是a[1...n]中的最大值;然后将a[1...n-1]重新调整为最大堆。 接着,将a[1]和a[n-1]交换,使a[n-1]是a[1...n-1]中的最大值;然后将a[1...n-2]重新调整为最大值。 依次类推,直到整个数列都是有序的。
* 数组实现的二叉堆的性质
* 1.索引为i的左孩子的索引是 (2*i+1);
* 2.索引为i的左孩子的索引是 (2*i+2);
* @author zzc
* @date 2019.4.14
*/
public class heapSort {
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 (int j = array.length - 1; j > 0; j--) {
// 把大顶堆的根元素,放到数组的最后,就是每一次的堆调整之后,都会把最大的元素放到自己的最终位置
swap(array, 0, j);
//继续调整剩下的数组
adjustHeap(array, 0, j);
}
}
/**
*
* @description 整个堆排序最关键的地方
*/
public static void adjustHeap(int[] array, int i, int length) {
// 先把当前元素取出来,因为当前元素可能要一直移动
int temp = array[i];
// 在堆建成,且完成第一次交换之后,实质上i=0;也就是说,是从根所在的最小子树开始调整的
// 接下来的讲解,都是按照i的初始值为0来讲述的
// 这一段很好理解,如果i=0;则k=1;k+1=2
// 实质上,就是根节点和其左右子节点记性比较,让k指向这个不超过三个节点的子树中最大的值
// 这里,必须要说下为什么k值是跳跃性的。
// 首先,举个例子,如果a[0] > a[1]&&a[0]>a[2],说明0,1,2这棵树不需要调整,那么,下一步该到哪个节点了呢?肯定是a[1]所在的子树了,
// 也就是说,是以本节点的左子节点为根的那棵小的子树
// 而如果a[0}<a[2]呢,那就调整a[0]和a[2]的位置,然后继续调整以a[2]为根节点的那棵子树,而且肯定是从左子树开始调整的
// 所以,这里面的用意就在于,自上而下,自左向右一点点调整整棵树的部分,直到每一颗小子树都满足大根堆的规律为止
//从当前节点的子节点开始,依次遍历下面的节点,找到一个最大的数值
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
// 让k先指向子节点中最大的节点
//如果右子节点的索引小于数组长度并且,左子节点小于右子节点,k+1
//就是找有子节点的子节点,进行下一次循环
if (k + 1 < length && array[k] < array[k + 1]) {
k++;
}
// 如果左子节点大于当前节点,交换值
if (array[k] > temp) {
swap(array, i, k);
// 下面就是非常关键的一步了
// 如果子节点更换了,那么,以子节点为根的子树会不会受到影响呢?
// 所以,循环对子节点所在的树继续进行判断
i = k;
} else {
// 如果不用交换,那么,就直接终止循环了
break;
}
}
}
/**
* 交换元素
*
* @param arr
* @param a
* 元素的下标
* @param b
* 元素的下标
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
数据结构与算法——堆排序
最新推荐文章于 2021-05-19 03:45:31 发布