排序算法----------堆排序

堆排序

介绍

堆排序也是一种选择排序最坏最好平均时间复杂度均为O(nlogn),它属于不稳定排序

是一个完全二叉树,每个节点的值都大于或等于其左右孩子节点的值===>大顶推,注意他并没有要求左右孩子节点值的大小关系,反之就是小顶堆

基本思想

1.将待排序序列构造成一个大顶堆

此时,整个序列的最大值就是堆项的根节点。

将其与末尾元素进行交换,此时末尾就为最大值。

然后将剩余n-1个元素重新构造成一个堆, 这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

图解

img

教学视频中的例子是给定一个数组{4,6,8,5,9}使用堆排序算法

在这里插入图片描述

然后我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点arr.length/2-1=5/2-1=1,也就是下面的6结点) , 从左至右,从下至上进行调整。

在这里插入图片描述

然后找到第二个非叶子节点4,发现4,9,8中9元素最大,4和9交换

在这里插入图片描述

这时,交换导致了子根[4,5,6]结构混乱,继续调整, [4,5,6]中6 最大,交换4和6。

在这里插入图片描述

至此,我们的大顶推完成

步骤二

将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末
元素交换,得到第二大元素。如此反复进行交换、重建、交换。

在这里插入图片描述

9已经不用在排序了,我们就当成移出数组了

然后再重新调整结构,使其继续满足堆定义

在这里插入图片描述

再将堆顶元素8与末尾元素5进行交换,得到第二大元素8

在这里插入图片描述

后续操作重复上面,最终得到有序序列

在这里插入图片描述

老师的总结

1.将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

2.将堆顶元素与末尾元素交换[将最大元素’沉"到数组末端

3.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

代码

package;

import java.util.Arrays;

//堆排序
//2021年1月31日21:26:04
//作者 @王
public class HeapSort {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		int arr[] ={
    
    4,6,8,5,9};
		heapSort(arr);
	}
	
	//编写堆排序的方法
	public static void heapSort(int[] arr){
    
    
		int temp = 0;
		System.out.println("堆排序");
		
//		//分步完成
//		adjustHeap(arr, 1, arr.length);
//		System.out.println("第一次调整"+Arrays.toString(arr));
//		adjustHeap(arr, 0, arr.length);
//		System.out.println("第二次调整"+Arrays.toString(arr));
		//将我们的数组变成一个大顶堆
		for (int i = arr.length/2 -1; i >=0; i--) {
    
    
			adjustHeap(arr, i, arr.length);
		}

		//将堆顶元素与末尾元素交换,将最大元素沉到数组末端
		//重新调整结构,使其满足堆定义,然后继续减缓堆顶元素与当前末尾元素,反复
		//执行调整+交换步骤直到整个序列有序
		for(int j = arr.length-1;j>0;j--){
    
    
			//交换
			temp = arr[j];
			arr[j] = arr[0];
			arr[0] = temp;
			adjustHeap(arr, 0, j);
		}
	}
	//将一个数组(二叉树),调整成一个大顶堆或者小顶堆
	/**
	 * 功能:完成将以i对应的非叶子结点的树调整成大顶堆
	 * 举例  4,6,8,5,9  => i=1   => 得到 4,9,8,5,6
	 * @param arr
	 * @param i		表示非叶子节点在数组中的索引 
	 * @param length	表示对多少个元素继续调整,length是在逐渐的减少
	 */
	public static void adjustHeap(int arr[],int i,int length){
    
    
		int temp = arr[i];//取出当前元素的值,保存在临时变量
		//开始调整
		//1. 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为父节点的树的最大值,放在了最顶上(局部)
		arr[i] = temp;
	}

}

猜你喜欢

转载自blog.csdn.net/qq_22155255/article/details/113823058