day07 原地堆排序

13 篇文章 0 订阅

前言

前面我们学习了堆排序的两种实现方式,不知道大家还记不记得。我们之前学习的堆排序都是通过把一个无序的数组复制到堆中,然后进行排序,但是这样一来,我们就需要开辟额外的存储空间去存储。那么我们今天就来学习如何去解决这个问题,然后对堆排序进行优化。

思想:

其实我们可以的数组本身就是一个堆,只不过并不是一个最大堆,那么通过之前的学习我们可以通过shitDown操作去把数组变成一个最大堆,这个操作完全可以数组中操作,无序进行额外的空间开辟。

在进行了shitDown之后,此时我们的数组就是一个最大堆了,此时我们最大的元素就放在第一个位置。我们可以每次将最大的元素与数组最后边的数字进行交换,此时我们最大的元素就放到了正确的位置上,此时第一个位置上的元素不符合最大堆的规范,我们对这个位置进行shitDown操作,然后对【0 ,n-1】区间重复前边的操作,直至只剩下一个元素。

 

注意要点:

第一幅图就是对堆排序的一个直观的描述。不断的把最大值放到数组的最后边,然后数组长度进行缩减一个索引。最后数组只剩下一个元素的时候就不需要再进行操作了,此时的数组就已经是有序的了。

这里要注意,由于之前我们的堆中,我们是从索引为1的位置开始存储的,但是原地排序中,由于我们是在原来的数组中,原地排序,所以我们的数组的下标是从0开始,那么我们进行shitDown操作的时候,对于最后一个非叶子节点的判断就需要进行一下改变了。具体参照第二张图

 

 

 

代码实现:

package com.smarking.lzy.part2;

import com.smarking.lzy.part1.SortTestHelper;

/*
 * 原地堆排序的实现
 * 在目标数组中进行排序
 * 思想:每次将最大的元素放到本轮区间的最后一个位置,直至只剩下一个元素
 * **/
public class HeapSort3 {
	
	/*
	 * 排序入口
	 * arr:目标数组
	 * size:数组大小
	 * */
	public static void heapSort3(int [] arr) {
		int size = arr.length;
		if(arr == null) {
			throw new RuntimeException("arr is null!");
		}
		
		//对数组进行heapify操作
		for(int i = (size - 2)/2;i >= 0;i--) {
			shitDown(arr,i,size);
		}
		
		for(int i = size-1;i >= 1;i--) {
			SortTestHelper.swap(arr, i, 0);
			shitDown(arr,0,i);//应该是在[0,n-1]范围内进行heapify,因为最后一个位置不考虑
		}
	}
	
	/**
	 * 对index位置上的元素进行位置的调整,使得整个堆符合最大堆的规范
	 * 
	 * ps:
	 * 这里的这个shitDown和之前的那个shitDown不同之处:
	 * 		必须注意,两个shitDown不同的地方在于,这里的这个shitDown并不是对整个数组进行调整,而是对数组中我们指定的范围内的元素进行调整
	 * 我们之前写的那个shitDown是针对整个数组进行调整,所以两个shitDown对边界问题的处理不一样,必须得想明白。
	 * 		弄清楚size的实际意义!
	 * */
	private static void shitDown(int [] arr ,int index,int size) {
		while((index*2+1) < size && index >= 0) {
			int j = index*2+1;
			if((j+1) < size && arr[j+1] > arr[j]) {
				if(arr[index] > arr[j+1]) {
					return;
				}
				j+=1;
			}else{
				if(arr[index] > arr[j]) {
					return;
				}
			}
			SortTestHelper.swap(arr, index, j);
			index = j;
		}
	}
	
	
	public static void main(String[] args) {
		int testTime = 1000;
		int maxValue = 100;
		int maxSize = 10000;
		boolean success = true ;
		Heap heap = null;
		for(int i = 0;i < testTime;i++) {
			int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
			int [] arr2 = SortTestHelper.copyArray(arr1);
			int [] arr3 = SortTestHelper.copyArray(arr1);
			SortTestHelper.comparator(arr2);
			heapSort3(arr3);
			if(!SortTestHelper.isEqual(arr2, arr3)) {
				success = false;
				SortTestHelper.printArray(arr1);
				break;
			}
		}
		
		if(success) {
			System.out.println("Nice!!!");
		}else {
			System.out.println("Fuck!");
		}
	}
	
}

改进版代码:

package com.smarking.lzy.part2;

import com.smarking.lzy.part1.SortTestHelper;

/**
 * 把原地堆排序重新写一下
 * 巩固一下
 * */
public class HeapSort4 {
	
	public static void heapSort4(int [] arr) {
		if(arr == null || arr.length == 0) {
			return;
		}
		
		int count = arr.length;
		for(int i = (count - 2)/2 ;i >= 0;i--) {
			shitDown(arr,count,i);
		}
		
		for(int i = count - 1;i >= 1;i--) {
			SortTestHelper.swap(arr, i, 0);
			shitDown(arr,i,0);
		}
	}
	
	private static void shitDown(int [] arr,int count,int index) {
		while((index*2 + 1) < count && index >= 0) {
			int j = index*2+1;
			if((j+1) < count && arr[j+1] > arr[j]) {
				j+=1;
			}
			
			if(arr[index] > arr[j]) {
				return;
			}
			
			SortTestHelper.swap(arr, index, j);
			index = j;
			}
	}
	
	
	public static void main(String[] args) {
		int testTime = 100000;
		int maxValue = 100;
		int maxSize = 10000;
		boolean success = true ;
		Heap heap = null;
		for(int i = 0;i < testTime;i++) {
			int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
			int [] arr2 = SortTestHelper.copyArray(arr1);
			int [] arr3 = SortTestHelper.copyArray(arr1);
			SortTestHelper.comparator(arr2);
			heapSort4(arr3);
			if(!SortTestHelper.isEqual(arr2, arr3)) {
				success = false;
				SortTestHelper.printArray(arr1);
				break;
			}
		}
		
		if(success) {
			System.out.println("Nice!!!");
		}else {
			System.out.println("Fuck!");
		}
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值