java_basic_heapSort

堆排序:

【转】:图解排序算法(三)之堆排序

堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

时间复杂度O(N*logN),额外空间复杂度O(1)

堆结构要掌握:

  1. 堆结构的HeapInsert和Heapify
  2. 堆结构的增大,和减小
  3. 如果只建立堆,时间复杂度为O(N) ->  (log1 + log2 + log3 + ... + logN)
  4. 优先级队列就是指堆结构

排序的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

  1. O(N平方) ->  冒泡排序(稳定) -> 相邻交换,大的值往后面沉下去,如果遇到可以设置为不换,次序可以保持
  2. O(N平方) ->  插入排序(稳定) -> 把后面的数往前面插入,如果遇到相同,可以设置为不换,所以次序可以保持
  3. O(N平方) ->  选择排序(不稳定) -> 每一趟从待排序的记录中选出最小的元素,与0号元素交换,次序变了。
  4. O(N*LogN)-> 归并排序(稳定) -> 在最后merge的过程中,2个指针比较左右两个数字,若同,则不换 -> 可保持
  5. O(N*LogN)-> 快排(不稳定) -> 因为partition,荷兰国旗问题中不能做到维持次序,划分为3个区域,会调动
  6. O(N*LogN)-> 堆排(不稳定)-> 不稳定,因为完全二叉树(堆结构)排序中,会调换 44455, 其中4的次序变后了。

冒泡排序稳定:

插入排序:稳定

选择排序不稳定:

减堆的操作(弹出堆顶):

  • 大根堆 -> 最后一个数与堆顶交换 -> 让堆的大小减1(记录Heapsize减1) ->  从0位置经历heapify,调整为大根堆  -> 循环

【堆排序】:利用堆结构完成的排序

  1. 让数组变为大根堆(未必有序)
  2. 最后一个位置与堆顶交换,堆大小减1
  3. 从0位置作Heapify操作(每次搞定一个末尾)
  4. 把堆顶和现在的堆底作交换,
  5. 直到堆的大小被减为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;
		}
		
	}
}

猜你喜欢

转载自blog.csdn.net/sinat_15355869/article/details/81545563