如何得到乙個資料流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。
例如,[2,3,4] 的中位數是 3
[2,3] 的中位數是 (2 + 3) / 2 = 2.5
設計乙個支援以下兩種操作的資料結構:
void addnum(int num) - 從資料流中新增乙個整數到資料結構中。
double findmedian() - 返回目前所有元素的中位數。
示例 1:
輸入:[「medianfinder」,「addnum」,「addnum」,「findmedian」,「addnum」,「findmedian」]
[,[1],[2],,[3],]
輸出:[null,null,null,1.50000,null,2.00000]
示例 2:
輸入:[「medianfinder」,「addnum」,「findmedian」,「addnum」,「findmedian」]
[,[2],,[3],]
輸出:[null,null,2.00000,null,2.50000]
限制:最多會對 addnum、findmedia進行 50000 次呼叫。
將資料放到陣列,需要找到中位數的時候排序即可。
o(n)
每次插入時先用二分查詢找到合適位置並插入元素,也就是說每次插入前後都是排好序狀態,這樣中位數就很好找出了。
採用兩個優先順序佇列分別模仿兩個堆,乙個堆為大根堆,另乙個為小根堆。
每次交替向其中乙個堆放元素,並取出堆頂元素放入另乙個堆內。
最終形成的兩個堆內,大根堆的堆頂為靠近中位數的較小值;小根堆的堆頂為中位數或靠近中位數的較大值
class
medianfinder
public
void
addnum
(int num)
else
flip =
1-flip;
}public
double
findmedian()
else
}public
static
void
main
(string[
] args)
}
o(logn)
o(n)
面試時如果直接用priorityqueue
面試官可能會不滿意,就是考察你寫堆你特麼給我個封裝好的類?
所以這裡自己實現。
class
medianfinder
public
void
addnum
(int num)
// 3. 將大頂堆堆頂節點推出,即交換大頂堆的堆頂節點好堆尾節點並重新從堆頂自頂向下調整該堆
int tmp = max.
get(0)
; max.
set(
0,max.
get(max.
size()
-1))
; max.
remove
(max.
size()
-1);
adjustmaxheap(0
, max, max.
size()
);// 4. 將大頂堆中最大的元素加入小頂堆
min.
add(tmp)
;// 5. 插入資料後,從該節點的父節點開始自底向上一次調整堆
i = min.
size()
>1?
(min.
size()
-2)/
2:-1
;while
(i >=0)
// 6. 這樣操作一波後,即將元素先加入大頂堆調整後,將最大的元素加入小頂堆並調整
// 此時大頂堆存放最小的那部分元素,小頂堆存放最大的那部分元素
}else
int tmp = min.
get(0)
; min.
set(
0,min.
get(min.
size()
-1))
; min.
remove
(min.
size()
-1);
adjustminheap(0
, min, min.
size()
);max.
add(tmp)
; i = max.
size()
>1?
(max.
size()
-2)/
2:-1
;while
(i >=0)
} even =
!even;
}public
double
findmedian()
else
}/**
* 自頂向下調整大頂堆
*/public
void
adjustmaxheap
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;for
(int i =
2* index+
1; i < k; i =
2* i+1)
if(tmp < nums.
get(i)
)else
} nums.
set(index,tmp);}
/** * 自頂向下調整小頂堆
*/public
void
adjustminheap
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;for
(int i =
2* index+
1; i < k; i =
2* i+1)
if(tmp > nums.
get(i)
)else
} nums.
set(index,tmp);}
}
上乙個版本調整堆全部用的自頂向下的方法,其實插入元素時應該用自底向上調整。
class
medianfinder
public
void
addnum
(int num)
else
even =
!even;
}public
double
findmedian()
else
}/**
* 自頂向下調整大頂堆
*/public
void
adjustmaxheap
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;for
(int i =
2* index+
1; i < k; i =
2* i+1)
if(tmp < nums.
get(i)
)else
} nums.
set(index,tmp);}
/** * 自底向上調整大頂堆
*/public
void
adjustmaxheapup
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;int parent;
while
(index >0)
else
} nums.
set(index,tmp);}
/** * 自頂向下調整小頂堆
*/public
void
adjustminheap
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;for
(int i =
2* index+
1; i < k; i =
2* i+1)
if(tmp > nums.
get(i)
)else
} nums.
set(index,tmp);}
/** * 自底向上調整小頂堆
*/public
void
adjustminheapup
(int index, list
nums,
int k)
int tmp = nums.
get(index)
;int parent;
while
(index >0)
else
} nums.
set(index,tmp);}
}
雙堆求動態中位數
acwing 106.動態中位數 這題可以用兩個堆完成,用乙個大根堆儲存中位數左邊的數,用乙個小根堆儲存中位數右邊的數。因此,我們需要維護兩個堆的乙個性質 兩個堆的數字數目需要相同 為了方便,將中位數也算在左邊的大根堆中,所以實際上大根堆會比小根堆的數目多乙個 每次讀入乙個數,判斷大根堆 down ...
動態求中位數 堆 優先佇列
堆是一種類似二叉樹的結構,分為小根堆和大根堆,小根堆是父節點的值比子節點小,大根堆是父節點的值比子節點大。堆是用陣列來實現的,0為根節點,每個結點的子節點下標為2 i 1和2 i 2 如果要保證堆的結構,需要 上浮 shift up 下沉 shift down 來維護,時間複雜度為log 有這樣一道...
求中位數總結
今天在某個群中看到有人問在流式的資料中如何動態的維護中位數的方法,因為之前看到同學的乙個問題,當時他沒答出來。但是後來了解到類似top k last k 需要用到最大堆,最小堆一樣,中位數需要利用雙堆維護一下。先複習一下,如果是靜態的資料求取中位數的方法有哪些呢?1 排序 2 fink k 這兩種方...