有一个远远不断的吐出整数的数据流,假设你有足够的空间来保存吐出的数,请设计一个名叫MedianHolder的结构,MedianHolder可以随时去的之前吐出所有数的中位数。
要求:1.如果MedianHolder已经保存了吐出的N个数,那么任意时刻将一个新数加入到MedianHolder的过程,其时间复杂度为O(logN)。
2.取得已经吐出的N个数整体的中位数的过程,时间复杂度为O(1)。
思路:准备两个堆,一个大根堆,一个小根堆。力争做到如果排完序之后,前n/2个数放在大根堆里面,后n/2个数放在小根堆里面。这样大根堆的堆顶和小根堆的堆顶就一定能压中中位数。比如7,3,4,5这样一个数据流,就把3,4放在大根堆里面,4在堆顶;7,5放在小根堆里面,5在堆顶。则4和5正好是可以压中中位数的位置。
具体做法是:一开始两个堆都是空的,遇到的第一个数默认放在大根堆里面,新的数记为new_number,如果这个数小于等于大根堆的堆顶,就把new_number放到大根堆的堆顶去;如果new_number大于大根堆的堆顶,就放到小根堆里面去。如果不平也好解决。比如如果第一个数是5,就把5放到大根堆里面,接下来再遇到3,则3放到大根堆里面去,这个时候大根堆有2个数,小根堆有0个数,而我们每加一个数,都考察一下两个堆的size大小是否超过1 。当一个size是2一个size是0的时候就超过了,这个时候大根堆的堆顶弹出一个5进小根堆,就又平了。接下里再遇到一个数6,大于大根堆的堆顶3,则6直接进小根堆;接下来又来一个数4,4比3大,则4也进小根堆,这时候两个堆size差值又超过1了,则小根堆的堆顶弹出一个4进大根堆,此时大根堆堆顶是5,小根堆堆顶是4,又压中了中位数的位置。所以整个策略就是,只关心大根堆的堆顶,判断当前数与大根堆堆顶的关系来决定当前数进大根堆还是小根堆。如果两个堆的size大小超过了1,就让较大的堆的堆顶弹出进另外一个堆。这样一直可以保证前n/2个数放在大根堆里面,后n/2个数放在小根堆里面。