zoukankan      html  css  js  c++  java
  • 利用最大堆和最小堆在线寻找中位数

    题外话:
    前段时间参加校园招聘,经常在一些公司的笔试或者面试中遇到一些不错的算法题,回到宿舍和同学进行交流,收获许多。这段时间,工作定下来后,整天闲着没事,就整理之前一些不错的算法题及其想法。下面这个算法题是一个同学去参加百度校园招聘面试时遇到的题目,当时他写了一篇日志。看到他那篇日志,我和舍友小平同学讨论了两三个小时。下面对当时的想法进行一些整理。

    问题:
    给定n个int型的数和一个空的集合,每次往集合中插入一个数,每次插入之后给出这个集合的中位数。(中位数的概念是:如果集合有奇数个数,给出排序后处在最中间的那个数;如果是偶数个数,给出排序后最中间两个数的均值。)

    分析:
    该同学在日记里写到,他看到题目时想到的是O(N*N)的算法,但是没有说具体的算法步骤和思想。我猜想,他可能是想在插入时是采用插入排序的思想,则在集合中插入一个数的复杂度为O(N),从排好序的集合中选出中位数则是一个复杂度为O(1)的过程,总共插入N次,所以总复杂度为O(N*N)。

    小平和我在看到题目时,第一时间想到的都是“二分”。在每次将数插入集合时,采用“二分查找”寻找该数在集合中的位置,这个过程的复杂度是O(log(N)),得出中位数的过程则是O(1),插入了N次,所以复杂度为O(N*log(N))。

    关于二分插入的思想,上面的复杂度分析好像毋庸置疑。但是,上面的分析过程是不对的。采用二分查找,则集合必须能随机访问,而这只能使用数组来实现。然而,在数组插入一个数,则需要将该位置后面的所有元素向后移动。上面的思想中,在二分查找到一个数的位置,并将该数插入集合中,则需要后移该位置后的所有元素。在最坏情况下,每次插入的位置都是第一个,则“移动元素”操作的复杂度为O(N),那么该思想的复杂度仍然是O(N*N)。

    有个同学在该日记的回复给了我思路,该同学的回复是:

    “感觉用堆也是可以的,所有比中位数大的组成一个最小堆,比中位数小的组成一个最大堆,每次插入只进行一个堆的插入,然后中位数一定是原中位数、最大堆的最大值和最小堆最小值中产生,再进行堆的调整就可以了,不过复杂度也是NlogN”

    刚开始,小平和我对这个回复进行了一下讨论,但是没讨论出一个结果。但是,这个“最大堆和最小堆”却一直萦绕在我的潜意识中。躺在床上,准备睡觉的时候,恍然大悟:可以使用两个变量来控制最大堆和最小堆中元素的个数差。

    具体的思路如下:

    集合中元素,前一半存储在一个最大堆中,后一半存储在一个最小堆中。使用变量MaxHeapNum记录最大堆元素的个数,使用变量MinHeapNum记录最小堆元素的个数。控制MaxHeapNum与MinHeapNum的差不能超过1。每次将要插入的元素Num与最大堆顶部元素MaxHeapTop和最小堆的顶部元素MinHeapTop将进行比较,根据具体情况进行插入:

    1.如果Num < MaxHeapTop,则
      1.1 如果MaxHeapNum <= MinHeapNum,将Num插入最大堆;
      1.2 如果MaxHeapNum == MinHeapNum + 1,将MaxHeapTop从最大堆中移到最小堆,并将Num插入最大堆。
    2.如果MaxHeapTop <= Num <= MinHeapTop,则
      2.1 如果MaxHeapNum <= MinHeapNum,将Num插入最大堆;
      2.2 如果MaxHeapNum == MinHeapNum + 1,将Num插入最小堆;
    3.如果Num > MinHeapTop,则
      3.1 如果MinHeapNum <= MaxHeapNum,将Num插入最小堆;
      3.2 如果MinHeapNum == MaxHeapNum + 1,将MinHeapTop移到最大堆中,将Num插入最小堆。

    在每次插入后,都要根据情况对MaxHeapNum和MinHeapNum进行变更,并将有改动的堆进行堆调整。

    上面的插入情况会保证最大堆和最小堆的元素个数差小于1,中位数就只在最大堆和最小堆的顶部元素中产生:如果最大堆和最小堆的元素个数相等,则中位数为最大堆和最小堆的顶部元素的平均值;否则,中位数为元素个数多的那个堆的堆顶元素。

    复杂度分析:每次插入元素时的堆调整平均复杂度为O(log(N/2)),插入N次,所以总的复杂度为O(N*log(N/2))。

    总结:
    前面插入排序的思想和二分查找的思想之所以复杂度高,是因为其做了许多寻找中位数之外的操作,即排序。题目只是要求每次插入集合时,求出集合的中位数,而对集合中的元素是否排序没有要求。寻找中位数,我只需要知道中间的数就OK了,没有必要对所有元素排好序。这正是最后一种思想的精髓:尽量减少额外操作的消耗。

  • 相关阅读:
    需要应用打开此 exe 解决办法:下载安装某软件所至,中毒了,
    AndroidManifest.xml清单文件详解--application节点
    [CSS 3] Solved: DOM element which is visible but not clickable: pointer-events
    [Cloud Architect] 2. Resiliency
    [Cloud Architect] 1. Design for Availability, Reliability, and Resiliency
    [Supabase] Use Triggers to Automatically Update Your Supabase Tables
    [SAA + SAP] 31. Migrations
    [SAA + SAP] 30. More solution Architectures
    [Javascript] event.target.closest(selector)
    [Javascript] Object.is() vs ===
  • 原文地址:https://www.cnblogs.com/lienhua34/p/2381299.html
Copyright © 2011-2022 走看看