java代码实现堆排序(注释超详细)

    这是我的最新修改:之前的代码有点小bug,一直没有解决,就找了一个新的代码。 

public static void heapSort(int[] tree,int n) {
		buildHeap(tree, n);//第一步是将得到的数组构建成大顶堆
		for(int i = n-1;i>=0;i--) {
			swap(tree, i, 0);//第一次构建完大顶堆之后,要进行第一个数和最后一个树的交换
			//交换完之后,最上面的数就不是最大数了,因此只需要对最上面的数,进行一个树的调整即可
			//所以,我们使用的时adjustTree而不是buildHeap
			adjustTree(tree, i, 0);//这里解释一下,这参数的含义:之所以将i当做数组的长度,
			//是因为我们将第一个数和最后一个数交换之后,就已经把最大的数放在了数组最后,进行
			//树调整的时候,就不需要管最后一个数字了。而0就是因为交换之后需要进行节点调节的那个节点
			//换到了第一个位置
		}
	}
	
	
	
	/*
	 * 这个函数写完之后,就可以将任意一个数组,构建成大顶堆了,构建完大顶堆之后,就要进行堆排序了
	 */
	public static void buildHeap(int[]tree,int n) {
		for(int i = (n-1)/2;i>=0;i--) {//i从最后一个子节点的父节点开始,所以i = (n-1)/2
			adjustTree(tree, n, i);
		}
	}
	//利用adjustTree和swap两个函数,可以针对某一个父节点,进行调节。接下来,解决当整个树是
	//乱序的,将一个树构建成一个大顶堆。思路是这样的:从最后一个子节点的父节点开始调节,往上走。
	//不断重复,每往上一个父节点,父节点的下标就减一,可以将adjustTree和swap函数放进一个for循环
	//就是上面的for循环
	/*
	 * 表示从某一个节点开始,调整一次树,使之成为堆,其中i表示某一个节点的下标
	 */
	public static void adjustTree(int[]tree,int n,int i) {
		if(i>=n) {//这是递归头。
			return;
		}
		//首先确定i节点的左右两个孩子的下标
		int c1 = 2*i+1;
		int c2 = 2*i+2;
		//接下来,在这三个值中,找出最大值
		int max = i;//先假设最大值为这个父节点
		if(c1<n && tree[c1]>tree[max]) {//要保证c1不会出界
			max = c1;
		}
		if(c2<n && tree[c2]>tree[max]) {//保证c2不会出界  c2<n
			max = c2;
		}
		//经过上面的条件判断,就可以将最大值的下标保存到max中了,如果最大值max就是i,也就是
		//父节点最大,就不用调整,但是如果父节点不是最大,就要进行交换了
		if(max!=i) {
			swap(tree,max,i);
			adjustTree(tree,n,max);//交换之后,将父节点下放一级,就有可能会破坏下一层结构,
			//所以,递归调用adjustTree.使用递归之后,就要添加递归头了
		}
		
		
	}
	private static void swap(int[] tree, int i, int j) {
		int temp = tree[i];
		tree[i] = tree[j];
		tree[j] = temp;
	}

   

在做面试题的时候看到有一个题目就是手写堆排序,当时还不知道什么是堆排序,还以为是计算机内存中的堆。看了好多博客,终于有点理解了,为了别的小朋友重蹈覆辙,这里我将把注释写的非常详细,以便你们理解。

       堆的物理存储结构是一维数组,逻辑存储结构是完全二叉树。

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

       堆分为大顶堆和小顶堆。每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图所示:(有一些基本概念和图我是从别的博客粘贴复制来的,望见谅,我会在底下注明出处。)

根据图中圈外的数字[0,1,2,3,4.....]

我们可以得出结论:根据任何一个元素的下表  i  我们可以得知其左孩子的元素下标:2*i+1;其右孩子的元素下标:2*i+2;其(最顶层的元素除外)父节点的下标为(i-1)/2;

好了,有了上面的基本知识,我们就可以进行堆排序了:

堆排序主要分为两个部分:构建大顶堆(升序)或者小顶堆(降序)和交换两个主要部分;

首先,我们先来构建大顶堆,思想理解很简单:就是我们来调整这个二叉树,使这个二叉树中的任意一个子二叉树满足如下条件:

父节点的值应该大于或者等于其左右孩子结点的值。

那我们应该怎调整呢?很简单,就是在二叉树中从下往上数找到第一个父节点,同时,从右往左找找到第一个父节点。如下图所示,结点4就是我们要调整的第一个父节点。所以我们要先调整以结点4为父节点的子树,然后调整以结点3为父节点的子树.然后调整以结点2为父节点的子树,这里要特别注意一点:假设我们将结点1和结点3互换以后,结点1下降一级,可能结点1下降以后,不满足了父节点的值大于左右孩子,然后要以递归的方式重新调整此子树。

按照这种思路调整完以后,我们会得到一个大顶堆。此时,第一步已经完成接下来就是交换。

