1.堆的概念
堆(Heap):
- 堆是一种特殊的树状数据结构,通常用于实现优先队列和相关算法。
- 堆分为最大堆(Max Heap)和最小堆(Min Heap)两种类型,具体取决于根节点的值与子节点的关系。在最大堆中,根节点的值最大,而在最小堆中,根节点的值最小。
- 堆具有以下特性:
- 它是一个完全二叉树,通常使用数组来表示。
- 在最大堆中,每个节点的值都大于或等于其子节点的值。
- 在最小堆中,每个节点的值都小于或等于其子节点的值。
- 堆支持基本操作,如插入(将新元素插入堆中)、删除最大/最小元素(取出根节点,并调整堆结构以保持堆属性),以及堆排序等。
2.堆的构造
以下是构造最大堆的一般步骤:
-
从最后一个非叶子节点开始:最后一个非叶子节点是位于数组中的索引
(n/2 - 1)
,其中n
是堆中元素的数量。这是因为在一个堆中,叶子节点已经满足堆性质,只需要调整非叶子节点。 -
下沉操作:从最后一个非叶子节点开始,对每个节点执行下沉操作。下沉操作是指将当前节点与其子节点中较大(或较小,具体取决于堆类型)的节点交换,以确保节点满足堆性质。这个过程会递归地向下进行,直到整个堆满足堆性质。
-
重复步骤 2:继续从前一个非叶子节点向前移动,每次都执行下沉操作,直到到达堆的根节点(索引为0)。
下面是一个简单的示例,展示如何将数组构造为最大堆:
假设初始数组:[4, 10, 3, 5, 1]
-
从最后一个非叶子节点开始,也就是索引1(10),执行下沉操作,将10与其子节点中较大的数(5)交换,得到:[4, 5, 3, 10, 1]
-
继续向前,处理索引0的元素(4),执行下沉操作,将4与其子节点中较大的数(5)交换,得到最终的最大堆:[5, 4, 3, 10, 1]
构造最小堆的过程类似,只不过在下沉操作中会选择较小的子节点来交换。
总之,构造堆的过程是通过逐步调整堆中元素的顺序,使其满足堆性质。这个过程的时间复杂度通常为 O(n),其中 n 是堆中元素的数量。
3.堆的插入
-
将新元素添加到堆的底部:首先,将新元素插入到堆的底部,即数组的末尾。
-
执行上浮操作:然后,对新插入的元素执行上浮操作。上浮操作是指将当前节点与其父节点进行比较,如果当前节点比父节点大(或小,根据堆的类型不同),则交换它们,直到整个堆满足堆性质。
-
重复步骤 2:重复执行上浮操作,直到新元素到达合适的位置并满足堆性质。
下面是一个简单的示例,展示如何向最大堆中插入一个元素:
假设初始最大堆:[5, 4, 3, 2, 1],现在要插入元素6。
-
将元素6添加到堆的底部,得到:[5, 4, 3, 2, 1, 6]
-
对新插入的元素6执行上浮操作。6比其父节点5大,所以交换它们,得到:[6, 4, 5, 2, 1, 3]
-
再次执行上浮操作,直到元素6到达堆中合适的位置。最终得到满足堆性质的最大堆:[6, 4, 5, 2, 1, 3]
这样,就成功将元素6插入到了最大堆中,并保持了堆的性质。
插入操作的时间复杂度通常为 O(log n),其中 n 是堆中元素的数量。这是因为插入操作需要进行一系列的上浮操作,其次数受到堆的高度(树的层数)的影响,通常堆是近似平衡的,因此高度约为 log n。
4.堆的删除
-
删除堆顶元素:首先,将堆顶元素(最大值)删除,通常是将它与堆中的最后一个元素交换,并从堆中删除最后一个元素。这样可以确保堆的大小减少1。
-
执行下沉操作:接下来,对新的堆顶元素执行下沉操作,也就是将当前节点与其较大的子节点进行比较并交换,直到整个堆再次满足堆性质。
-
重复步骤 2:重复执行下沉操作,直到新的堆顶元素到达合适的位置并满足堆性质。
下面是一个简单的示例,展示如何从最大堆中删除最大值:
假设初始最大堆:[6, 4, 5, 2, 1, 3],现在要删除最大值。
-
删除堆顶元素6,并将它与堆中的最后一个元素3交换,得到:[3, 4, 5, 2, 1]
-
对新的堆顶元素3执行下沉操作。3比其两个子节点4和5小,所以与较大的子节点5交换,得到:[5, 4, 3, 2, 1]
-
再次执行下沉操作,直到新的堆顶元素5到达堆中合适的位置并满足堆性质。最终得到满足堆性质的最大堆:[5, 4, 3, 2, 1]
这样,就成功删除了最大堆中的最大值,并保持了堆的性质。
删除操作的时间复杂度通常为 O(log n),其中 n 是堆中元素的数量。这是因为删除操作需要进行一系列的下沉操作,其次数受到堆的高度(树的层数)的影响,通常堆是近似平衡的,因此高度约为 log n。