堆排序:利用带大顶堆的特点(从小到大时),按照从右向左、从下自上的顺序将所有的非叶子节点都调整成以当前非叶子节点为根的大顶堆,这样遍历完成之后,整棵树就变成了一个大顶堆,最大的元素在根节点上,这时将这个最大的元素和最后一个位置上的元素进行交换,这样最大值就跑到了最后,这样操作之后,由于整棵树原本就是大顶堆,虽然把根节点调换了,但底下的还是维持原来的大顶堆特点的,因此只需要对根节点进行调整就好了,也就是调用上面的那个方法,这样length-1次之后就完成了排序;
将指定节点为根节点的树调整成大顶堆思路:将指定节点和两个子节点中较大的那个进行比较,但是较高层若发生了交换会影响下面的顺序,因此一旦发生了交换,用来交换的那个节点应该也进行判断直到不发生交换为止。采用插入排序的思想:从给定节点开始和子节点中较大的那个进行比较,如果这个较大的比给定节点还大就将其上移,并将指针指向这个上移的原来的位置,直到不发生上移为止。循环结束之后,就找到了给定根节点应该插入的位置。
复杂度分析:最好情况下(也就是已经排好序)为n+nlogn也就是nlogn,最坏情况下为nlogn+nlogn也就是nlogn,因此堆排序的平均复杂度就是nlogn
代码如下:
static void heapSort(int[] arr) {
int length = arr.length;
for (int i = length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, length);
}
for (int i = length - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
adjustHeap(arr, 0, i);
}
}
/**
* @param arr 待调整数组
* @param i 将以索引i为根节点的树调整成大顶堆
* @param length 待调整数组的长度
*/
static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];
for (int j = 2 * i + 1; j < length; j = 2 * j + 1) {
if (j + 1 < length && arr[j] < arr[j + 1]) {
j++;
}
if (temp < arr[j]) {
arr[i] = arr[j];
i = j;
} else {
break;
}
}
arr[i] = temp;
}