本公司現在要給公司員工發波福利,在員工工作時間會提供大量的水果供員工補充營養。由於水果種類比較多,但是卻又不知道哪種水果比較受歡迎,然後公司就讓每個員工報告了自己最愛喫的k種水果,並且告知已經將所有員工喜歡喫的水果存儲於一個數組中。然後讓我們統計出所有水果出現的次數,並且求出大家最喜歡喫的前k種水果。
void GetFavoriteFruit(const vector& fruits,size_t k);
思路一:
排序,但是我們又不知道有多少種水果,如果水果很多。排序將會帶來很大的時間複雜度和空間複雜度。
那麼,這道題應該怎麼做呢?
當然,首選的應該是紅黑樹了,並且需要的是K/V結構的紅黑樹,K來存儲水果的名稱,V來存儲這個水果出現的次數。
可是紅黑樹結構複雜,在面試的時候想寫出來不是那麼容易,所以這裏有了STL兩個容器,map和set。
set和map這兩個容器的底層都是用紅黑樹來實現的,並且他們都具有防冗餘的特性(被插入的數據如果在容器中已經出現,那麼插入失敗),他兩的唯一區別就是set是一個K結構,而map是一個K/V結構。
map原型:
template < class Key,
class T,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key,T> >
> class map;
Key:這個就是我剛纔說的K/V結構中的K
T:這是K/V結構中的V。
Compare:這是一個接受仿函數類型的參數,可以控制map是一個升序的還是降續的(不傳這個參數時,默認是升序)。
Alloc:空間配置器。
那麼這個題該怎麼解決?
定義一個map,然後將數組中所有的元素插入到map中,在插入前先使用find()來查找存在不存在,如果不存在則插入,如果存在,則利用find返回值來對找到的元素的V進行+1操作。(其中fruits是水果數組)
map<string, int> fruitCount;//創建map對象
for (int i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
{
map<string, int>::iterator it = fruitCount.find(fruits[i]);//創建map迭代器
if (it != fruitCount.end())//先查找看該字符數組內是否存在該字符串
{
it->second++;//給該類對象的計數+1
}
else
{
fruitCount.insert(pair<string, int>(fruits[i], 1));
}
}
缺點:如果map中沒有要插入的這個水果,則需要遍歷兩次map。
思路二:只遍歷一次map
insert的返回值pair。既然不管是否插入成功,它都能返回我們需要的這個元素的迭代器。那麼,我們可以先插入,然後對其返回值進行保存,如果該返回值得第二個參數是true,表示插入成功,不進行其他操作,如果爲flase,表示插入失敗,那麼其返回的第一個參數將會帶回已經存在的這個被插入元素的迭代器,當然輕而易舉就可以通過迭代器拿到這個元素的第二個參數V。
void CalculateFruitCount(map<string,int>& m, string fruits[], size_t size)
{
for (size_t i = 0; i < size; i++)
{
//m[fruits[i]]++; //map中有operator[]的重載,其內容等同於下邊代碼
pair<map<string, int>::iterator, bool> ret;
ret = m.insert(make_pair(fruits[i], 1));
if (ret.second == false)
ret.first->second++;
}
}
現在走到這裏已經全部插入了,那接下來我們就需要找前K個最喜歡的水果了。
思路一:
將統計好的數據全部放入一個vector中,並且利用排序算法sort進行排序。而其默認爲升序,最大的則位於數組後邊,但是我們並不知道vector有多大。所以,我們採用降續,這樣最大的永遠在vector的前列.
void GetBeginOfThreeFruits(map<string, int>& m, vector<map<string, int>::iterator>& v) //按照水果出現的次數降續存儲於v中
{
map<string, int>::iterator it = m.begin();
while (it != m.end())
{
v.push_back(it);
it++;
}
struct Compare //仿函數(降續)
{
bool operator()(map<string, int>::iterator l, map<string, int>::iterator r)
{
return l->second > r->second;
}
};
sort(v.begin(), v.end(),Compare());
}
思路二:給一個堆,這個堆只要K個大小。
因爲找出現次數最多的,所以這裏給一個小堆。堆頂元素爲最小的數。每一次新進來的數字和堆頂比較,如果比堆頂小則不要。比堆頂大則把堆頂pop,把這個元素插入再重新排堆。
void GetBeginOfNFruits(map<string, int>& m, size_t n, vector<map<string,int>::iterator>& v)
{
map<string, int> ::iterator it = m.begin();
for (size_t i = 0; i < n; ++i)
{
v.push_back(it);
it++;
}
struct Compare //堆算法默認是大堆,此處需要仿函數將其改爲小堆
{
bool operator()(map<string, int>::iterator l, map<string, int>::iterator r)
{
return l->second > r->second; //小堆
}
};
make_heap(v.begin(), v.end(), Compare());
while (it != m.end())
{
if (it->second > v.front()->second)
{
pop_heap(v.begin(), v.end(), Compare());
v.pop_back();
v.push_back(it);
push_heap(v.begin(), v.end(), Compare());
}
it++;
}
}