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

java写一个堆排序(大顶堆)

2017-08-28 12:22 381 查看

java写一个堆排序(大顶堆)

堆排序的时间复杂度,最好,最差,平均都是O(nlogn),空间复杂度O(1),是不稳定的排序

堆(或二叉堆),类似于完全二叉树,除叶子节点外,每个节点均拥有左子树和右子树,同时左子树和右子树也是堆。

小顶堆:父节点的值 <= 左右孩子节点的值

大顶堆:父节点的值 >= 左右孩子节点的值

堆的存储:

用一个数组存储堆就可以了,如【19, 17, 20, 18, 16, 21】



对于数组中的第 i 个节点(从0开始),有如下规律:

如果父节点存在,则它的父节点是 (i - 1) / 2; 比如3的父亲是(3-1)/2=1

如果左孩子存在,则它的左孩子是 2 * i + 1; 比如1的左孩子是2*1+1=3

如果右孩子存在,则右孩子是 2 * i + 2;比如1的右孩子是2*1+2=4

假设当前要排序的数组是:arr = [19, 17, 20, 18, 16, 21],下面以大顶堆为例,介绍如何构建大顶堆

1、由于叶子节点没有左孩子和右孩子,所以不必从叶子节点开始调整堆,即不从18、16、21开始调整,直接从20开始调整,直至堆顶。 用伪代码(Java)描述如下:

for(int i = arr.length / 2 - 1; i >= 0; i--) {
// 调整();
fix_down(arr, i, arr.length-1);
}


2、如果当前节点 小于 左右孩子的最大值【此处假设是右孩子】,则交换当前节点的值与右孩子的值,并从右孩子开始,继续向下调整。由于20不大于21,因此交换20和21。

接下来从17开始,因为17 < 18, 所以交换17与18的值。

接下来,由于19 < 21 ,所以交换19和21,又由于19 < 20,所以需要继续交换19和20。



3、 经过上一步,就得到了一个大顶堆,接下来就是堆排序的过程

在这个大顶堆中,堆顶元素是整个堆中的最大值,取出堆顶元素,并与堆中的最后一个元素交换位置,即21与19交换,然后调整堆(21不动)。不断重复这个过程,每一次取出堆顶元素,并与最后一个元素交换位置,直至最后一个元素就是堆顶,这样就可以得到一个自上而下的递增的堆。

public static void head_sort(int[] arr) {
// 取出堆顶元素,与最后一个元素交换,调整堆
for(int i = arr.length - 1; i >= 0; i--) {
int temp = arr[i]; // 最后一个元素
arr[i] = arr[0];
arr[0] = temp;
fix_down(arr, 0, i-1); // 注意此处是i-1; 刚刚交换后的最后一个元素不参与调整,即21不参与调整
}
}




堆排序完整代码如下:

/**
*
* @author lijialin
* @since 2017/08/28
*/
public class HeapSort {

public static void print(int[] arr) {
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}

public static void create_heap(int[] arr) {
for(int i = arr.length / 2 - 1; i >= 0; i--) {
fix_down(arr, i, arr.length-1);
}
}

public static void fix_down(int[] arr, int i, int end) {
int child = (i << 1) + 1; // 当前节点的左孩子
int temp = arr[i];

while(child <= end) {
// 选出两个孩子较大的那个
if(child < end && arr[child+1] > arr[child]) {
child++;
}
if(temp < arr[child]) {
arr[i] = arr[child]; // 孩子节点与当前节点替换
i = child;
child = (i << 1) + 1;
}else {
break;
}
}
arr[i] = temp;
}

public static void head_sort(int[] arr) {
// 取出堆顶元素,与最后一个元素交换,调整堆
for(int i = arr.length - 1; i >= 0; i--) {
int temp = arr[i]; // 最后一个元素
arr[i] = arr[0];
arr[0] = temp;
fix_down(arr, 0, i-1);

}
}

public static void main(String[] args) {
// TODO Auto-generated method stub

// Test case 1
int[] arr = {19,17,20,18,16,21};
create_heap(arr); // 创建堆
head_sort(arr);
print(arr); // 16 17 18 19 20 21

// Test case 2
int[] arr1 = {16,7,3,20,17,8};
create_heap(arr1);
head_sort(arr1);
print(arr1); // 3 7 8 16 17 20

// Test case 3
int[] arr2 = {5,4,3,2,1};
create_heap(arr2);
head_sort(arr2);
print(arr2); // 1 2 3 4 5

// Test case 4
int[] arr3 = {1,1,1,1};
create_heap(arr3);
head_sort(arr3);
print(arr3); // 1 1 1 1

}

}


总结:

堆排序是不稳定的排序

在待排序的文件中,若存在多个关键字相同的记录,经过排序后,这些记录之间的相对次序保持不变,该排序方法是稳定的;反之,若相对次序变化,则排序不稳定。

堆排序的时间复杂度,最好最差平均都是O(nlogn),空间复杂度是O(n)。父节点与子节点交换时,需要使用一个中间变量,所以是O(1),当然创建堆的空间不计算在内。

大顶堆小顶堆
父节点大于左右孩子父节点小于左右孩子
排序后从小到大排序后从大到小
参考,Python写的小顶堆:http://blog.csdn.net/u010429424/article/details/70038193

加油,再接再厉!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: