堆排序

1614108793241
这样看是不是就是一个数组?是的,(二叉)堆就是一个数组,可以被看作近似的 完全二叉树。

对于表示堆的数组array[0…n-1],我们以array[0]为根,给定某个节点下标 i,其父节点和左右后代节点的下标分别为:

parent(i) = (i-1)/2;

left(i) = 2*i+1;

right(i) = 2*i+2;

实际操作是可以根据您的设置而改变。所以看作完全二叉树:
      根据它的特点,堆分为最大堆和最小堆。最大堆就是:除根节点以外的每个节点 i,都有arr[ parent(i) ] >= arr[i]。 最小堆的特点则是:除根节点以外的每个节点i,都有arr[ parent(i) ] <= arr[i]。在做排序时,一般使用最大堆。

故而这个排序大致可分为以下的步骤:

一、维护最大堆性质
     此时,我们假定left(i), right(i)为根节点的二叉树均满足条件,可是i对应的值可能小于其孩子啊,所以就让i对应的这个值逐级下降,从而满足最大堆的条件。

    所以需要做的是: 从元素arr[i], arr[left(i)], arr[right(i)]中找出最大的元素,将下标存在largest中;如果arr[i]是最大的,说明以节点i为根的二叉树是最大堆,无须调整;否则,交换arr[i]和arr[largest],于是arr[i], arr[left(i)], arr[right(i)]三者满足了最大堆的性质,但是交换后,下标为largest的节点存放arr[i]的值,以该节点为根的子树又可能违反最大堆的性质,因此需要对该子树递归调用本调整过程。
参考代码:
private static void MAX_heapify(int[] a, int i, int size) {//维护最大堆性质  -- 前提是i的左右子树均是最大堆
               //parent(i) = (i - 1) / 2
		int left_child = 2 * i + 1;
		int right_child = 2 * (i + 1);

		int largest;
		if(left_child < size && a[left_child] > a[i]){
			largest = left_child;
		}
		else{
			largest = i;
		}
		if(right_child < size && a[right_child] > a[largest]){
			largest = right_child;
		}
		if(largest != i){
			int temp = a[i];
			a[i] = a[largest];
			a[largest] = temp;

			MAX_heapify(a, largest, size);
		}
二、建最大堆
    利用(一)建堆。 对于每一片树叶,我们都可以看作是一个只含一个元素的堆。于是对于叶子结点的父亲结点(左右子树都是最大堆),我们可以调用maxHeapify()来进行调整。调整之后,我们得到更大的堆,对于这些堆的父节点,我们又可以调用maxHeapify()来进行调整。
   所以保证前提的情况下,只需要从他的最下面的非叶子节点开始,一直到根节点结束即可。( 叶子或终端结点:度为0的结点。 :结点拥有的子树数称为结点的度。)
参考代码:
private static void Build_MAXheap(int[] a, int size) {
		 //当数组表示存储n个元素的堆时,叶子结点的下标:n/2, n/2+1...n-1(n/2表示向下取整)
		int i;
		for(i = size / 2 - 1; i >=0 ; i --){
			MAX_heapify(a, i, size);
		}
	}

三、堆排序算法

      引入另一个变量:heap_size,它用来表示堆的大小,而用size来表示数组的大小。
    根据最大堆的性质,知道第一个值最大,所以将他和最后一个值进行位置交换,移除出它,但是需要保证剩余的部分仍然满足最大堆的性质,故而调用(一)。重复过程。
private static void Heap_sort(int[] a, int size) {

		if(a == null || size <= 0){
			return;
		}
		int heap_size = size;
		Build_MAXheap(a, heap_size);//建堆
		for(int i= size - 1; i >= 1; i --){
			int temp = a[0];
			a[0] = a[i];
			a[i] = temp;
			heap_size --;//将最大的值给数组 的第一个数,然后将他移除 堆的长度减 1
			
			MAX_heapify(a, 0, heap_size);
			//System.out.println(Arrays.toString(a)); 仍然使用原数组 来装数字, 相当于把最大的数字调到了数组的最后一位,在返回做堆里的活动
		}
	}

测试代码和结果:

public static void main(String[] args) {
		int[] A={1, 3, 2, 5, 7, 6, 9, 4, 10};
		int size = A.length;

		Heap_sort(A, size);
		System.out.println(Arrays.toString(A));
	}


堆排序的时间复杂度为O(n logn),堆排序具有空间原址性:任何时候都只需常数个额外的元素空间来存储临时数据。

引入堆这种数据结构进行数据管理,不仅可应用于堆排序中,还可用于一种有效的优先队列(参看:点击打开链接)。


参考: 《算法导论》


以上就是这篇文章的内容,如果存在错误或者有什么可以改进的地方,请您指出,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值