排序算法之堆排序—原地排序O(nlgn)

《算法导论》第6章介绍堆排序(heapsort)像插入排序而不像合并排序,是一种原地(in place)排序算法:任何时候,数组中只有常数个元素存储在输入数组之外。并且时间复杂度为O(nlgn)。
堆数据结构不只在堆排序中有用,还可以构成一个有效的优先队列。

堆介绍

(二叉)堆数据结构是一种数组对象。可以被视为一棵完全二叉树。每个节点都有两个子结点,每一层都是填满的最后一层除外,因此可以用数组表示即可。(《算法导论》中指出,length为此数组中的元素个数,heap_size为此数组中堆的元素个数。就是说,heap_size以后的数组元素不属于堆内,不符合堆的要求。)
树的根为A[1],给定一个数组位置i,它的父节点为i/2」,左儿子为2i,右儿子为2i+1。

堆实现

堆的高度为O(lgn),一些基本操作可以在O(lgn)内完成。
注:以下算法假设数组a下标从1开始,a[0]不参与计算,heap_size为数组中堆的大小。

MAX-HEAPIFY过程,保持最大堆性质的关键

输入一个数组A和下标i,并且假定以i为父节点的所有子结点满足堆性质。
输出使得i结点保持堆性质。

	/**
     * 输入:默认A数组中i后面的位置都满足最大堆性质
     * 输出:使得i也满足最大堆性质
     * 时间复杂度:O(lgn)
     */
		private static void MaxHeapify(int[] a, int i, int heap_size) {
		int left = 2 * i;     //左儿子
		int right = 2 * i+1;  //右儿子
	    int largest = i;      //i和左儿子、右儿子中最大值的位置
		//如果左儿子比i大,则把左儿子做为最大值
		if( left <= heap_size && a[left] > a[i]) {
			largest = left;	
		}
		//如果右儿子比最大值大,则把右儿子做为最大值位置
		if( right <= heap_size && a[right] > a[largest]) {
			largest = right;
		}
		//如果最大值位置不是i,则将最大值位置的值与i位置的值交换,交换后需要被交换的子结点继续保持堆性质
		if ( largest != i) {
			int tmp = a[i];
			a[i] = a[largest];
			a[largest] = tmp;
			MaxHeapify(a, largest, heap_size);
		}
	}

BUILD-MAX-HEAP过程,可以在无序的数组上构造堆

	/**
	 * 自底向上的用MaxHeapify将一个数组变成最大堆
	 * 时间复杂度O(nlgn)
	 */
    private static void BuildMaxHeap(int[] a) {
    	//堆底为叶结点肯定都满足性质,从非叶结点开始构建。
    	int heap_size = a.length - 1;
    	int i = heap_size/2;
    	for (; i>0; i--)
    		MaxHeapify(a, i, heap_size);
	}

HEAPSORT过程,运行时间为O(nlgn)

排序算法每次从堆顶取出最大值,与数组末尾值交换即可,并缩小heap的大小,将末尾值排除在堆外。当堆缩小为1时,数组已完成从小到大排序。

	private static void HeapSort(int[] a) {
		BuildMaxHeap(a);
	    for(int i=0;i<a.length;i++) {
	    	System.out.println(a[i]);
	    }
		int heap_size = a.length-1;   //构造后的堆的heap_size为数组大小
		for(; heap_size > 0; heap_size--){//每次从堆顶取出最大值与堆末尾交换,并缩减堆大小
			int tmp = a[heap_size];
			a[heap_size] = a[1];
			a[1] = tmp;
			MaxHeapify(a, 1, heap_size-1);
		}
	}

优先级队列

《算法导论》第6章第5节优先级队列中指出,“实际中快速排序的一个好的实现往往由于堆排序。尽管这样,堆数据结构还是有着很大的用处:作为高效的优先级队列(priority queue)。”【查看JAVA8源码可知,Arrays.sort也使用的快速排序(小于47个数的时候用的插入排序)。】
“最大优先级队列的一个应用是在一台分时计算机上进行作业调度。当一个作业做完或被中断时,用EXTRACT-MAX操作从所有等待的作业中,选择出具有最高优先级的作业”

猜你喜欢

转载自blog.csdn.net/weixin_42628594/article/details/82944051