第六种排序算法:堆排序
##
/**
* 选择排序
* 堆排序:
* 基本思想:(建堆再调整堆)(大根堆,小根堆,堆是完全二叉树)
* 堆排序是一种树形选择排序,是对直接选择排序的有效改进。
* 堆的定义如下:具有n个元素的序列(h1,h2,…,hn),
* 当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,…,n/2)时称之为堆。
* 在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大根堆)。
* 完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
* 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,
* 这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。
* 依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。
* 从算法描述来看,堆排序需要两个过程,一是建堆,二是交换位置并调整堆。
* 所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
*
* 时间复杂度:O(nlog2 n) 建堆:O(n);调整堆:O(log2 n)
* 空间复杂度:O(1)
*
* 稳定性:不稳定
*
*/
堆排序示例代码如下:
/**
* 选择排序
* 堆排序:
* 基本思想:(建堆再调整堆)(大根堆,小根堆,堆是完全二叉树)
* 堆排序是一种树形选择排序,是对直接选择排序的有效改进。
* 堆的定义如下:具有n个元素的序列(h1,h2,...,hn),
* 当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2)时称之为堆。
* 在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大根堆)。
* 完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
* 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,
* 这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。
* 依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。
* 从算法描述来看,堆排序需要两个过程,一是建堆,二是交换位置并调整堆。
* 所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
*
* 时间复杂度:O(nlog2 n) 建堆:O(n);调整堆:O(log2 n)
* 空间复杂度:O(1)
*
* 稳定性:不稳定
*
*/
public class HeapSort {
public static void main(String[] args) {
int arr[] = {1,4,6,8,2,5,3,7,9};
System.out.println("数组排序前顺序:");
for(int n : arr){
System.out.print(n+" ");
}
//堆排序
heapSort(arr);
System.out.println("\n数组排序后顺序:");
for(int n : arr){
System.out.print(n+" ");
}
}
//堆排序
private static void heapSort(int []arr){
//1.构建大根堆
for(int i=arr.length/2-1;i>=0;i--){ //第一个非叶子结点下标i=arr.length/2-1
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.交换堆顶元素与末尾元素+调整堆结构
for(int j=arr.length-1;j>0;j--){ //堆末尾元素的下标j=arr.length-1,不能为0,因为它不能是堆顶元素
//将堆顶元素与末尾元素进行交换
int temp = arr[0];
arr[0] = arr[j];
arr[j] = temp;
//重新对堆进行调整
adjustHeap(arr,0,j);
}
}
//调整大根堆(仅是调整过程,建立在大根堆已构建的基础上)
// arr: 要排序的序列(数组)
// i: 第一个非叶子结点下标
// n: 剩余序列的长度(需要调整的结点个数)
private static void adjustHeap(int []arr,int i,int n){
int temp = arr[i];//先取出第一个非叶子结点(根节点)
for(int k=i*2+1;k<n;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<n && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点(k+1,即2i+2)
k++;
}
if(arr[k] > temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
}