您的位置:首页 > 编程语言 > Java开发

堆与堆排序--递归与非递归java实现

2017-04-05 09:30 597 查看
堆排序是利用完全二叉树的结构思想,对线性数组进行排序的。

最大堆进行升序排序的基本思想:

① 初始化堆:将数列a[0...n-1]构造成最大堆。

② 交换数据:将a[0]和a[n-1]交换,使a[n-1]是a[0...n-1]中的最大值;然后将a[0...n-2]重新调整为最大堆。 接着,将a[0]和a[n-2]交换,使a[n-2]是a[0...n-2]中的最大值;然后将a[0...n-3]重新调整为最大值。 依次类推,直到整个数列都是有序的。

在第一个元素的索引为 0 的情形中:

性质一:索引为i的左孩子的索引是 (2*i+1);

性质二:索引为i的左孩子的索引是 (2*i+2);

性质三:索引为i的父结点的索引是 floor((i-1)/2);

堆分为大顶堆和小顶堆,

满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,

满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆

import java.util.Arrays;

/**
* 堆与堆排序
* @author mine_song 2017.04.05
*/
public class HeapSort {
public static void main(String[] args) {
int arr[] = { 4, 5, 2, 1, 8, 10, 2 };
System.out.println("before sort:" + Arrays.toString(arr));
heapSortAsc(arr, arr.length);
System.out.println("after sort:" + Arrays.toString(arr));
}

/**
* 数组从0作为第一个结点,从下往上,从右往左, 第一个非叶结点为len / 2 - 1下标位置
*
* @param arr
*            需要建立大顶堆的数组
* @param len
*            数组的长度
*/
public static void buildMaxHeap(int arr[], int len) {
// 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个大顶堆。
for (int i = len / 2 - 1; i >= 0; i--)
// adjustDown(arr, i, len)
adjustHeapWithRecur(arr, i, len);
}

/**
* 大顶堆的向下调整算法 (非递归实现)
*  注:数组实现的堆中, 第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
* 其中,N为数组下标索引值,如数组中第1个数对应的N为0。
*
* @param arr
*            待排序的数组
* @param start
*            被下调节点的起始位置(一般为0,表示从跟元素开始)
* @param end
*            截至范围(一般为数组中最后一个元素的索引)
*/
public static void adjustDown(int[] arr, int start, int end) {
int cur = start;// 当前(current)节点的位置
int temp = arr[cur];
int left = 2 * start + 1;// left左孩子,left + 1右孩子

for (; left <= end; cur = left, left = 2 * left + 1) {
// 选择左右孩子较大者
if (left < end && arr[left] < arr[left + 1])
left++;
// 当前结点和左右孩子大小相比较
if (temp >= arr[left])
break;
// 当前结点比左右孩子的较大者小
else {
// 交换
arr[cur] = arr[left];
arr[left] = temp;
}
}
}

/**
* 大顶堆的向下调整算法 (递归实现)
*
* @param arr
* @param start
* @param end
*/
public static void adjustHeapWithRecur(int[] arr, int start, int end) {
int temp = arr[start];
int left = 2 * start + 1;
if (left >= end)
return;
if (left < end - 1 && arr[left] < arr[left + 1])
left++;
if (temp >= arr[left])
return;
else {
arr[start] = arr[left];
arr[left] = temp;
}
// 继续调整被破坏的子堆
adjustHeapWithRecur(arr, left, end);
}

/**
* 升序排序
*
* @param arr
*            排序数组
* @param len
*            数组长度
*/
public static void heapSortAsc(int arr[], int len) {
buildMaxHeap(arr, len);
// 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
for (int i = len - 1; i > 0; i--) {
// 把最大值放在数组的末尾
swap(arr, i, 0);
// 继续调整0~i-1使之成为最大堆
adjustDown(arr, 0, i - 1);
}
}

// 交换
private static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
运行结果:

before sort:[4, 5, 2, 1, 8, 10, 2]

after sort:[1, 2, 2, 4, 5, 8, 10]

堆排序性能;

空间效率:常数辅助单元,空间复杂度为O(1)

时间效率:建堆时间是O(n),之后有n-1次向下调整堆,每次调整堆的时间复杂度为O(h),最好,最坏,平均复杂度是O(nlog)

稳定性:堆排序是一种选择排序,是不稳定的排序算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息