今天来了解一下堆排序的问题,“堆”是个很有趣的结构。通常“堆”是通过数组来实现的,这样可以利用数组的特点快速定位指定索引的元素。而对“堆”的操作中定位某个元素简直就是家常便饭。为什么这么说呢,我们来看看“堆”的定义:
在起始索引为 0 的“堆”中:
1) 堆的根节点将存放在位置 0
2) 节点 i 的左子节点在位置 2 * i + 1
3) 节点 i 的右子节点在位置 2 * i + 2
4) 节点 i 的父节点在位置 floor( (i - 1) / 2 ) : 注 floor 表示“取整”操作
在起始索引为 1 的“堆”中:
1) 堆的根节点将存放在位置 1
2) 节点 i 的左子节点在位置 2 * i
3) 节点 i 的右子节点在位置 2 * i + 1
4) 节点 i 的父节点在位置 floor( i / 2 ) : 注 floor 表示“取整”操作
以下是一个“堆”的图例:
因此,当我们得知某个节点的索引为 i 时,可以通过以下“堆”的定义很容易地定位到它的子节点和父节点。
不仅如此,“堆”还有个特性:每个节点的键值一定总是大于(或小于)它的父节点。如果我们修改了某个节点的键值,这样就会破坏“堆”的这个特性,因此这时我们就要根据该节点的新键值与它的父节点和子节点的键值进行比较,对该节点进行“上移”或者“下移”操作,使之能够重新放置到合适位置。
这里我们了解一下“堆”的这两个很重要的操作:“上移”和“下移”。
这里我们假设这是一个“最大堆”,即“堆”的根节点保存的是键值最大的节点。即“堆”中每个节点的键值都总是大于它的子节点。
当某节点的键值大于它的父节点时,这时我们就要进行“上移”操作,即我们把该节点移动到它的父节点的位置,而让它的父节点到它的位置上,然后我们继续判断该节点,直到该节点不再大于它的父节点为止才停止“上移”。
现在我们再来了解一下“下移”操作。当我们把某节点的键值改小了之后,我们就要对其进行“下移”操作。首先,我们要取该节点的两个左右子节点中键值较大的一个,与该节点进行比较,如果该节点小于它的这个子节点,就把该节点移动到它的子节点的位置,而让它原来的子节点升到它的位置上。然后我们继续判断该节点,直到该节点不再小于它的左右子节点为止才停止“下移”。
现在我们再来看看如何把一个无序的序列建立成为一个“堆”?
根据“堆”的特性,我们可以从无序序列的最后一个节点的父节点开始,对其进行“下移”操作,直到序列的第一个节点为止。这样就可以保证每个节点都在合适的位置上,也就建立起了一个“堆”。
但是“堆”并不是一个完全排序的序列,因为“堆”只保证了父节点与子节点的位置关系,但并不保证左右子节点的位置关系。那么我们如何进行“堆排序”呢?
由于一个“堆”的根节点必然是整个序列中最大的元素,因此对于一个排序的序列而言,每个“堆”中我们只能得到一个有效的元素。如果我们拿掉根节点,再对剩下的序列重新排列组成一个“堆”,反复如此,我们就可以依次得到一个完整的排序序列了。
当然,为了简化操作,每次我们只需要把根节点与最后一个位置的节点交换,然后把最后一个位置排除之外,对根节点进行“下移”操作即可。
![点击查看原始大小图片](http://dl2.iteye.com/upload/attachment/0025/1138/abe0f3b2-4990-3791-bef4-88517c9af9f2-thumb.bmp)
- 大小: 4.2 KB
分享到:
相关推荐
已知(k1, k2, …, kp)是堆,则可以写一个时间复杂度为O(logn)的算法,将(k1, k2, …, kp, kp+1)调整为堆。试编写“从p=1起,逐个插入建堆”的算法,并讨论由此方法建堆的时间复杂度。
建堆,并分析时间复杂度——————
总结起来,"MaxHeap堆排序建堆类"是一个实现了最大堆概念的类,它可能包含了建堆、下沉、交换和排序等关键操作。通过测试类如"TinyTest",我们可以验证这个实现是否正确并符合预期。理解和掌握堆排序及其相关算法...
### 建堆时间复杂度解析 在计算机科学与数据结构领域中,堆是一种非常重要的数据结构,广泛应用于各种算法中,尤其是堆排序算法。本文将深入探讨建堆操作的时间复杂度,尤其关注如何理解从单个节点进行堆化到整体建...
首先,我们来详细了解建堆的过程。建堆是从无序数组构建一个合法的堆的过程。对于最大堆,通常从最后一个非叶子节点开始,即数组长度除以2向下取整的位置,然后逐个检查其子节点,确保它们满足堆的性质。如果发现不...
在Java中实现堆的操作,主要包括建堆、插入和删除三大功能。下面将详细阐述这三个操作的原理以及如何用Java代码实现它们。 ### 建堆(Heapify) 建堆的操作目的是将一个无序的数组调整成一个堆结构。建堆可以分为...
快速排序和堆排序是两种非常重要的内部排序算法,在计算机科学中有着广泛的应用。它们都是基于比较的排序方法,但各自有着独特的实现策略和性能特点。 快速排序由C.A.R. Hoare在1960年提出,其核心思想是分治法。...
在堆排序的过程中,通常包括两个主要步骤:建堆和调整堆。 1. 建堆:将待排序的序列构造成一个大顶堆或小顶堆。这个过程通常从最后一个非叶子节点开始,自底向上进行。对于每个节点,如果它比它的子节点小(大顶堆...
建堆:建堆是指将一个无序序列转换为一个堆的过程。解决方法是:将序列中的元素逐个加入堆中,并在加入每个元素时,对堆进行调整,使其满足堆的性质。 堆排序的实现:堆排序的实现可以通过以下步骤来完成: 1. 将...
3. 建堆(Heapify):对于一个无序数组,可以通过下沉操作从下往上遍历,将每个非叶子节点调整到位,从而构建一个合法的堆。 4. 堆排序:堆可以用来实现快速的排序算法——堆排序。首先,将数组转换为最大堆,然后...
建堆过程中,我们通常从最后一个非叶子节点开始,自底向上地对每个节点进行下沉操作,确保其满足堆属性。调整堆则是从根节点开始,取出最大元素(大顶堆),然后将最后一个元素下移至根部,并再次调整堆。这个过程...
建堆通常从最后一个非叶子节点开始,按照从下到上、从右到左的顺序进行调整,确保每个节点都满足堆的性质。在最大堆中,这个过程被称为下沉操作;在最小堆中,对应的是上浮操作。插入元素时,将新元素放入末尾并从该...
内容概要:本文详细介绍了堆排序算法的基本概念、建堆和排序过程,以及其时间复杂度和空间复杂度。堆排序是一种高效的基于比较的排序算法,利用二叉堆的数据结构特性进行排序。具体步骤包括建堆和排序过程,通过下沉...
堆排序的过程分为两个主要阶段:建堆和调整堆。在建堆阶段,我们把待排序的元素构造成一个大顶堆(或小顶堆)。这个过程从最后一个非叶子节点(即数组的中间元素)开始,逐个向上回溯,确保每个父节点的值都大于或...
堆排序是一种基于比较的排序算法,分为两个主要阶段:建堆和排序。在建堆阶段,将待排序的序列构造成一个大顶堆,然后在排序阶段,不断将堆顶元素(最大值)与末尾元素交换并缩小堆的范围,直到所有元素都按照顺序...
堆排序算法主要分为两个关键步骤:建堆和调整堆。 1. **建堆**: - 首先,将待排序的序列构造成一个大顶堆(或小顶堆)。这个过程通常从最后一个非叶子节点(即数组长度除以2的下标位置)开始,逐级向下进行。 - ...
创建堆的过程称为建堆,通常从最后一个非叶子节点开始,自底向上地调整。调整过程确保每个父节点都大于或小于其子节点,使得整个数据结构满足堆的性质。这个过程也被称为下沉操作。 接下来是核心的排序步骤——交换...
堆排序分为两个主要步骤:建堆和排序。 在堆排序中,我们使用最大堆(Max Heap)来进行排序,步骤如下: 建堆:将待排序的序列构造成一个最大堆。这个过程从最后一个非叶子节点开始向前遍历,对每个节点进行堆调整...
堆排序的基本步骤包括建堆、交换根节点与最后一个元素、减小堆的大小并重新调整堆的过程,这个过程反复执行直至完成排序。 实验目的: 1. 理解数组上不同排序算法的工作原理和实现方法。 2. 熟悉快速排序的单趟排序...
堆排序分为两个主要步骤:建堆和调整堆。 首先,我们理解一下什么是堆。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。对于最大堆,每个节点的值都大于...