您的位置:首页 > 其它

【排序算法】 堆排序 heap sort(选择类排序)

2017-06-22 22:17 274 查看

【排序算法】 堆排序 heap sort(选择类排序)

堆排序的过程

创建最大堆:

从最后一个非叶子结点到根结点(从下向上,这点非常关键)调整每一个子堆,使子堆有序化。

最开始的子堆的根结点是最后一个非叶子结点,它有一个或者两个孩子,它与较大者交换位置即可实现子堆有序化。

底部的子堆有序化,是其上面的堆进行调整的前提条件。

也即是,如果子堆是无序的,那么包含它的一个更大的堆就无法进行调整操作,

所以需要从最后一个非叶子结点到根结点(从下向上,这点非常关键)调整每一个子堆,使子堆有序化。。

调整最大堆(有序化堆):

(只有一个或者零个父结点相对孩子是无序)

如果当前结点有孩子结点,

如果当前结点的孩子结点的较大者比当前结点大,则交换两者位置,

否则调整结束,退出,

继续比较当前结点与其新的孩子结点

堆排序的步骤:

初始化最大堆(使堆有序化,可能所有的子堆都是无序的,需要调整所有的子堆)

将最大堆的第一个元素和最后一个元素交换位置;

使堆的长度减 1;

最大堆调整(使堆有序化,只有第一个元素相对于它的孩子是无序的,不断调整此元素和其后代的相对位置即可);

重复以上三步,直到堆的长度为 1。

索引关系

使用数组存储完全二叉树(堆),则结点下标关系是:

父结点 i 的左子结点是 2 * i + 1;

父结点 i 的右子结点是 2 * i + 2;

子结点 i 的父结点是 floor((i-1)/2)

后一个非叶子结点是 floor(N/2)-1

时间复杂度

expense:

时间复杂度 O(N*log(N))

应用场景

堆排序实现

package algorithm.algorithm4.ch02_sort;

import org.junit.Test;

import java.util.Arrays;
import java.util.Random;

/**
* algorithm: heap sort,堆排序
*/
public class Heap {

public static void sort (Comparable[] arr){
if (arr == null || arr.length < 2) return;

// 1. 创建堆
// 从下向上使每一个有孩子的结点都成为一个有序堆(宏观角度)
// 多次堆化,从下到上不断新加元素,因为单次堆化要求最多只有根结点相对于孩子结点是无序的,所以需要从后往前添加元素
// 具体的堆化操作是从上到下完成的,而且一次堆化要求最多只有根结点相对于孩子结点是无序的,此外所有的子堆都是有序的
for (int i = arr.length/2-1; i >= 0; i --)  // 完全二叉树中最后一个有孩子的结点是 N/2-1
maxHeapify(arr, i, arr.length);

// 2. 堆排序
for (int j = arr.length - 1; j > 0; j --) {
swap(arr, 0, j);  // 堆首元素不断和堆尾元素交换位置,堆的大小减 1
maxHeapify(arr, 0, j);  // 将去掉一个元素的新堆调整为最大堆(有序堆)
}
}

/**
* 从上到下有序化堆,使小于孩子的父结点下沉(sink)
* @param arr 二叉堆
* @param index 待调整的子堆的根结点
* @param newSize 除了排序后的元素外,堆中剩余元素的个数
*/
// 具体的堆化操作是从上到下完成的,而且一次堆化要求最多只有根结点相对于孩子结点是无序的,此外所有的子堆都是有序的
private static void maxHeapify (Comparable[] arr, int index, int newSize){

while (2 * index + 1 < newSize){  // 当前结点至少有左孩子
int childIdx = 2 * index + 1;  // index 左孩子的下标
// 存在右孩子,且右孩子比左孩子更大,则记录右孩子的下标
if (childIdx + 1 < newSize && arr[childIdx].compareTo(arr[childIdx+1]) < 0) childIdx ++;
if (arr[childIdx].compareTo(arr[index]) <= 0) break;  // 两个孩子均小于等于当前元素,则退出循环
swap(arr, index, childIdx);
index = childIdx;  // 当前不断跟新的孩子比较,跟其中一个较大的孩子交换位置
}
}

private static void swap(Comparable[] arr, int i, int j){
Comparable tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}

@Test
public void test(){
Random random = new Random();
int N = 20;
Integer[] arr = new Integer
;
for (int i = 0; i < N; i ++) arr[i] = random.nextInt(100);
System.out.println(Arrays.toString(arr));
Heap.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: