/**
* 堆排序思路:
* 1.拷贝数组
* 2.按i/2位置开始构造大堆或者小堆
* 堆排序交换(从下往上,将大值向下交换)
* 3.进行排序,将堆顶与尾标识进行交换
*
* 堆排序属于选择排序
*/
public class HeapSort {
public int[] sort(int[] sourceArray) {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
// 获取数组的长度
int len = arr.length;
// 构造大堆
buildMaxHeap(arr, len);
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);//将堆顶即树根与最后一个值进行交换,即把最大值逐步往后放,来完成排序
len--;
heapify(arr, 0, len);//大堆位置交换
}
return arr;
}
/**
* 大堆
* b0
* b1 b2
* b3 b4 b5 b6
*b7 b8 b9
*
* 左孩子节点下标i = 父节点下标z*2+1
* 右孩子节点下标i = 父节点下标z*2+2
* 进行二叉树大堆构造的时候,只需要知道最后一个节点的父节点,然后从该父节点下标往前构造即可
* 最后一个节点的父节点的下标 i = length
*/
//构建堆的时候,需要从最下层往上层排,按从小到大顺序往上排,然后将上面小的值和其子节点大的值互换位置
private void buildMaxHeap(int[] arr, int len) {
//节点(在数组中下标为i)
//其左孩子节点下标 = 2i+1
//其有孩子节点下标 = 2i+2
for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
//从后往前,从下往上进行排序
heapify(arr, i, len);
}
}
// 判断父节点和子节点的大小关系,并进行位置交换
private void heapify(int[] arr, int i, int len) {
// i下标 节点位置为 父节点位置
// 2*i+1 left节点位置为 左孩子节点位置
// 2*i+2 right节点位置为 右孩子节点位置
int left = 2 * i + 1;
int right = 2 * i + 2;
int largest = i;
// 如果left节点下标大于等于len了,说明 i 节点是没有左孩子节点的
if (left < len
// 此时largest的值 = i,即父节点和左孩子节点比较大小
// 如果左孩子节点大于父节点
&& arr[left] > arr[largest]) {
// 则把以i节点为子树中的最大值确定为左孩子节点
largest = left;
}
// 如果right节点下标大于等于len了,说明 i 节点是没有右孩子节点的
if (right < len
// 如果上面的if成立,则此时largest的值 为 left
// 那么如果右孩子节点的值大于 largest节点即左孩子节点的值
&& arr[right] > arr[largest]) {
// 则以i节点为子树中的最大值确定为右孩子节点
largest = right;
}
// 如果largest发生了改变,不为 i 值,则进行位置交换
if (largest != i) {
swap(arr, i, largest);
//发生了交换
heapify(arr, largest, len);
}
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void println(int [] a) {
for(int x:a) {
System.out.print(" "+x);
}
System.out.println("");
}
public static void main(String[] args) {
HeapSort heapSortTest = new HeapSort();
int[] nums = {5,9,6,8,7,3,5,10,15,22,69,36};
heapSortTest.println(heapSortTest.sort(nums));
}
}