面试必问之堆排序及堆

     已经参加过很多公司的面试,如百度、腾讯、阿里,他们都问到了堆排序,虽然问的形式不一样,但都是堆排序的那些东西。今天我总结下我所被问到的堆排序问题。

堆排序定义:

“堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法”,时间复杂度是O(NlogN),是基于关键字比较排序算法里比较理想的排序算法(另个是快速排序),空间复杂度O(1).

堆分为最大堆和最小堆,最大堆的根节点最大,最小堆的根节点最小;最大堆用于递增堆排序,最小堆用于递减堆排序,下文提到的堆是指最大堆。

堆排序的实现可以分为两个子算法:初始建堆和进行堆排序,我见过两种堆排序的实现方法,其中进行堆排序的算法大同小异,主要是初始建堆略有不同。

实现方法:

方法一:

     upHeap(A,i):数组A中第1~i-1个元素符合对结构,加入第i个元素后进行的堆结构调整,使1~i元素复合堆结构。这个算法很容易实现,时间复杂度是O(logi)。

 初始建堆时,逐渐加入元素即可实现初始建堆的功能;(堆排序中,元素是从左到右进行1到n的编号,对应到数组的相应Index,这是为了根节点下标和其左右子结点的下标更好的对应关系)

for(int i=1;i<=N;i++)
     upHeap(A,i);

所以初始建堆的复杂度是O(NlogN);


HeapAdjust(A,n):堆结构调整,第1个元素的左右子树(2~n范围内)是堆结构,调整元素使数组A[1]~A[n]整个是个堆结构(即,出来堆顶元素不符合堆的要求,其子树都是堆),其时间复杂度是O(logn)。

进行堆排序:初始建堆后,将堆顶元素和最后一个元素对换,然后对前n-1个元素堆结构调整;

for(i=n;i>1;i--)
{
     swap(A[1],A[i]);
     heapAdjust(A,i-1);
}

所以进行堆排序的时间复杂度也是O(NlogN);总体对排序的复杂度就是O(NlogN)。


方法二:

        方法二和方法一的主要区别是初始建堆上,初始化堆时是对所有的非叶子结点进行筛选。最后一个非叶子节点的下标是[n/2]向下取整,筛选只需要从第[n/2]向下取整个元素开始,从后往前进行调整。 

  从最后一个非叶子结点开始,每次都是从父结点、左孩子、右孩子中进行比较交换,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。此处的堆结构调整和方法一的类似.

HeapAdjust(A,i,n):堆结构调整,第i个元素的左右子树(i+1~n外围内)是堆结构,调整元素是数组A[i]~A[n]整个是个堆结构;思想是找出第i个元素的左右子结点总最大的节点(下标m),然后和节点i比较,若元素i大,则停止,负责将节点m和节点i交换,递归调整节点m的堆结构HeapAdjust(A,m,n),具体实现见http://blog.csdn.net/shuilan0066/article/details/8659235,时间复杂度为O(logn)。

初始建堆算法:

 int iFirst=(N-1)/2; //第一个非叶子节点  
 for (;iFirst>=0;iFirst--)  
{  
    AdjustHeapNode(A,iFirst,N);  
} 

进行堆排序算法:

for (int i=N;i>1;i--)  
{  
   swap(A[1],A[i])
   AdjustHeapNode(A,1,i-1);  
}

容易看出进行堆排序的算法时间复杂度是O(NlogN),初始建堆时间复杂度 貌似也是O(NlogN),所以总体堆排序的复杂度是O(NlogN)。

但是!此方法初始建堆的时间复杂度并不是O(NlogN),而是O(N)!  具体证明可参考http://blog.sina.com.cn/s/blog_714dacd10101809a.html


适用范围:

     最大的优点是   时间复杂度是O(NlogN),空间复杂度O(1),所以可以用于对时间可空间都要求严格的排序。

     另外堆排序中的堆结构很适合从大数量数据中选出最大或最小的固定数目的元素,其中要选出最大的k个元素,只需要遍历一遍,并维持一个k大小的最小堆(堆顶元素最小),遍历到元素i时,如果元素i比堆顶元素大,则将堆顶元素换成元素i并调整堆结构,否则继续遍历元素i+1,时间复杂是O(NlogK).

      C++中优先队列(priority_queue)的内部实现就用到了堆,优先队列的pop()函数就是将堆顶元素删除,调整堆结构使得堆顶元素最小(或最大),push()函数就是加入一个元素,调整堆结构。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值