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

排序算法(七)-堆排序

2017-09-17 17:12 211 查看

1. 原理

1.1 什么是堆

要理解堆排序,首先要先理解什么是堆。堆是一颗顺序存储的完全二叉树,堆又分为最大堆和最小堆。

完全二叉树:设二叉树的深度为h,除h层外,其他各层的节点数都达到最大个数,第h层的所有节点都连续集中在最左边。

最小堆:每个节点的值都不大于其子节点的值的完全二叉树

最小堆:每个节点的值都不小于其子节点的值的完全二叉树

根据上面的描述我们可以用一个数学描述来定义最大最小堆:

对于数组[D(0), D(1), …., D(n)]当且仅当满足下列关系时称之为堆

最小堆:D(i) <= D(2 * i + 1)并且D(i) <= D(2 * i + 2)

最大堆:D(i) >= D(2 * i + 1)并且D(i) >= D(2 * i + 2)

其中整数i = 0, 1, 2, …, n/2

举个栗子:[3, 4, 7, 12, 15, 18]就是一个典型的最小堆, i <= 2。

i取0时:3 > 4, 3 > 4

i取1时:4 > 12, 4 > 15

i取2时:7 > 18

1.2 堆排序

理解了堆的概念之后,堆排序就是利用最大堆和最小堆的特性进行排序

将数组初始化成最大堆

交换最大堆的第一个和最后一个数字,输出最后一个数字(最大值)

将破坏后的最大堆重新调整为最大堆

接着重复2~3步直到交换堆的第一和第二个节点,此时结束排序

2. 实现

此算法的关键在于如何构建最大堆。根据上诉最大最小堆的定义,可以写出针对某一parent的调整算法。

public void adjustMaxHeap(int[] data, int parent, int length) {
int temp = data[parent];
int child = 2 * parent + 1;

while (child < length) {
//如有有右子节点,并且右子节点大于左子节点,则选取右子节点和parent比较
if (child + 1 < length && data[child] < data[child + 1]) {
child++;
}
//如果父节点的值大于子节点的值,则跳出循环
if (temp >= data[child]) {
break;
}
//把孩子节点的值赋给父节点
data[parent] = data[child];
//选取子节点的左子节点,继续向下调整
parent = child;
child = 2 * child + 1;
}

data[parent] = temp;
}


此函数为调整某个parent的值,初始化的时候需要从n/2开始一直循环调整到0;

for (int i = data.length / 2; i >= 0; i--) {
adjustMaxHeap(data, i);
}


完整对堆排序算法如下:

@Override
public int[] sort(int[] data) {
if (data == null || data.length <= 1) {
return data;
}

for (int i = data.length / 2; i >= 0; i--) {
adjustMaxHeap(data, i, data.length);
}

for (int i = data.length - 1; i > 0; i--) {
//最后一个数字和第一个数字交换
swap(data, 0, i);

//由于parent=0的节点发生了变化,因此需要重新调整堆
adjustMaxHeap(data, 0, i);
}

return data;
}


完整实现可查看:

GitHub/HeapSort

3. 复杂度

堆排序是一种不稳定的排序算法。平均,最坏和最好的时间复杂度都是O(nlogn)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息