堆排序算法

1.堆排序概念基本介绍:

重要:

2.堆排序算法思想:

 

 

3.堆排序代码实现:

堆就是一颗完全二叉树,又分为大顶堆和小顶堆,堆排序的时间复杂度为O(n*logn)线性对数阶

800万的数据只需要3秒就能完成排序。

堆排序完整代码如下:

package com.bruce.sorting;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Random;

/**
 * 用大(小)顶堆,给无序数组排序,整个过程中,是没有见过树的。时间复杂度O(nlogn),并且其时间复杂度是不稳定的
 * 大顶堆: 升序排序
 * 小顶堆: 降序排序
 * @author bwang018
 * 思想简述:以升序排列为例,把一个数组(例如arr[ 0~10])看成一个完全二叉树,将这个完全二叉树调成为一个大顶堆,然后将堆顶元素与数组最后一个元素交换;
 * 然后将arr[0~9]重复以上过程,直到最后剩下2个元素,变成大顶堆后再交换。
 */
public class BigPileTop {

	public static void main(String[] args) {
//		//升序排列
//		int arr[]= {4,6,8,5,9};
//		heapSort(arr);
//		System.out.println(Arrays.toString(arr));

		//要求将数组进行升序排序
		//创建有800万数据的随机无序数组
		int [] arr = new int[8000000];
		for (int i=0; i < arr.length;i++){
			arr[i] = (int) (Math.random() * 8000000); //生成一个[0,8000000)的随机无序数组
		}
		System.out.println("排序前");
		Date dataStart = new Date();
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
		String dataStartStr = format.format(dataStart);
		System.out.println("排序前的时间是:" + dataStartStr);

		//开始排序
		heapSort(arr);

		Date dataEnd = new Date();
		String dataEndStr1= format.format(dataEnd);
		System.out.println("排序后的时间是:" + dataEndStr1);
//		System.out.println("排序后:" + Arrays.toString(arr));

	}
	//堆排序方法
	public static void heapSort(int[] arr) {
		int temp=0;
		
		//根据升序降序要求,先将数组转换为一个大或小顶堆的顺序。
		//这里是升序排序,所以用大顶堆
		//****这里每次调整一个非叶子节点对应的子树,从最左侧的非叶子节点依次调整****。
		//举例,数组arr[4,6,8,5,9],第一次调整arr[1]对应的子树,第二次调整arr[0]对应的子树
		for(int i=arr.length/2-1;i>=0;i--) {
			adjustHeap(arr,i,arr.length);
		}
		
		//将堆顶元素arr[j]与末尾元素交换,将最大元素沉到数组末端。
		//重新调整结构,使其满足堆定义,然后继续交换栈顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
		for(int j=arr.length-1;j>0;j--) {
			temp=arr[j];
			arr[j]=arr[0];
			arr[0]=temp;
			adjustHeap(arr,0,j); //为什么直接为对堆顶元素调整??每次调整都是一步到位
		}
	}
	
	/*
	 * 将以数组元素i对应的非叶子节点的子树调整为大顶堆
	 * 将数组调整成大顶堆的方法:将以i对应的非叶子节点树(子树)调整成大顶堆
	 *
	 * arr:待调整数组
	 * i:非叶子节点在数组中索引
	 * length:对数组中前多少个元素进行调整,length是在逐渐的减少
	 */
	public static void adjustHeap(int arr[],int i,int length) {
		int temp=arr[i];//取出当前元素,保存在临时变量
		//k=i*2+1,k是i节点的左子节点
		for(int k=i*2+1;k<length;k=k*2+1) {
			if(k+1<length && arr[k]<arr[k+1]) { //比较左、右子节点的大小
				k++;//k指向右子节点
			}
			if(arr[k]>temp) {//如果子节点大于父节点
				arr[i]=arr[k];//把较大的值赋值给当前节点,保证父节点比子节点大
				i=k;//重要:i指向k,继续循环比较(比较难以理解,其实就是跟自己的左或右子树交换之后,左、右子树又不能保证自己比自己的子节点大,所以需要处理)
			}else {
				break; //如果不交换,那么就无需做什么了,因为是从左到右,从下到上调整的,下面的子树本来就是大顶堆了
			}
		}
		//当for循环结束后,我们已经将i为父节点的这个树的最大值,放在了最顶上(i的位置),这颗子树已经是大顶堆
		arr[i]=temp;//将temp赋值放到调整后的位置

	}

}

 其中,adjustHeap(...)方法是将每个非叶子节点对应的子树调整为一个大顶堆。

例如下图中的树,第一次调用adjustHeap(...)方法,先将节点6(arr[1])所在的子树调整为大顶堆;然后第二次调用adjustHeap(...)方法,将4(arr[0])做为根节点的整棵树调整为大顶堆

调整之后:

调用adjustHeap(...),第一次将整棵树调整为大顶堆:

第一次调整的完整代码:将数组调整为9,6,8,5,4

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值