【每日算法】堆排序
2017-07-23 19:31
169 查看
1、堆:堆数据结构是一种数组对象。
对于表示堆的数组arr[0…n-1],我们以arr[0]为根,给定某个节点下标i,令其父节点和左右后代节点的下标为:
parent(i) = (i-1)/2;
left(i) = 2*i+1;
right(i) = 2*i+2;
堆分为最大堆和最小堆,上面就是最大堆,特点就是:除根节点以外的每个节点i,都有arr[ parent(i) ] >= arr[i]。最小堆的特点则是:除根节点以外的每个节点i,都有arr[ parent(i) ] <= arr[i]。
堆排序一般使用最大堆,最大堆中的最大元素位于根节点。
因为具有n个元素的堆是基于一颗完全二叉树的,所以其高度为O(log n)。
2、如何保持堆中的性质:
首先,我们假定以节点i的左右儿子为根的两棵二叉树都是最大堆,而以节点i为根的二叉树可能不是最大堆,则调整的过程如下:
从元素arr[i], arr[left(i)], arr[right(i)]中找出最大的元素,将下标存在largest中;
如果arr[i]是最大的,说明以节点i为根的二叉树是最大堆,无须调整,程序结束;否则,交换arr[i]和arr[largest],于是arr[i], arr[left(i)], arr[right(i)]三者满足了最大堆的性质,但是交换后,下标为largest的节点存放arr[i]的值,以该节点为根的子树又可能违反最大堆的性质,因此需要对该子树递归调用本调整过程
代码:
3、建堆:
上面保持堆的性质是一个铺垫,它也是堆算法中的核心部分,后面我们将利用它完成建堆和堆排序。
我们先看看如何使用maxHeapify()来将一个数组arr[0…size-1]变成一个最大堆。
对于每一片树叶,我们都可以看作是一个只含一个元素的堆。于是对于叶子节点的父亲节点(左右子树都是最大堆),我们可以调用maxHeapify()来进行调整。调整之后,我们得到更大的堆,对于这些堆的父节点,我们又可以调用maxHeapify()来进行调整。
为保证maxHeapify()的调用前提,我们只需从最下面的非叶子节点开始调整,一直到根节点,整个堆建立完毕。
那么,最下面的非叶子节点的下标是多少?
在这里我只给出结论,有兴趣的读者可以尝试证明一下:
当用数组表示存储了n个元素的堆时,叶子节点的下标为:n/2, n/2+1, … , n-1。 (n/2表示向下取整)
于是我们的调整顺序为n/2-1, … , 0:
代码:
buildMaxHeap(int arr[], int size)
{
for (int i = size/2-1; i >= 0; --i)
maxHeapify(arr, i, size);
}
4、堆排序:
为进行原地排序,我们引入另一个变量:heap_size,它用来表示堆的大小,而用size来表示数组的大小。
于是数组arr[0…size-1]中,arr[0…heap_size-1]为堆,arr[heap_size, size-1]为排好序的元素。
由最大堆的性质可知道,arr[0]存放着堆中最大的元素,于是可以利用该性质如下排序:
初始heap_size = size,调用buildMaxHeap(arr, heap_size)建立最大堆;
令i = size-1,交换arr[0]和arr[i],heap_size–,i–;
交换后,原来根的子女仍是最大堆,而根元素则可能违背了最大堆的性质,所以调用maxHeapify(arr, 0, heap_size)进行调整;
重复以上过程,直到堆的大小变为2,此时再重复一次以上过程,整个数组便从小到大排好序了。
代码:
void heapSort(int arr[], int size)
{
if (NULL == arr || size <= 0)
return ;
int heap_size = size;
buildMaxHeap(arr, heap_size);
for (int i = size-1; i >= 1; --i)
{
exchange(arr[0], arr[i]);
--heap_size;
maxHeapify(arr, 0, heap_size);
}
}
堆排序(heapsort)的时间复杂度为O(n logn),不稳定。
对于表示堆的数组arr[0…n-1],我们以arr[0]为根,给定某个节点下标i,令其父节点和左右后代节点的下标为:
parent(i) = (i-1)/2;
left(i) = 2*i+1;
right(i) = 2*i+2;
堆分为最大堆和最小堆,上面就是最大堆,特点就是:除根节点以外的每个节点i,都有arr[ parent(i) ] >= arr[i]。最小堆的特点则是:除根节点以外的每个节点i,都有arr[ parent(i) ] <= arr[i]。
堆排序一般使用最大堆,最大堆中的最大元素位于根节点。
因为具有n个元素的堆是基于一颗完全二叉树的,所以其高度为O(log n)。
2、如何保持堆中的性质:
首先,我们假定以节点i的左右儿子为根的两棵二叉树都是最大堆,而以节点i为根的二叉树可能不是最大堆,则调整的过程如下:
从元素arr[i], arr[left(i)], arr[right(i)]中找出最大的元素,将下标存在largest中;
如果arr[i]是最大的,说明以节点i为根的二叉树是最大堆,无须调整,程序结束;否则,交换arr[i]和arr[largest],于是arr[i], arr[left(i)], arr[right(i)]三者满足了最大堆的性质,但是交换后,下标为largest的节点存放arr[i]的值,以该节点为根的子树又可能违反最大堆的性质,因此需要对该子树递归调用本调整过程
代码:
//arr[0...size-1] void maxHeapify(int arr[], int i, int size) { int l = left(i); int r = right(i); int largest = i; if (l < size && arr[l] > arr[largest]) largest = l; if (r < size && arr[r] > arr[largest]) largest = r; if (largest != i) { exchange(arr[i], arr[largest]); maxHeapify(arr, largest, size); } }
3、建堆:
上面保持堆的性质是一个铺垫,它也是堆算法中的核心部分,后面我们将利用它完成建堆和堆排序。
我们先看看如何使用maxHeapify()来将一个数组arr[0…size-1]变成一个最大堆。
对于每一片树叶,我们都可以看作是一个只含一个元素的堆。于是对于叶子节点的父亲节点(左右子树都是最大堆),我们可以调用maxHeapify()来进行调整。调整之后,我们得到更大的堆,对于这些堆的父节点,我们又可以调用maxHeapify()来进行调整。
为保证maxHeapify()的调用前提,我们只需从最下面的非叶子节点开始调整,一直到根节点,整个堆建立完毕。
那么,最下面的非叶子节点的下标是多少?
在这里我只给出结论,有兴趣的读者可以尝试证明一下:
当用数组表示存储了n个元素的堆时,叶子节点的下标为:n/2, n/2+1, … , n-1。 (n/2表示向下取整)
于是我们的调整顺序为n/2-1, … , 0:
代码:
buildMaxHeap(int arr[], int size)
{
for (int i = size/2-1; i >= 0; --i)
maxHeapify(arr, i, size);
}
4、堆排序:
为进行原地排序,我们引入另一个变量:heap_size,它用来表示堆的大小,而用size来表示数组的大小。
于是数组arr[0…size-1]中,arr[0…heap_size-1]为堆,arr[heap_size, size-1]为排好序的元素。
由最大堆的性质可知道,arr[0]存放着堆中最大的元素,于是可以利用该性质如下排序:
初始heap_size = size,调用buildMaxHeap(arr, heap_size)建立最大堆;
令i = size-1,交换arr[0]和arr[i],heap_size–,i–;
交换后,原来根的子女仍是最大堆,而根元素则可能违背了最大堆的性质,所以调用maxHeapify(arr, 0, heap_size)进行调整;
重复以上过程,直到堆的大小变为2,此时再重复一次以上过程,整个数组便从小到大排好序了。
代码:
void heapSort(int arr[], int size)
{
if (NULL == arr || size <= 0)
return ;
int heap_size = size;
buildMaxHeap(arr, heap_size);
for (int i = size-1; i >= 1; --i)
{
exchange(arr[0], arr[i]);
--heap_size;
maxHeapify(arr, 0, heap_size);
}
}
堆排序(heapsort)的时间复杂度为O(n logn),不稳定。
相关文章推荐
- 【每日算法】选择排序算法之堆排序
- 每日一算法之堆和堆排序
- 每日一道算法题(6)
- 算法之排序-----排序第四篇 堆排序
- 【每日一道算法题】
- 算法-堆排序
- 【算法总结】堆及堆排序总结
- 【每日一道算法题】
- 算法举例说明————堆排序
- [算法系列之一]堆排序
- 算法----堆排序(heap sort)
- 算法实现之简单选择排序、二元选择排序和堆排序
- 每日算法(数组与字符串二)
- 经典算法题每日演练——第一题 百钱买百鸡
- 堆排序(Heap Sort)算法学习
- 经典算法题每日演练——第二十二题 奇偶排序
- 每日两算法8.28
- C# 实现常用的算法-- 堆排序(转)
- 每日一道算法题1 ——把二元查找树转变成排序的双向链表
- java每日小算法(2)