堆排序:
【转】:图解排序算法(三)之堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
时间复杂度O(N*logN),额外空间复杂度O(1)
堆结构要掌握:
- 堆结构的HeapInsert和Heapify
- 堆结构的增大,和减小
- 如果只建立堆,时间复杂度为O(N) -> (log1 + log2 + log3 + ... + logN)
- 优先级队列就是指堆结构
排序的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
- O(N平方) -> 冒泡排序(稳定) -> 相邻交换,大的值往后面沉下去,如果遇到可以设置为不换,次序可以保持
- O(N平方) -> 插入排序(稳定) -> 把后面的数往前面插入,如果遇到相同,可以设置为不换,所以次序可以保持
- O(N平方) -> 选择排序(不稳定) -> 每一趟从待排序的记录中选出最小的元素,与0号元素交换,次序变了。
- O(N*LogN)-> 归并排序(稳定) -> 在最后merge的过程中,2个指针比较左右两个数字,若同,则不换 -> 可保持
- O(N*LogN)-> 快排(不稳定) -> 因为partition,荷兰国旗问题中不能做到维持次序,划分为3个区域,会调动
- O(N*LogN)-> 堆排(不稳定)-> 不稳定,因为完全二叉树(堆结构)排序中,会调换 44455, 其中4的次序变后了。
冒泡排序稳定:
插入排序:稳定
选择排序不稳定:
减堆的操作(弹出堆顶):
- 大根堆 -> 最后一个数与堆顶交换 -> 让堆的大小减1(记录Heapsize减1) -> 从0位置经历heapify,调整为大根堆 -> 循环
【堆排序】:利用堆结构完成的排序
- 让数组变为大根堆(未必有序)
- 最后一个位置与堆顶交换,堆大小减1
- 从0位置作Heapify操作(每次搞定一个末尾)
- 把堆顶和现在的堆底作交换,
- 直到堆的大小被减为0。
【堆排序代码】
import java.util.Arrays;
public class HeapSort_01 {
// public static void main(String[] args) {
// // TODO Auto-generated method stub
//
// }
// 首先把数组建立为大根堆操作。
// 1)判断是否为空数组,或者只有1个数字的数组
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 把每一个新的树,做为大根堆, 遍历一下。
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
// 数填满了,用变量heapsize记录堆的大小
int heapsize = arr.length;
// 2) 让最后一个数与堆顶交换,堆大小减1,
swap(arr, 0, --heapsize);
// 3) 进入循环,只当堆大小为0时停止,进行heapify操作,重新调整为大根堆,然后交换第一个数与新的堆底
while (heapsize > 0) {
heapify( arr, 0, heapsize);
swap(arr, 0, --heapsize);
}
}
// 建立大根堆,操作,如果进来一个数,它比它的根节点还大 -> 交换数值 & 同时交换序号index
public static void heapInsert(int[] arr, int index) {
if (arr[index] > arr[(index-1)/2]) {
swap(arr, index , (index-1)/2);
index = (index -1)/2;
}
}
// 交换的函数,swap
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// heapify函数功能: (0 ~ heapsize-1)上形成了大根堆,交换了一下,其中某个index的值变小了,我们要让这个变小的数字往下沉
// 这个变小的index的数,找到它的左右孩子 -> 先左右孩子对比,找最大周 -> 然后左右孩子中最大的与index的值比较 -> 看谁大
// 若左右孩子的最大值更大,则进行值交换,index更新为左右孩子中较大的那个,继续判断新的左右孩子,循环
public static void heapify(int[] arr, int index, int heapsize) {
int left = index*2 + 1;
while (left < heapsize) {
int largest = left+1 < heapsize && arr[left+1] > arr[left] ? left+1 : left;
largest = arr[largest] > arr[index] ? largest : index;
// 比如[7, 4, 5, 2, 3] -> 7 变为了6, index,还是最大的,所有largest==index
if (largest == index) {
break;
}
// 如果不是,则交换最大值,交换index,继续判定下个位置,是否需要下沉,所以left= index*2+1
swap(arr, largest , index);
index = largest;
left = index*2 + 1;
}
}
}