建堆复杂度O(n)证明

堆排序中首先需要做的就是建堆,广为人知的是建堆复杂度才O(n),不过很少有人去了解过这个复杂度的证明过程,因为不是那么直观地可以一眼就看出来。本文不讲堆排序,只单纯讲建堆过程

建堆代码

欲了解复杂度的计算过程,必先看懂建堆代码。先看这个建堆过程

// 将arr[n]向上调整至合适位置
void AdjustHeap(vector<int> &arr, int n)
{
    if(n<=0) return ;
    if(arr[(n-1)/2] > arr[n]) {  //与父结点比较
        swap(arr[(n-1)/2], arr[n]);
        AdjustHeap(arr, (n-1)/2);   //递归调整
    }
}
// 小根堆
void BuildHeap(vector<int> &arr)
{
    for(int i=1; i<arr.size(); i++) {
        AdjustHeap(arr, i);
    }
}

因为不讲堆排序,所以这里用的是向上调整即可,更加直观易懂。相信这么简单的代码你应该能看懂它的原理。

复杂度计算

从直观上看,AdjustHeap()的调用深度最多为logn层,故复杂度上限为O(logn)。而BuildHead()中的循环为n-1次,故它的复杂度为O(nlogn),但这不是它的实际平均复杂度,而是一个估算的上界,它很可能永远达不到这个上界。下面来分析一下。

AdjustHeap(arr, 1) 比较次数最多为1次,最少为1次。
AdjustHeap(arr, 2) 比较次数最多为1次,最少为1次。
AdjustHeap(arr, 3) 比较次数最多为2次,最少为1次。
AdjustHeap(arr, 4) 比较次数最多为2次,最少为1次。
AdjustHeap(arr, 5) 比较次数最多为2次,最少为1次。
AdjustHeap(arr, 6) 比较次数最多为2次,最少为1次。
AdjustHeap(arr, 7) 比较次数最多为3次,最少为1次。
...
AdjustHeap(arr, n-1) 比较次数最多为logn次,最少为1次。

按最坏情况来算,将这些比较次数累加起来就是建堆的时间复杂度,显然最佳情况是O(n)。而最坏情况的比较次数复杂一些,为了方便计算,假设n是2的k次幂,则k = logn
\({1 + 1 + 2 + 2 + 2 + 2 + 3 + 3 + ... + k}\)
= \({1*2^1+2*2^2+3*2^3}+\cdots + {k*2^k}\)
=

原文地址:https://www.cnblogs.com/xcw0754/p/8398535.html

时间: 2024-10-05 09:05:34

建堆复杂度O(n)证明的相关文章

自下而上建堆(heap)的时间复杂度证明

作者:Tobin 日期:2019/04/13 缘由:看python cookbook时,用到了heapq的库,书中提到,如果仅仅是返回一个数组的最大值,用max就可以了,但是如果返回多个较大或者较小元素用堆,如果返回的个数接近于数组本身的元素个数时,直接用排序即可.那么我在想,为啥返回几个元素的时候用堆效果比较好呢?于是我翻开了尘封许久的<算法导论>. 什么是堆 堆是一种数据结构.二叉堆是一个数组,近似于一个完全二叉树.树上的每个结点对应于数组的一个元素,除了最底层外树是充满的.下面的图是一个

Python3实现最小堆建堆算法

今天看Python CookBook中关于“求list中最大(最小)的N个元素”的内容,介绍了直接使用python的heapq模块的nlargest和nsmallest函数的解决方式,记得学习数据结构的时候有个堆排序算法,所以顺便研究了一下“堆”结构(这里特指二叉堆). 概念 所谓二叉堆(binary heap)实际上就是一颗特殊的完全二叉树,其特殊性在于: 二叉树中所有的父节点的值都不大于/不小于其子节点: 根节点的值必定是所有节点中最小/最大的. 父节点值不大于子节点且根节点值最小称为最小堆

建堆[HihoCoder-1405]

Building Heap HihoCoder-1405 hihoCoder太阁最新面经算法竞赛11 问题大意:给定一个$N$个元素的数组$A$(元素互不相同),要求你建立满足下列要求的二叉树$T$,并输出其前序遍历: 1)$T$满足最小堆性质: 2)输入的数组$A$满足$T$的中序遍历. 这是一道数据结构基础题,主要是解决如何建堆.题目要求最小堆,其树根必然是整个树的最小值.因此在建堆过程中,只需寻找最小值,根据找到的最小值的位置将中序遍历的序列一分为二,继续对这两个子序列建堆,直到子序列没有

建堆,以及对堆排序

建堆,以及堆排序 代码1: #include<stdio.h> int h[101];//用来存放堆的数组 int n;//用来存储堆中元素的个数,就是堆的大小 //交换函数,用来交换堆中的俩个元素的值 void swap(int x,int y) { int t; t=h[x]; h[x]=h[y]; h[y]=t; } //向下调整函数 void siftdown(int i) {//传入一个须要向下调整的的节点编号i,这里传入1.即从堆的顶点開始向下调整 int t,flag=0;//f

堆+建堆、插入、删除、排序+java实现

package testpackage; import java.util.Arrays; public class Heap { //建立大顶堆 public static void buildMaxHeap(int[] a) { for(int i=(a.length/2)-1;i>=0;i--) { adjustDown(a,i,a.length); } } //向下调整 public static void adjustDown(int[] a,int i,int len) { int

算法导论笔记——第六七章 堆排序和快速排序

第六章 堆排序 最小堆和最大堆:近似的完全二叉树 A[parent(i)]<=A[i]或者A[parent(i)]>=A[i] 建堆复杂度O(n) 排序O(nlgn) 实际应用中,快速排序一般优于堆排序.可用于优先队列等. 在一个包含n个元素的堆中,所有优先队列的操作均可在O(lgn)时间内完成. 第七章 快速排序 与归并排序一样用分治思想 主元pivot可随机生成

堆与堆排序—优先队列

上一节我们写了树以及二叉树的知识 http://blog.csdn.net/wtyvhreal/article/details/43487095 堆是一种特殊的完全二叉树. 所有父节点都比子节点要小,这样的完全二叉树称为最小堆,反之叫最大堆. 下图一棵完全二叉树,调整为最小堆步骤: 向下调整的代码如下: 从上面可以得到:调整堆的时间复杂度是O(logN). 如果堆的大小为N,那么插入一个新元素所需要的时间为O(logN). 在刚刚那个最小堆里插入数值3 向上调整的代码如下: 建堆:从空的堆开始,

ADT - heap(堆)

二叉堆: 以前写过二叉堆,但很少使用,快忘了.最近又查了一些关于堆的资料,于是重新熟悉一下这种数据结构. 一个快速又简单的方式建立二叉堆,仅使用简单vector(或者数组也行): #include "stdafx.h" #include <iostream> #include <vector> #define LeftChild(i) (2*(i) + 1) #define RightChild(i) (2*((i) + 1)) template<clas

优先队列Priority Queue和堆Heap

对COMP20003中的Priority queue部分进行总结.图片来自于COMP20003 queue队列,顾名思义特点先进先出 priority queue优先队列,出来的顺序按照优先级priority大小,越大(小)的先pop. 普通的方法: Unsorted array: Construct: O(n) Get highest priority: O(n) Sorted array: Construct: O(n2) Get highest priority: O(1) 使用堆heap