正在载入交互式动画窗口请稍等
堆排序 可视化交互式动画版
堆排序是一种基于 二叉堆 数据结构的比较排序技术 。 它类似于 选择排序 ,我们首先找到最小元素并将最小元素放在开头。 对其余元素重复相同的过程。
堆排序算法
要解决该问题,请遵循以下想法:
首先使用heapify将数组转换为堆数据结构,然后逐个删除Max-heap的根节点并替换为堆中的最后一个节点,然后对堆的根进行heapify。 重复此过程,直到堆的大小大于 1。
- 从给定的输入数组构建一个堆。
- 重复以下步骤,直到堆只包含一个元素:
- 将堆的根元素(最大的元素)与堆的最后一个元素交换。
- 删除堆的最后一个元素(现在位于正确的位置)。
- 堆化堆的剩余元素。
- 排序数组是通过反转输入数组中元素的顺序获得的。
堆排序的详细工作原理
为了更清楚地理解堆排序,让我们采用一个未排序的数组并尝试使用堆排序对其进行排序。
考虑数组:arr[] = {4, 10, 3, 5, 1}。构建完整二叉树: 从数组构建完整二叉树。
转换为最大堆: 之后,任务是从未排序的数组构造一棵树,并尝试将其转换为 最大堆。
- 要将堆转换为最大堆,父节点应始终大于或等于子节点
- 在此示例中,由于父节点 4 小于子节点 10, 因此将它们交换以构建最大堆。
- 现在,作为父级的 4 小于子级 5 ,因此再次交换这两个值,结果堆和数组应如下所示:
执行堆排序: 删除每一步中的最大元素(即,将其移动到结束位置并将其删除),然后考虑剩余元素并将其转换为最大堆。
- 从最大堆中 删除根元素 (10) 。 为了删除这个节点,尝试将它与最后一个节点交换,即 (1)。 删除根元素后,再次对其进行堆化,将其转换为最大堆。
- 结果堆和数组应如下所示:
- 重复上面的步骤,就会像下面这样:
- 现在再次删除根(即3)并执行heapify。
- 现在,当根再次被删除时,它就被排序了。 排序后的数组将类似于 arr[] = {1, 3, 4, 5, 10} 。
堆排序的实现
- C++
- C
- Java
- Python3
- C#
- JavaScript
- PHP
C++
// C++ program for implementation of Heap Sort #include <iostream> using namespace std; // To heapify a subtree rooted with node i // which is an index in arr[]. // n is size of heap void heapify( int arr[], int N, int i)
{ // Initialize largest as root int
largest = i; // left = 2*i + 1 int
l = 2 * i + 1; // right = 2*i + 2 int
r = 2 * i + 2; // If left child is larger than root if
(l < N && arr[l] > arr[largest]) largest = l; // If right child is larger than largest // so far if
(r < N && arr[r] > arr[largest]) largest = r; // If largest is not root if
(largest != i) { swap(arr[i], arr[largest]); // Recursively heapify the affected // sub-tree heapify(arr, N, largest); } } // Main function to do heap sort void heapSort( int arr[], int N) { // Build heap (rearrange array) for ( int i = N / 2 - 1; i >= 0; i--)
heapify(arr, N, i); // One by one extract an element // from heap for ( int i = N - 1; i > 0; i--) { // Move current root to end swap(arr[0], arr[i]); // call max heapify on the reduced heap heapify(arr, i, 0); } } // A utility function to print array of size n void printArray( int arr[], int N) { for ( int i = 0; i < N; ++i) cout << arr[i] << " " ; cout << "\n" ; } // Driver's code int main() { int
arr[] = { 12, 11, 13, 5, 6, 7 }; int
N = sizeof (arr) / sizeof (arr[0]); // Function call heapSort(arr, N); cout << "Sorted array is \n" ; printArray(arr, N); } |
C
Java
Python3
C#
JavaScript
PHP
排序后的数组是 5 6 7 11 12 13
堆排序 的复杂度分析
时间复杂度:
O(N log N)
辅助空间:
O(log n),由于递归调用堆栈。
然而,对于迭代实现来说,辅助空间可以是O(1)。
关于堆排序的要点:
- 堆排序是一种就地算法。
- 它的典型实现不稳定,但可以使其稳定(请参阅 此 )
- 通常比实现良好的 QuickSort 慢 2-3 倍。 缓慢的原因是缺乏参考位置。
堆排序的优点:
- 高效的时间复杂度: 在所有情况下,堆排序的时间复杂度均为 O(n log n)。 这使得对大型数据集进行排序变得高效。 log n 因子 来自二叉堆的高度,它确保算法即使在元素数量较多的情况下也能保持良好的性能。
- 内存使用 – 内存使用量可以最小化,因为除了保存要排序的初始项目列表所需的内存之外,它不需要额外的内存空间来工作
- 简单性—— 它比其他同样高效的排序算法更容易理解,因为它不使用递归等先进的计算机科学概念。
堆排序的缺点:
- 成本高昂 :堆排序的成本很高。
- 不稳定 :堆排序不稳定。 它可能会重新排列相对顺序。
- 高效: 在处理高度复杂的数据时,堆排序不是很高效。
与堆排序相关的常见问题
Q1. 堆排序分为哪两个阶段?
堆排序算法由两个阶段组成。 在第一阶段,数组被转换为最大堆。 在第二阶段,最高的元素(即树根处的元素)被删除,剩余的元素用于创建新的最大堆。
Q2。 为什么堆排序不稳定?
堆排序算法不是一个稳定的算法。 此算法不稳定,因为在堆中执行的操作可能会更改等效键的相对顺序。
Q3。 堆排序是“分而治之”算法的一个例子吗?
堆排序根本 不是 分而治之的算法。 它使用堆数据结构来有效地对其元素进行排序,而不是使用“分而治之的方法”来对元素进行排序。
Q4。 哪种排序算法更好——堆排序还是归并排序?
答案在于它们的时间复杂度和空间要求的比较。 归并排序比堆排序稍快。 但另一方面,合并排序需要额外的内存。 根据要求,应选择使用哪一种。
Q5. 为什么堆排序比选择排序更好?
堆排序与选择排序类似,但有更好的方法来获取最大元素。 它利用堆数据结构在常数时间内获取最大元素