数据结构算法常见面试考题
(1) 紅黑樹(shù)的了解(平衡樹(shù),二叉搜索樹(shù)),使用場(chǎng)景
把數(shù)據(jù)結(jié)構(gòu)上幾種樹(shù)集中的討論一下:
1.AVLtree
定義:最先發(fā)明的自平衡二叉查找樹(shù)。在AVL樹(shù)中任何節(jié)點(diǎn)的兩個(gè)子樹(shù)的高度最大差別為一,所以它也被稱為高度平衡樹(shù)。查找、插入和刪除在平均和最壞情況下都是O(log n)。增加和刪除可能需要通過(guò)一次或多次樹(shù)旋轉(zhuǎn)來(lái)重新平衡這個(gè)樹(shù)。
節(jié)點(diǎn)的平衡因子是它的左子樹(shù)的高度減去它的右子樹(shù)的高度(有時(shí)相反)。帶有平衡因子1、0或 -1的節(jié)點(diǎn)被認(rèn)為是平衡的。帶有平衡因子 -2或2的節(jié)點(diǎn)被認(rèn)為是不平衡的,并需要重新平衡這個(gè)樹(shù)。平衡因子可以直接存儲(chǔ)在每個(gè)節(jié)點(diǎn)中,或從可能存儲(chǔ)在節(jié)點(diǎn)中的子樹(shù)高度計(jì)算出來(lái)。
一般我們所看見(jiàn)的都是排序平衡二叉樹(shù)。
AVLtree使用場(chǎng)景:AVL樹(shù)適合用于插入刪除次數(shù)比較少,但查找多的情況。插入刪除導(dǎo)致很多的旋轉(zhuǎn),旋轉(zhuǎn)是非常耗時(shí)的。AVL 在linux內(nèi)核的vm area中使用。
2.二叉搜索樹(shù)
二叉搜索樹(shù)也是一種樹(shù),適用與一般二叉樹(shù)的全部操作,但二叉搜索樹(shù)能夠?qū)崿F(xiàn)數(shù)據(jù)的快速查找。
二叉搜索樹(shù)滿足的條件:
1.非空左子樹(shù)的所有鍵值小于其根節(jié)點(diǎn)的鍵值
2.非空右子樹(shù)的所有鍵值大于其根節(jié)點(diǎn)的鍵值
3.左右子樹(shù)都是二叉搜索樹(shù)
二叉搜索樹(shù)的應(yīng)用場(chǎng)景:如果是沒(méi)有退化稱為鏈表的二叉樹(shù),查找效率就是lgn,效率不錯(cuò),但是一旦退換稱為鏈表了,要么使用平衡二叉樹(shù),或者之后的RB樹(shù),因?yàn)殒湵砭褪蔷€性的查找效率。
3.紅黑樹(shù)的定義
紅黑樹(shù)是一種二叉查找樹(shù),但在每個(gè)結(jié)點(diǎn)上增加了一個(gè)存儲(chǔ)位表示結(jié)點(diǎn)的顏色,可以是RED或者BLACK。通過(guò)對(duì)任何一條從根到葉子的路徑上各個(gè)著色方式的限制,紅黑樹(shù)確保沒(méi)有一條路徑會(huì)比其他路徑長(zhǎng)出兩倍,因而是接近平衡的。
當(dāng)二叉查找樹(shù)的高度較低時(shí),這些操作執(zhí)行的比較快,但是當(dāng)樹(shù)的高度較高時(shí),這些操作的性能可能不比用鏈表好。紅黑樹(shù)(red-black tree)是一種平衡的二叉查找樹(shù),它能保證在最壞情況下,基本的動(dòng)態(tài)操作集合運(yùn)行時(shí)間為O(lgn)。
紅黑樹(shù)必須要滿足的五條性質(zhì):
性質(zhì)一:節(jié)點(diǎn)是紅色或者是黑色; 在樹(shù)里面的節(jié)點(diǎn)不是紅色的就是黑色的,沒(méi)有其他顏色,要不怎么叫紅黑樹(shù)呢,是吧。
性質(zhì)二:根節(jié)點(diǎn)是黑色; 根節(jié)點(diǎn)總是黑色的。它不能為紅。
性質(zhì)三:每個(gè)葉節(jié)點(diǎn)(NIL或空節(jié)點(diǎn))是黑色;
性質(zhì)四:每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色的(也就是說(shuō)不存在兩個(gè)連續(xù)的紅色節(jié)點(diǎn)); 就是連續(xù)的兩個(gè)節(jié)點(diǎn)不能是連續(xù)的紅色,連續(xù)的兩個(gè)節(jié)點(diǎn)的意思就是父節(jié)點(diǎn)與子節(jié)點(diǎn)不能是連續(xù)的紅色。
性質(zhì)五:從任一節(jié)點(diǎn)到其每個(gè)葉節(jié)點(diǎn)的所有路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。從根節(jié)點(diǎn)到每一個(gè)NIL節(jié)點(diǎn)的路徑中,都包含了相同數(shù)量的黑色節(jié)點(diǎn)。
紅黑樹(shù)的應(yīng)用場(chǎng)景:紅黑樹(shù)是一種不是非常嚴(yán)格的平衡二叉樹(shù),沒(méi)有AVLtree那么嚴(yán)格的平衡要求,所以它的平均查找,增添刪除效率都還不錯(cuò)。廣泛用在C++的STL中。如map和set都是用紅黑樹(shù)實(shí)現(xiàn)的。
4.B樹(shù)定義
B樹(shù)和平衡二叉樹(shù)稍有不同的是B樹(shù)屬于多叉樹(shù)又名平衡多路查找樹(shù)(查找路徑不只兩個(gè)),不屬于二叉搜索樹(shù)的范疇,因?yàn)樗恢箖陕?#xff0c;存在多路。
B樹(shù)滿足的條件:
(1)樹(shù)種的每個(gè)節(jié)點(diǎn)最多擁有m個(gè)子節(jié)點(diǎn)且m>=2,空樹(shù)除外(注:m階代表一個(gè)樹(shù)節(jié)點(diǎn)最多有多少個(gè)查找路徑,m階=m路,當(dāng)m=2則是2叉樹(shù),m=3則是3叉);
(2)除根節(jié)點(diǎn)外每個(gè)節(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量大于等于ceil(m/2)-1個(gè)小于等于m-1個(gè),非根節(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)必須>=2;(注:ceil()是個(gè)朝正無(wú)窮方向取整的函數(shù) 如ceil(1.1)結(jié)果為2)
(3)所有葉子節(jié)點(diǎn)均在同一層、葉子節(jié)點(diǎn)除了包含了關(guān)鍵字和關(guān)鍵字記錄的指針外也有指向其子節(jié)點(diǎn)的指針只不過(guò)其指針地址都為null對(duì)應(yīng)下圖最后一層節(jié)點(diǎn)的空格子
(4)如果一個(gè)非葉節(jié)點(diǎn)有N個(gè)子節(jié)點(diǎn),則該節(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)等于N-1;
(5)所有節(jié)點(diǎn)關(guān)鍵字是按遞增次序排列,并遵循左小右大原則;
B樹(shù)的應(yīng)用場(chǎng)景:構(gòu)造一個(gè)多階的B類(lèi)樹(shù),然后在盡量多的在結(jié)點(diǎn)上存儲(chǔ)相關(guān)的信息,保證層數(shù)盡量的少,以便后面我們可以更快的找到信息,磁盤(pán)的I/O操作也少一些,而且B類(lèi)樹(shù)是平衡樹(shù),每個(gè)結(jié)點(diǎn)到葉子結(jié)點(diǎn)的高度都是相同,這也保證了每個(gè)查詢是穩(wěn)定的。
5.B+樹(shù)
B+樹(shù)是B樹(shù)的一個(gè)升級(jí)版,B+樹(shù)是B樹(shù)的變種樹(shù),有n棵子樹(shù)的節(jié)點(diǎn)中含有n個(gè)關(guān)鍵字,每個(gè)關(guān)鍵字不保存數(shù)據(jù),只用來(lái)索引,數(shù)據(jù)都保存在葉子節(jié)點(diǎn)。是為文件系統(tǒng)而生的。
相對(duì)于B樹(shù)來(lái)說(shuō)B+樹(shù)更充分的利用了節(jié)點(diǎn)的空間,讓查詢速度更加穩(wěn)定,其速度完全接近于二分法查找。為什么說(shuō)B+樹(shù)查找的效率要比B樹(shù)更高、更穩(wěn)定;我們先看看兩者的區(qū)別
(1)B+跟B樹(shù)不同,B+樹(shù)的非葉子節(jié)點(diǎn)不保存關(guān)鍵字記錄的指針,這樣使得B+樹(shù)每個(gè)節(jié)點(diǎn)所能保存的關(guān)鍵字大大增加;
(2)B+樹(shù)葉子節(jié)點(diǎn)保存了父節(jié)點(diǎn)的所有關(guān)鍵字和關(guān)鍵字記錄的指針,每個(gè)葉子節(jié)點(diǎn)的關(guān)鍵字從小到大鏈接;
(3)B+樹(shù)的根節(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)量和其子節(jié)點(diǎn)個(gè)數(shù)相等;
(4)B+的非葉子節(jié)點(diǎn)只進(jìn)行數(shù)據(jù)索引,不會(huì)存實(shí)際的關(guān)鍵字記錄的指針,所有數(shù)據(jù)地址必須要到葉子節(jié)點(diǎn)才能獲取到,所以每次數(shù)據(jù)查詢的次數(shù)都一樣;
特點(diǎn):
在B樹(shù)的基礎(chǔ)上每個(gè)節(jié)點(diǎn)存儲(chǔ)的關(guān)鍵字?jǐn)?shù)更多,樹(shù)的層級(jí)更少所以查詢數(shù)據(jù)更快,所有指關(guān)鍵字指針都存在葉子節(jié)點(diǎn),所以每次查找的次數(shù)都相同所以查詢速度更穩(wěn)定;
應(yīng)用場(chǎng)景: 用在磁盤(pán)文件組織 數(shù)據(jù)索引和數(shù)據(jù)庫(kù)索引。
6.Trie樹(shù)(字典樹(shù))
trie,又稱前綴樹(shù),是一種有序樹(shù),用于保存關(guān)聯(lián)數(shù)組,其中的鍵通常是字符串。與二叉查找樹(shù)不同,鍵不是直接保存在節(jié)點(diǎn)中,而是由節(jié)點(diǎn)在樹(shù)中的位置決定。一個(gè)節(jié)點(diǎn)的所有子孫都有相同的前綴,也就是這個(gè)節(jié)點(diǎn)對(duì)應(yīng)的字符串,而根節(jié)點(diǎn)對(duì)應(yīng)空字符串。一般情況下,不是所有的節(jié)點(diǎn)都有對(duì)應(yīng)的值,只有葉子節(jié)點(diǎn)和部分內(nèi)部節(jié)點(diǎn)所對(duì)應(yīng)的鍵才有相關(guān)的值。
在圖示中,鍵標(biāo)注在節(jié)點(diǎn)中,值標(biāo)注在節(jié)點(diǎn)之下。每一個(gè)完整的英文單詞對(duì)應(yīng)一個(gè)特定的整數(shù)。Trie 可以看作是一個(gè)確定有限狀態(tài)自動(dòng)機(jī),盡管邊上的符號(hào)一般是隱含在分支的順序中的。
鍵不需要被顯式地保存在節(jié)點(diǎn)中。圖示中標(biāo)注出完整的單詞,只是為了演示 trie 的原理。
trie樹(shù)的優(yōu)點(diǎn):利用字符串的公共前綴來(lái)節(jié)約存儲(chǔ)空間,最大限度地減少無(wú)謂的字符串比較,查詢效率比哈希表高。缺點(diǎn):Trie樹(shù)是一種比較簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu).理解起來(lái)比較簡(jiǎn)單,正所謂簡(jiǎn)單的東西也得付出代價(jià).故Trie樹(shù)也有它的缺點(diǎn),Trie樹(shù)的內(nèi)存消耗非常大.
其基本性質(zhì)可以歸納為:
典型應(yīng)用是用于統(tǒng)計(jì),排序和保存大量的字符串(但不僅限于字符串),所以經(jīng)常被搜索引擎系統(tǒng)用于文本詞頻統(tǒng)計(jì)。字典樹(shù)與字典很相似,當(dāng)你要查一個(gè)單詞是不是在字典樹(shù)中,首先看單詞的第一個(gè)字母是不是在字典的第一層,如果不在,說(shuō)明字典樹(shù)里沒(méi)有該單詞,如果在就在該字母的孩子節(jié)點(diǎn)里找是不是有單詞的第二個(gè)字母,沒(méi)有說(shuō)明沒(méi)有該單詞,有的話用同樣的方法繼續(xù)查找.字典樹(shù)不僅可以用來(lái)儲(chǔ)存字母,也可以儲(chǔ)存數(shù)字等其它數(shù)據(jù)。
(2) 紅黑樹(shù)在STL上的應(yīng)用
STL中set、multiset、map、multimap底層是紅黑樹(shù)實(shí)現(xiàn)的,而unordered_map、unordered_set 底層是哈希表實(shí)現(xiàn)的。
multiset、multimap: 插入相同的key的時(shí)候,我們將后插入的key放在相等的key的右邊,之后不管怎么進(jìn)行插入或刪除操作,后加入的key始終被認(rèn)為比之前的大。
(3) 了解并查集嗎?(低頻)
什么是合并查找問(wèn)題呢?
顧名思義,就是既有合并又有查找操作的問(wèn)題。舉個(gè)例子,有一群人,他們之間有若干好友關(guān)系。如果兩個(gè)人有直接或者間接好友關(guān)系,那么我們就說(shuō)他們?cè)谕粋€(gè)朋友圈中,這里解釋下,如果Alice是Bob好友的好友,或者好友的好友的好友等等,即通過(guò)若干好友可以認(rèn)識(shí),那么我們說(shuō)Alice和Bob是間接好友。隨著時(shí)間的變化,這群人中有可能會(huì)有新的朋友關(guān)系,這時(shí)候我們會(huì)對(duì)當(dāng)中某些人是否在同一朋友圈進(jìn)行詢問(wèn)。這就是一個(gè)典型的合并-查找操作問(wèn)題,既包含了合并操作,又包含了查找操作。
并查集,在一些有N個(gè)元素的集合應(yīng)用問(wèn)題中,我們通常是在開(kāi)始時(shí)讓每個(gè)元素構(gòu)成一個(gè)單元素的集合,然后按一定順序?qū)儆谕唤M的元素所在的集合合并,其間要反復(fù)查找一個(gè)元素在哪個(gè)集合中。
并查集是一種樹(shù)型的數(shù)據(jù)結(jié)構(gòu),用于處理一些不相交集合(Disjoint Sets)的合并及查詢問(wèn)題。
并查集也是使用樹(shù)形結(jié)構(gòu)實(shí)現(xiàn)。不過(guò),不是二叉樹(shù)。每個(gè)元素對(duì)應(yīng)一個(gè)節(jié)點(diǎn),每個(gè)組對(duì)應(yīng)一棵樹(shù)。在并查集中,哪個(gè)節(jié)點(diǎn)是哪個(gè)節(jié)點(diǎn)的父親以及樹(shù)的形狀等信息無(wú)需多加關(guān)注,整體組成一個(gè)樹(shù)形結(jié)構(gòu)才是重要的。類(lèi)似森林
(4) 貪心算法和動(dòng)態(tài)規(guī)劃的區(qū)別
貪心算法:局部最優(yōu),劃分的每個(gè)子問(wèn)題都最優(yōu),得到全局最優(yōu),但是不能保證是全局最優(yōu)解,所以對(duì)于貪心算法來(lái)說(shuō),解是從上到下的,一步一步最優(yōu),直到最后。
動(dòng)態(tài)規(guī)劃:將問(wèn)題分解成重復(fù)的子問(wèn)題,每次都尋找左右子問(wèn)題解中最優(yōu)的解,一步步得到全局的最優(yōu)解.重復(fù)的子問(wèn)題可以通過(guò)記錄的方式,避免多次計(jì)算。所以對(duì)于動(dòng)態(tài)規(guī)劃來(lái)說(shuō),解是從小到上,從底層所有可能性中找到最優(yōu)解,再一步步向上。
分治法:和動(dòng)態(tài)規(guī)劃類(lèi)似,將大問(wèn)題分解成小問(wèn)題,但是這些小問(wèn)題是獨(dú)立的,沒(méi)有重復(fù)的問(wèn)題。獨(dú)立問(wèn)題取得解,再合并成大問(wèn)題的解。
例子:比如錢(qián)幣分為1元3元4元,要拿6元錢(qián),貪心的話,先拿4,再拿兩個(gè)1,一共3張錢(qián);實(shí)際最優(yōu)卻是兩張3元就夠了。
(5) 判斷一個(gè)鏈表是否有環(huán),如何找到這個(gè)環(huán)的起點(diǎn)
給定一個(gè)單鏈表,只給出頭指針h:
1、如何判斷是否存在環(huán)?
2、如何知道環(huán)的長(zhǎng)度?
3、如何找出環(huán)的連接點(diǎn)在哪里?
4、帶環(huán)鏈表的長(zhǎng)度是多少?
解法:
1、對(duì)于問(wèn)題1,使用追趕的方法,設(shè)定兩個(gè)指針slow、fast,從頭指針開(kāi)始,每次分別前進(jìn)1步、2步。如存在環(huán),則兩者相遇;如不存在環(huán),fast遇到NULL退出。
2、對(duì)于問(wèn)題2,記錄下問(wèn)題1的碰撞點(diǎn)p,slow、fast從該點(diǎn)開(kāi)始,再次碰撞所走過(guò)的操作數(shù)就是環(huán)的長(zhǎng)度s。
3、問(wèn)題3:有定理:碰撞點(diǎn)p到連接點(diǎn)的距離=頭指針到連接點(diǎn)的距離,因此,分別從碰撞點(diǎn)、頭指針開(kāi)始走,相遇的那個(gè)點(diǎn)就是連接點(diǎn)。(證明在后面附注)
4、問(wèn)題3中已經(jīng)求出連接點(diǎn)距離頭指針的長(zhǎng)度,加上問(wèn)題2中求出的環(huán)的長(zhǎng)度,二者之和就是帶環(huán)單鏈表的長(zhǎng)度
http://blog.sina.com.cn/s/blog_725dd1010100tqwp.html
(6) 實(shí)現(xiàn)一個(gè)strcpy函數(shù)(或者memcpy),如果內(nèi)存可能重疊呢
——大家一般認(rèn)為名不見(jiàn)經(jīng)傳strcpy函數(shù)實(shí)現(xiàn)不是很難,流行的strcpy函數(shù)寫(xiě)法是:
1. char *my_strcpy(char *dst,const char *src) 2. { 3. assert(dst != NULL); 4. assert(src != NULL); 5. char *ret = dst; 6. while((* dst++ = * src++) != '\0') 7. ; 8. return ret; 9. }如果注意到:
1,檢查指針有效性;
2,返回目的指針des;
3,源字符串的末尾 ‘\0’ 需要拷貝。
內(nèi)存重疊
內(nèi)存重疊:拷貝的目的地址在源地址范圍內(nèi)。所謂內(nèi)存重疊就是拷貝的目的地址和源地址有重疊。
在函數(shù)strcpy和函數(shù)memcpy都沒(méi)有對(duì)內(nèi)存重疊做處理的,使用這兩個(gè)函數(shù)的時(shí)候只有程序員自己保證源地址和目標(biāo)地址不重疊,或者使用memmove函數(shù)進(jìn)行內(nèi)存拷貝。
memmove函數(shù)對(duì)內(nèi)存重疊做了處理。
strcpy的正確實(shí)現(xiàn)應(yīng)為:
memmove函數(shù)實(shí)現(xiàn)時(shí)考慮到了內(nèi)存重疊的情況,可以完成指定大小的內(nèi)存拷貝
(7) 快排存在的問(wèn)題,如何優(yōu)化
快排的時(shí)間復(fù)雜度
時(shí)間復(fù)雜度最快平均是O(nlogn),最慢的時(shí)候是O(n2);輔助空間也是O(logn);最開(kāi)始學(xué)快排時(shí)最疑惑的就是這個(gè)東西不知道怎么得來(lái)的,一種是通過(guò)數(shù)學(xué)運(yùn)算可以的出來(lái),還有一種是通過(guò)遞歸樹(shù)來(lái)理解就容易多了
這張圖片別人博客那里弄過(guò)來(lái)的,所謂時(shí)間復(fù)雜度最理想的就是取到中位數(shù)情況,那么遞歸樹(shù)就是一個(gè)完全二叉樹(shù),那么樹(shù)的深度也就是最低為L(zhǎng)ogn,這個(gè)時(shí)候每一次又需要n次比較,所以時(shí)間復(fù)雜度nlogn,當(dāng)快排為順序或者逆序時(shí),這個(gè)數(shù)為一個(gè)斜二叉樹(shù),深度為n,同樣每次需要n次比較,那那么最壞需要n2的時(shí)間
優(yōu)化:
1.當(dāng)整個(gè)序列有序時(shí)退出算法;
2.當(dāng)序列長(zhǎng)度很小時(shí)(根據(jù)經(jīng)驗(yàn)是大概小于 8),應(yīng)該使用常數(shù)更小的算法,比如插入排序等;
3.隨機(jī)選取分割位置;
4.當(dāng)分割位置不理想時(shí),考慮是否重新選取分割位置;
5.分割成兩個(gè)序列時(shí),只對(duì)其中一個(gè)遞歸進(jìn)去,另一個(gè)序列仍可以在這一函數(shù)內(nèi)繼續(xù)劃分,可以顯著減小棧的大小(尾遞歸):
6.將單向掃描改成雙向掃描,可以減少劃分過(guò)程中的交換次數(shù)
優(yōu)化1:當(dāng)待排序序列的長(zhǎng)度分割到一定大小后,使用插入排序
原因:對(duì)于很小和部分有序的數(shù)組,快排不如插排好。當(dāng)待排序序列的長(zhǎng)度分割到一定大小后,繼續(xù)分割的效率比插入排序要差,此時(shí)可以使用插排而不是快排
優(yōu)化2:在一次分割結(jié)束后,可以把與Key相等的元素聚在一起,繼續(xù)下次分割時(shí),不用再對(duì)與key相等元素分割
優(yōu)化3:優(yōu)化遞歸操作
快排函數(shù)在函數(shù)尾部有兩次遞歸操作,我們可以對(duì)其使用尾遞歸優(yōu)化
優(yōu)點(diǎn):如果待排序的序列劃分極端不平衡,遞歸的深度將趨近于n,而棧的大小是很有限的,每次遞歸調(diào)用都會(huì)耗費(fèi)一定的??臻g,函數(shù)的參數(shù)越多,每次遞歸耗費(fèi)的空間也越多。優(yōu)化后,可以縮減堆棧深度,由原來(lái)的O(n)縮減為O(logn),將會(huì)提高性能。
(8) Top K問(wèn)題(可以采取的方法有哪些,各自優(yōu)點(diǎn)?)
1.將輸入內(nèi)容(假設(shè)用數(shù)組存放)進(jìn)行完全排序,從中選出排在前K的元素即為所求。有了這個(gè)思路,我們可以選擇相應(yīng)的排序算法進(jìn)行處理,目前來(lái)看快速排序,堆排序和歸并排序都能達(dá)到O(nlogn)的時(shí)間復(fù)雜度。
2.對(duì)輸入內(nèi)容進(jìn)行部分排序,即只對(duì)前K大的元素進(jìn)行排序(這K個(gè)元素即為所求)。此時(shí)我們可以選擇冒泡排序或選擇排序進(jìn)行處理,即每次冒泡(選擇)都能找到所求的一個(gè)元素。這類(lèi)策略的時(shí)間復(fù)雜度是O(Kn)。
3.對(duì)輸入內(nèi)容不進(jìn)行排序,顯而易見(jiàn),這種策略將會(huì)有更好的性能開(kāi)銷(xiāo)。我們此時(shí)可以選擇兩種策略進(jìn)行處理:
用一個(gè)桶來(lái)裝前k個(gè)數(shù),桶里面可以按照最小堆來(lái)維護(hù)
a)利用最小堆維護(hù)一個(gè)大小為K的數(shù)組,目前該小根堆中的元素是排名前K的數(shù),其中根是最小的數(shù)。此后,每次從原數(shù)組中取一個(gè)元素與根進(jìn)行比較,如大于根的元素,則將根元素替換并進(jìn)行堆調(diào)整(下沉),即保證小根堆中的元素仍然是排名前K的數(shù),且根元素仍然最小;否則不予處理,取下一個(gè)數(shù)組元素繼續(xù)該過(guò)程。該算法的時(shí)間復(fù)雜度是O(nlogK),一般來(lái)說(shuō)企業(yè)中都采用該策略處理top-K問(wèn)題,因?yàn)樵撍惴ú恍枰淮螌⒃瓟?shù)組中的內(nèi)容全部加載到內(nèi)存中,而這正是海量數(shù)據(jù)處理必然會(huì)面臨的一個(gè)關(guān)卡。
b)利用快速排序的分劃函數(shù)找到分劃位置K,則其前面的內(nèi)容即為所求。該算法是一種非常有效的處理方式,時(shí)間復(fù)雜度是O(n)(證明可以參考算法導(dǎo)論書(shū)籍)。對(duì)于能一次加載到內(nèi)存中的數(shù)組,該策略非常優(yōu)秀。
(9) Bitmap的使用,存儲(chǔ)和插入方法
BitMap從字面的意思
很多人認(rèn)為是位圖,其實(shí)準(zhǔn)確的來(lái)說(shuō),翻譯成基于位的映射。
在所有具有性能優(yōu)化的數(shù)據(jù)結(jié)構(gòu)中,大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量時(shí)間,多么的簡(jiǎn)潔優(yōu)美。但是數(shù)據(jù)量大了,內(nèi)存就不夠了。
當(dāng)然也可以使用類(lèi)似外排序來(lái)解決問(wèn)題的,由于要走IO所以時(shí)間上又不行。
所謂的Bit-map就是用一個(gè)bit位來(lái)標(biāo)記某個(gè)元素對(duì)應(yīng)的Value, 而Key即是該元素。由于采用了Bit為單位來(lái)存儲(chǔ)數(shù)據(jù),因此在存儲(chǔ)空間方面,可以大大節(jié)省。
其實(shí)如果你知道計(jì)數(shù)排序的話(算法導(dǎo)論中有一節(jié)講過(guò)),你就會(huì)發(fā)現(xiàn)這個(gè)和計(jì)數(shù)排序很像。
bitmap應(yīng)用
1)可進(jìn)行數(shù)據(jù)的快速查找,判重,刪除,一般來(lái)說(shuō)數(shù)據(jù)范圍是int的10倍以下。2)去重?cái)?shù)據(jù)而達(dá)到壓縮數(shù)據(jù)還可以用于爬蟲(chóng)系統(tǒng)中url去重、解決全組合問(wèn)題。
BitMap應(yīng)用:排序示例
假設(shè)我們要對(duì)0-7內(nèi)的5個(gè)元素(4,7,2,5,3)排序(這里假設(shè)這些元素沒(méi)有重復(fù))。那么我們就可以采用Bit-map的方法來(lái)達(dá)到排序的目的。要表示8個(gè)數(shù),我們就只需要8個(gè)Bit(1Bytes),首先我們開(kāi)辟1Byte的空間,將這些空間的所有Bit位都置為0(如下圖:)
然后遍歷這5個(gè)元素,首先第一個(gè)元素是4,那么就把4對(duì)應(yīng)的位置為1(可以這樣操作 p+(i/8)|(0×01<<(i%8)) 當(dāng)然了這里的操作涉及到Big-ending和Little-ending的情況,這里默認(rèn)為Big-ending。不過(guò)計(jì)算機(jī)一般是小端存儲(chǔ)的,如intel。小端的話就是將倒數(shù)第5位置1),因?yàn)槭菑牧汩_(kāi)始的,所以要把第五位置為一(如下圖):
然后再處理第二個(gè)元素7,將第八位置為1,,接著再處理第三個(gè)元素,一直到最后處理完所有的元素,將相應(yīng)的位置為1,這時(shí)候的內(nèi)存的Bit位的狀態(tài)如下:
然后我們現(xiàn)在遍歷一遍Bit區(qū)域,將該位是一的位的編號(hào)輸出(2,3,4,5,7),這樣就達(dá)到了排序的目的。
bitmap排序復(fù)雜度分析
Bitmap排序需要的時(shí)間復(fù)雜度和空間復(fù)雜度依賴于數(shù)據(jù)中最大的數(shù)字。
bitmap排序的時(shí)間復(fù)雜度不是O(N)的,而是取決于待排序數(shù)組中的最大值MAX,在實(shí)際應(yīng)用上關(guān)系也不大,比如我開(kāi)10個(gè)線程去讀byte數(shù)組,那么復(fù)雜度為:O(Max/10)。也就是要是讀取的,可以用多線程的方式去讀取。時(shí)間復(fù)雜度方面也是O(Max/n),其中Max為byte[]數(shù)組的大小,n為線程大小。
空間復(fù)雜度應(yīng)該就是O(Max/8)bytes吧
BitMap算法流程
假設(shè)需要排序或者查找的最大數(shù)MAX=10000000(lz:這里MAX應(yīng)該是最大的數(shù)而不是int數(shù)據(jù)的總數(shù)!),那么我們需要申請(qǐng)內(nèi)存空間的大小為int a[1 + MAX/32]。
其中:a[0]在內(nèi)存中占32為可以對(duì)應(yīng)十進(jìn)制數(shù)0-31,依次類(lèi)推:
bitmap表為:
a[0]--------->0-31
a[1]--------->32-63
a[2]--------->64-95
a[3]--------->96-127
…
我們要把一個(gè)整數(shù)N映射到Bit-Map中去,首先要確定把這個(gè)N Mapping到哪一個(gè)數(shù)組元素中去,即確定映射元素的index。我們用int類(lèi)型的數(shù)組作為map的元素,這樣我們就知道了一個(gè)元素能夠表示的數(shù)字個(gè)數(shù)(這里是32)。于是N/32就可以知道我們需要映射的key了。所以余下來(lái)的那個(gè)N%32就是要映射到的位數(shù)。
1.求十進(jìn)制數(shù)對(duì)應(yīng)在數(shù)組a中的下標(biāo):
先由十進(jìn)制數(shù)n轉(zhuǎn)換為與32的余可轉(zhuǎn)化為對(duì)應(yīng)在數(shù)組a中的下標(biāo)。
如十進(jìn)制數(shù)0-31,都應(yīng)該對(duì)應(yīng)在a[0]中,比如n=24,那么 n/32=0,則24對(duì)應(yīng)在數(shù)組a中的下標(biāo)為0。又比如n=60,那么n/32=1,則60對(duì)應(yīng)在數(shù)組a中的下標(biāo)為1,同理可以計(jì)算0-N在數(shù)組a中的下標(biāo)。
i = N>>K % 結(jié)果就是N/(2^K)
Note: map的范圍是[0, 原數(shù)組最大的數(shù)對(duì)應(yīng)的2的整次方數(shù)-1]。
2.求十進(jìn)制數(shù)對(duì)應(yīng)數(shù)組元素a[i]在0-31中的位m:
十進(jìn)制數(shù)0-31就對(duì)應(yīng)0-31,而32-63則對(duì)應(yīng)也是0-31,即給定一個(gè)數(shù)n可以通過(guò)模32求得對(duì)應(yīng)0-31中的數(shù)。
m = n & ((1 << K) - 1) %結(jié)果就是n%(2^K)
3.利用移位0-31使得對(duì)應(yīng)第m個(gè)bit位為1
如a[i]的第m位置1:a[i] = a[i] | (1<<m)
如:將當(dāng)前4對(duì)應(yīng)的bit位置1的話,只需要1左移4位與B[0] | 即可。
Note:
1 p+(i/8)|(0×01<<(i%8))這樣也可以?
2 同理將int型變量a的第k位清0,即a=a&~(1<<k)
BitMap算法評(píng)價(jià)
優(yōu)點(diǎn):
1. 運(yùn)算效率高,不進(jìn)行比較和移位;
2. 占用內(nèi)存少,比如最大的數(shù)MAX=10000000;只需占用內(nèi)存為MAX/8=1250000Byte=1.25M。
3.
缺點(diǎn):
1. 所有的數(shù)據(jù)不能重復(fù),即不可對(duì)重復(fù)的數(shù)據(jù)進(jìn)行排序。(少量重復(fù)數(shù)據(jù)查找還是可以的,用2-bitmap)。
2. 當(dāng)數(shù)據(jù)類(lèi)似(1,1000,10萬(wàn))只有3個(gè)數(shù)據(jù)的時(shí)候,用bitmap時(shí)間復(fù)雜度和空間復(fù)雜度相當(dāng)大,只有當(dāng)數(shù)據(jù)比較密集時(shí)才有優(yōu)勢(shì)。
http://blog.csdn.net/pipisorry/article/details/62443757
(10) 字典樹(shù)的理解以及在統(tǒng)計(jì)上的應(yīng)用
Trie的核心思想是空間換時(shí)間。利用字符串的公共前綴來(lái)降低查詢時(shí)間的開(kāi)銷(xiāo)以達(dá)到提高效率的目的。Trie樹(shù)也有它的缺點(diǎn),Trie樹(shù)的內(nèi)存消耗非常大.當(dāng)然,或許用左兒子右兄弟的方法建樹(shù)的話,可能會(huì)好點(diǎn).
就是在海量數(shù)據(jù)中找出某一個(gè)數(shù),比如2億QQ號(hào)中查找出某一個(gè)特定的QQ號(hào)。。
(11) N個(gè)骰子出現(xiàn)和為m的概率
典型的可以用動(dòng)態(tài)規(guī)劃的思想來(lái)完成
1.現(xiàn)在變量有:骰子個(gè)數(shù),點(diǎn)數(shù)和。當(dāng)有k個(gè)骰子,點(diǎn)數(shù)和為n時(shí),出現(xiàn)次數(shù)記為f(k,n)。那與k-1個(gè)骰子階段之間的關(guān)系是怎樣的?
2.當(dāng)我有k-1個(gè)骰子時(shí),再增加一個(gè)骰子,這個(gè)骰子的點(diǎn)數(shù)只可能為1、2、3、4、5或6。那k個(gè)骰子得到點(diǎn)數(shù)和為n的情況有:
(k-1,n-1):第k個(gè)骰子投了點(diǎn)數(shù)1
(k-1,n-2):第k個(gè)骰子投了點(diǎn)數(shù)2
(k-1,n-3):第k個(gè)骰子投了點(diǎn)數(shù)3
…
(k-1,n-6):第k個(gè)骰子投了點(diǎn)數(shù)6
在k-1個(gè)骰子的基礎(chǔ)上,再增加一個(gè)骰子出現(xiàn)點(diǎn)數(shù)和為n的結(jié)果只有這6種情況!
所以:f(k,n)=f(k-1,n-1)+f(k-1,n-2)+f(k-1,n-3)+f(k-1,n-4)+f(k-1,n-5)+f(k-1,n-6)
3.有1個(gè)骰子,f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1。
用遞歸就可以解決這個(gè)問(wèn)題:
用迭代來(lái)完成
(19) 海量數(shù)據(jù)問(wèn)題(可參考左神的書(shū))
目前關(guān)于海量數(shù)據(jù)想到的解決辦法:
1.bitmap
2.桶排序,外部排序,將需要排序的放到外存上,不用全部放到內(nèi)存上
(20) 一致性哈希
說(shuō)明
http://www.zsythink.net/archives/1182
優(yōu)點(diǎn)
1.當(dāng)后端是緩存服務(wù)器時(shí),經(jīng)常使用一致性哈希算法來(lái)進(jìn)行負(fù)載均衡。使用一致性哈希的好處在于,增減集群的緩存服務(wù)器時(shí),只有少量的緩存會(huì)失效,回源量較小。
2.盡量減少數(shù)據(jù)丟失問(wèn)題,減少移動(dòng)數(shù)據(jù)的風(fēng)險(xiǎn)
總結(jié)
以上是生活随笔為你收集整理的数据结构算法常见面试考题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 安卓初级开发教程 ppt+视频+案例源码
- 下一篇: 基于matlab人脸识别论文,基于mat