《劍指offer》:[64]資料流中的中位數
題目:如何得到一個資料流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有資料排序後中間兩個數的平均值。
例如:1,2,3,4,5的中位數為:3。1,2,3,4的中位數為:(2+3)/2=3。
方案一:採用Partition來解決。在[29]中我們講過,快速查詢中的Partition函式是十分重要,是一個比較常用的演算法。所以這裡我們採用partion函式來解決。從字元流裡讀字元,插入到一個無需的陣列中的複雜度為O(1),查詢中位數的時間複雜度為O(N)。
方案二:採用排序陣列。採用插入排序,讀取一個字元進行有序的插入操作。這樣排序的時間複雜度為O(N*N),但是取得中位數的時間複雜度為O(1)。
方案三:考慮到方案二陣列的插入需要移動資料,所以這裡我們可以採用連結串列來解決,這樣我們需要定義兩個額外的指標指向中間結點。插入資料排序的時間複雜度為O(N),但是得到中位數的時間效率為:O(1)。
方案四:為了提高方案三中插入資料的效率,我們採用二叉排序樹,此時的時間複雜度為O(logN),但是當二叉搜尋樹看起來不平衡看起來像個連結串列的時候,其插入的時間複雜度任然為O(logN)。為了得到中位數,我們可以在結點中新增一個表示結點數目的欄位,有了這個欄位我們可以在平均O(logN)時間得到中位數,但是最差情況任然需要O(N)的時間。
方案五:為了避免方案四中極度不平衡的情況,我們採用平衡二叉樹(AVL樹)。但是AVL樹中的平衡因子是左右子樹的高度差,我們可以將該平衡因子修改為左右子樹的結點數目的差。有了這個改動,可以用O(logN)時間向該樹中新增一個新的結點。同時用O(1)的時間來得到中位數的值。
方案六:採取大頂堆和小頂堆。雖然AVL樹的效率較高,但是大部分程式語言函式庫裡沒有實現這個資料結構,需要對平衡因子修改的同時還要在短時間內寫出其實現程式碼有點兒困難。這裡我們用兩個容器來實現和方案五一樣的效果。方法如下:
(1)用兩個堆,一個大頂堆,一個小頂堆。將資料分割成兩部分,左邊的大頂堆餓資料都小於右邊的小頂堆的資料。
(2)先往小頂堆裡面存數,並保持: 0 <= 小頂堆的size()-大頂堆的size() <= 1
(3)保持兩邊數量幾乎一致就需要在插入的時候進行比較、調整。
(4)返回中位數的時候,如果小頂堆和大頂堆size()相同,就返回他們堆頂元素的平均值;否則返回小頂堆的堆頂元素。
這種方法插入時間複雜度是O(log n),返回中位數的時間複雜度是O(1)
這樣我們可以在O(logN)的時間複雜度裡完成資料的插入,在O(1)的時間複雜度裡完成中位數的提取操作。所以綜合其時間複雜度為O(logN)。
核心類實現程式碼如下:
例如:1,2,3,4,5的中位數為:3。1,2,3,4的中位數為:(2+3)/2=3。
方案一:採用Partition來解決。在[29]中我們講過,快速查詢中的Partition函式是十分重要,是一個比較常用的演算法。所以這裡我們採用partion函式來解決。從字元流裡讀字元,插入到一個無需的陣列中的複雜度為O(1),查詢中位數的時間複雜度為O(N)。
方案二:採用排序陣列。採用插入排序,讀取一個字元進行有序的插入操作。這樣排序的時間複雜度為O(N*N),但是取得中位數的時間複雜度為O(1)。
方案三:考慮到方案二陣列的插入需要移動資料,所以這裡我們可以採用連結串列來解決,這樣我們需要定義兩個額外的指標指向中間結點。插入資料排序的時間複雜度為O(N),但是得到中位數的時間效率為:O(1)。
方案四:為了提高方案三中插入資料的效率,我們採用二叉排序樹,此時的時間複雜度為O(logN),但是當二叉搜尋樹看起來不平衡看起來像個連結串列的時候,其插入的時間複雜度任然為O(logN)。為了得到中位數,我們可以在結點中新增一個表示結點數目的欄位,有了這個欄位我們可以在平均O(logN)時間得到中位數,但是最差情況任然需要O(N)的時間。
方案五:為了避免方案四中極度不平衡的情況,我們採用平衡二叉樹(AVL樹)。但是AVL樹中的平衡因子是左右子樹的高度差,我們可以將該平衡因子修改為左右子樹的結點數目的差。有了這個改動,可以用O(logN)時間向該樹中新增一個新的結點。同時用O(1)的時間來得到中位數的值。
方案六:採取大頂堆和小頂堆。雖然AVL樹的效率較高,但是大部分程式語言函式庫裡沒有實現這個資料結構,需要對平衡因子修改的同時還要在短時間內寫出其實現程式碼有點兒困難。這裡我們用兩個容器來實現和方案五一樣的效果。方法如下:
(1)用兩個堆,一個大頂堆,一個小頂堆。將資料分割成兩部分,左邊的大頂堆餓資料都小於右邊的小頂堆的資料。
(2)先往小頂堆裡面存數,並保持: 0 <= 小頂堆的size()-大頂堆的size() <= 1
(3)保持兩邊數量幾乎一致就需要在插入的時候進行比較、調整。
(4)返回中位數的時候,如果小頂堆和大頂堆size()相同,就返回他們堆頂元素的平均值;否則返回小頂堆的堆頂元素。
這種方法插入時間複雜度是O(log n),返回中位數的時間複雜度是O(1)
這樣我們可以在O(logN)的時間複雜度裡完成資料的插入,在O(1)的時間複雜度裡完成中位數的提取操作。所以綜合其時間複雜度為O(logN)。
核心類實現程式碼如下:
template<typename T>
class Heap
{
private:
vector<T> min;
vector<T> max;
public:
void Insert(T num)
{
if(((min.size()+max.size())&1)==0)//偶數插入左邊最大堆;
{
if(max.size()>0 && num<max[0])
{
max.push_back(num);
push_heap(max.begin(),max.end(),less<T>());
num=max[0];
pop_heap(max.begin(),max.end(),less<T>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(),min.end(),greater<T>());
}
else //奇數插入右邊最小堆;
{
if(min.size()>0&&num>min[0])
{
min.push_back(num);
push_heap(min.begin(),min.end(),greater<T>());
num=min[0];
pop_heap(min.begin(),min.end(),greater<T>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(),max.end(),less<T>());
}
}
T get_median()
{
int size=min.size()+max.size();
if(size==0)
throw exception("no numbers are available");
T median=0;
if((size&1)!=0) //如果是奇數返回最小堆的第一個元素;
{
median=min[0];
}
else //如果是偶數則返回中間兩個數的平均值;
{
median=(max[0]+min[0])/2;
}
return median;
}
};
相關文章
- 【劍指offer】二進位制中1的個數
- 劍指 Offer 15. 二進位制中1的個數
- 資料流中的中位數
- 劍指 Offer 44. 數字序列中某一位的數字
- 【劍指offer中等部分4】二進位制中1的個數(java)Java
- 劍指Offer 字元流中第一個不重複的字元字元
- 1202-資料流中的中位數
- 劍指Offer--陣列中重複的數字陣列
- 1. 二位陣列中的查詢(劍指offer)陣列
- JZ-063-資料流中的中位數
- 求資料流中的中位數問題
- 劍指Offer-把陣列中的數排成一個最小的數陣列
- Leetcode 劍指 Offer 03. 陣列中重複的數字LeetCode陣列
- 劍指 Offer 56 - I. 陣列中數字出現的次數陣列
- 劍指Offer 表示數值的字串字串
- 【劍指 Offer 】17. 列印從1到最大的n位數
- 劍指offer | 17. 列印從1到最大的n位數
- 【劍指offer】二維陣列中的查詢陣列
- 劍指 Offer 列印從1到最大n位數c++C++
- 劍指OFFER
- 劍指offer——連結串列中倒數第k個結點
- 劍指Offer-37-陣列中逆序對陣列
- 劍指offer刷題之路--1.陣列中重複的數字陣列
- 【劍指offer】【1】二維陣列中的查詢陣列
- 劍指offer中幾道演算法題的思考演算法
- 劍指offer:JZ12 矩陣中的路徑矩陣
- 劍指offer——陣列中的逆序對C++(75%)陣列C++
- LeetCode|劍指 Offer 49.醜數LeetCode
- 劍指offer第49題 醜數
- 劍指Offer-31-最小的K個數
- 劍指Offer系列之「表示數值的字串」字串
- 二維陣列中的查詢——牛客劍指offer陣列
- 劍指 Offer 04. 二維陣列中的查詢陣列
- 劍指 Offer 12-矩陣中的路徑c++矩陣C++
- 力扣 - 劍指 Offer 12. 矩陣中的路徑力扣矩陣
- 劍指Offer-連續子陣列中的最大和陣列
- 【劍指Offer】調整陣列順序使奇數位於偶數前面陣列
- 【leetcode】劍指 Offer 16. 數值的整數次方LeetCode
- 劍指offer——斐波那契數列