交换也很简单,就是把构建好的大顶堆的最顶层的元素和最后一个元素进行交换。交换之后,由于大顶堆的元素已经改变。这个时候需要重新调整树。直到最后一个元素。 

来看代码:首先是构建大顶堆:

//构建大顶堆
private int[] buildBigHeap(int [] array){//接受的参数为原始数组,
    // 从最后一个父节点开始,所以应该先找到最后一个父节点的下标索引,我们知道最后一个子节点的索引
    //是array.length-1.那么最后一个结点的父节点就是(array.lenght-1-1)/2.你们在这里一定想,最后
    //一结点不一定是左孩子(左孩子下标减一)还是右孩子(右孩子下标见2),这里你为什么减1呢?可以
    //验证一下,如果减2,右孩子肯定能定位到父节点,但左孩子就定位错误,减1的话,由于存在取整的
    //效果,所以可以定位到父节点
    for(int i = (array.length-1-1)/2;i>=0;i--){//循环的作用就是从下至上,从右至左的去调整子树
        adjustTree(array,i,array.length);//参数为原始数组、需要调整子树的父节点的下标、数组的
    //长度
    } 
}

//对子树进行调整的方法,上面的方法是循环调用此方法进行整个树的调整
private void adjustTree(int[] array,int k,int length){
    int temp = array[k];//首先将父节点的值存在临时变量中
    for(int i = 2*k+1;i < length-1; i = 2*i+1){ //i为初始化结点的左孩子,当子树的层数为两层
//的时候,这个for循环只执行一次,当超过两层的时候,就是执行超过一次,而语句i = 2*i+1,的含义就是
//在执行第二次循环的时候,将父节点定位到下一层的左孩子。
        if(i+1<length-1 && array[i] < array[i+1]){//如果左孩子小于右孩子,就将i加1,得较大
//的孩子 ,之所以是i+1<length,我们不确定最后一个子节点是左孩子还是右孩子。如果最后一个结点是左
//孩子,就只有一个孩子左右孩子就不用比较了,也就不用i++了
            i++;
        }
        if(temp >= array[i]){ //然后再用父节点的值与较大孩子比较,如果父节点较大,直接跳出此
//次循环
            break;
        }else{//如果父节点较小,将左右孩子中较大的孩子赋值给父节点。
            array[k] = array[i];
            k=i;  //修改k值,当for循环多于一次的时候即子树多于两层的时候,暂时利用k将下降一层
//至左孩子。
        }    
    } 
    array[k] = temp;  //当多与两层的子树循环完毕后,将最小值放入最终位置
}

下面就是进行堆排序了(交换)

//堆排序
public int[] heapSort(int[] array){
    array = buildBigHeap(array);//将原始数组构建成大顶堆
    for(int i = array.length - 1;i>=1;i--){
        int temp = array[0];.//以下三局代码实现了将堆顶元素和最后一个元素互换
        array[0] = array[i];
        array[i] = temp;
        //互换之后,由于顶层元素改变,继续进行新一轮的调整。
        adjustTree(array,0,i);

    }


}

 下面的代码就是构建最小堆,进行降序排列了,代码与上面的一样,不过就是修改了两个地方,一个是左右孩子比较大小的时候,将小于换成了大于,父节点和较大结点进行比较的时候,将大于等于换成了小于等于。

public static void main(String[] args) {
		int[] arr = {5,8,4,2,0,1,7,6,3};
		
		int[] result = heapSort(arr);
		for(int i = 0;i<result.length;i++) {
			System.out.print(result[i]);
		}
	}
	private static int[] buildBigHeap(int [] array){
	    for(int i = (array.length-1-1)/2;i>=0;i--){
	        adjustTree(array,i,array.length);
	    
	    } 
	    return array;
	}
	
	private static void adjustTree(int[] array,int k,int length){
	    int temp = array[k];
	    for(int i = 2*k+1;i < length-1; i = 2*i+1){ 
	        if(i+1<length-1 && array[i] > array[i+1]){
	
	            i++;
	        }
	        if(temp <= array[i]){ 
	            break;
	        }else{
	            array[k] = array[i];
	            k=i;  
	        }    
	    } 
	    array[k] = temp;  
	}
	public static int[] heapSort(int[] array){
	    array = buildBigHeap(array);
	    for(int i = array.length - 1;i>=1;i--){
	        int temp = array[0];
	        array[0] = array[i];
	        array[i] = temp;
	        adjustTree(array,0,i);
	    }
	    return array;

	}
	public static String selcetfront(int[] array,int k) {
		int[] result = heapSort(array);
		StringBuilder str = new StringBuilder();
		for(int i = result.length-1;i>result.length-1-k;i--) {
			str.append(result[i]);
			str.append(" ");
		}
		return str.toString();
	}

参考博客: Java实现堆排序(大根堆) - CherishFu - 博客园

有不对的地方   欢迎批评指正

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值