好像是去年cvte在招聘的時候出了這樣的乙個筆試題:
題目的大意就是:本公司現在要給公司員工發波福利,在員工工作時間會提供大量的水果供員工補充營養。由於水果種模擬較多,但是卻又不知道哪種水果比較受歡迎,然後公司就讓每個員工報告了自己最愛吃的三種水果,並且告知已經將所有員工喜歡吃的水果儲存於乙個陣列中。然後讓我們統計出所有水果出現的次數,並且求出排列前三的這三種水果。
拿到這個題,我們應該怎麼做呢?
首先,統計所有水果出現的次數,很顯而易見的我們最先想到的當然是,將陣列遍歷一遍,就能得到每種水果的出現次數。然後再對求出水果次數的陣列進行排序,這樣很容易就可以拿到最受歡迎三種水果。
可是,大家有沒有想過,如果該公司有100w的員工,如此龐大的數量,我們對數字遍歷並且排序是多麼大的開銷。而且其時間複雜度和空間複雜度都很高,如果按照這種思路我們肯定是會被拒之門外。
那麼,這道題應該怎麼做呢?
當然,首選的應該是紅黑樹了,並且需要的是k/v結構的紅黑樹,k來儲存水果的名稱,v來儲存這個水果出現的次數。
可是,筆試時間那麼短,紅黑樹相對來說比較複雜的結構,我們如何在短時間內拿到它呢?
不用擔心,在c++stl庫中提供了兩個關聯式容器:set和map.
那麼我先大概的介紹一下這個set和map.
set和map這兩個容器的底層都是用紅黑樹來實現的,並且他們都具有防冗餘的特性(被插入的資料如果在容器中已經出現,那麼插入失敗),他兩的唯一區別就是set是乙個k結構,而map是乙個k/v結構。
那麼他們是怎麼使用呢?
在這裡,我主要講解一下map,因為上邊的這個題需要用到它。而set和map的用法基本上是一模一樣的。
首先,來看一下這個map的原型:
template < class key, // map::key_type
class compare = less, // map::key_compare
class alloc = allocator> // map::allocator_type
> class map;
那麼這個map的模板引數都是什麼意思呢?
key:這個就是我剛才說的k/v結構中的k
t:這個就是我剛才說的k/v結構中的v
compare:這個是乙個接受仿函式型別的引數,可以控制map是乙個公升序的還是降續的(不傳這個引數時,預設是公升序)
alloc:這個是空間配置器,我們現在還用不到,先知道它是什麼就好了。
那麼,這個map都有哪些介面呢?(這裡主要講幾個重要的介面)
1.insert
pairinsert (const value_type& val);
首先其返回值是乙個pair,什麼是pair:乙個結構體,第乙個引數是乙個迭代器,第二個引數數乙個bool值。意思就是:如果插入成功,就返回乙個被插入元素的迭代器,並且第二個引數為true;反之返回容器中已經存在的這個和被插入元素相同的元素的迭代器和false.
其引數是value_type typedef pair
value_type;
2.operator
這個運算子的過載非常巧妙,等會再來講解。
那麼現在,我們回歸這道題:
首先,求解所有水果出現的次數,在這裡有三種解法:
解法一:
定義乙個map,然後將陣列中所有的元素插入到map中,在插入前先使用find()來查詢存在不存在,如果不存在則插入,如果存在,則利用find返回值來對找到的元素的v進行+1操作。(其中strs是水果陣列,同下)
mapfruitcount;//建立map物件
for (int i = 0; i < sizeof(strs) / sizeof(strs[0]); ++i)
else
}
解法二:
上邊的解法一就是一種傳統的思維方式,但是效率稍微顯慢,英文如果map中沒有要插入的元素,則需要遍歷兩次map。
那麼,如何做到只遍歷一次就能完成這個任務呢?
還記得我之前講的那個insert的返回值pair嗎?既然不管是否插入成功,它都能返回我們需要的這個元素的迭代器。那麼,我們可以先插入,然後對其返回值進行儲存,如果該返回值得第二個引數是true,表示插入成功,不進行其他操作,如果為flase,表示插入失敗,那麼其返回的第乙個引數將會帶回已經存在的這個被插入元素的迭代器,當然輕而易舉就可以通過迭代器拿到這個元素的第二個引數v。
void calculatefruitcount(map& m, string s, size_t size)
}
解法三:
解法二中被注釋掉的那一句。那麼現在我就講一下這個operator.map中對這個運算子進行了過載,其格式為:
t& operator (const key& k);
而其**的實現就是解法二中的思想。現在大家對這個operator了解了吧。
好!到這裡,這個問題已經解決了一半。那麼我們繼續往下走。
現在水果出現的次數已經統計出來,那麼如何求出排列前3的水果呢?乃至前n呢?
這裡有4中解法:
1.講統計好的資料全部放入乙個vector中,並且利用排序演算法sort進行排序。而其預設為公升序,最大的則位於陣列後邊,但是我們並不知道vector有多大。所以,我們採用降續,這樣最大的永遠在vector的前列.
void getbeginofthreefruits(map& m, vector::iterator>& v) //按照水果出現的次數降續儲存於v中
struct compare //仿函式(降續)
};sort(v.begin(), v.end(),compare());
}
2.同1的思想,只不過這次我們不是放在vector中,而是放在乙個set中,這樣就可以省去我們自己給它排序的麻煩。(因為set底層是乙個紅黑樹,而紅黑樹就是乙個近似平衡的二叉排序樹)**比較簡單,這裡就不實現了。
3.大家還知道堆嗎?既然我們需要找出最大的3個,那麼我們只需要建立乙個3個大小的堆,但是,我們應該建大堆還是小堆呢?當然是小堆,為什麼呢?
因為,小堆的話大的資料就會往下沉,而對頂永遠是最小的,當來了乙個資料時,我們只需要和對頂的元素做乙個判斷,如果比對頂元素小,那麼不用插入到堆,如果比對頂元素大,那麼將對頂元素pop出去,然後將這個元素插入。當剛才求好水果出現次數的陣列全部遍歷一遍後,最後堆裡面剩下的肯定就是出現次數最多的三個水果。
void getbeginofnfruits(map& m, size_t n, vector::iterator>& v)
struct compare //堆演算法預設是大堆,此處需要仿函式將其改為小堆
};make_heap(v.begin(), v.end(), compare());
while (it != m.end())
it++;
}}
4.利用優先順序佇列,優先順序佇列的底層其實就是乙個堆,這裡**不予實現了,思想同3.
到這裡,整個題目就解決完了。**寫出來其實不難,難的就是我們需要對stl庫函式有一定的了解,已經要會使用。
CVTE筆試題2 生成zigzag矩陣
其幾天參加的cvte兩道筆試題,第二道是生成zigzag矩陣,具體如下 給定陣列輸出 1,2,6 3,5,7 4,8,9 vector to zigzagmatrix vector data int temp sqrt n if temp temp n throw new exception 輸入陣...
CVTE筆試 最大數
cvte的筆試題一共兩道,其中一道是leecode上的最大數,具體題目如下 給定一組非負整數,重新排列它們的順序使之組成乙個最大的整數。示例 1 輸入 10,2 輸出 210 示例 2 輸入 3,30,34,5,9 輸出 9534330 說明 輸出結果可能非常大,所以你需要返回乙個字串而不是整數。思...
CVTE筆試題 字串的全排列
下面我就程式設計題 字串的全排列進行總結 當然,這道題在劍指offer上就有,面試經常出,我以前也做過,但是時間長了,不太熟了,做的時候挺吃力,就在這裡總結一下吧!題目 輸入乙個字串,列印該字串中字元的所有排列,例如輸入abc,則列印出abc,acb,bac,bca,cab,cba。解法一 遞迴 就...