1. 程式人生 > >面試中紅黑樹常考問題

面試中紅黑樹常考問題

沒有學習過紅黑樹的同學請參考:

<<Introduction to Algorithms>> Chapter 13 Red-Black Trees Chapter 14 Augmenting Data Structures

1.stl中的set底層用的什麼資料結構?

2.紅黑樹的資料結構怎麼定義的?

3.紅黑樹有哪些性質?

4.紅黑樹的各種操作的時間複雜度是多少?

5.紅黑樹相比於BST和AVL樹有什麼優點?

6.紅黑樹相對於雜湊表,在選擇使用的時候有什麼依據?

7.如何擴充套件紅黑樹來獲得比某個結點小的元素有多少個?

8.擴充套件資料結構有什麼步驟?

9 為什麼一般hashtable的桶數會取一個素數

詳細解答

1.stl中的set底層用的什麼資料結構?

紅黑樹

2.紅黑樹的資料結構怎麼定義?

  1. enum Color  
  2. {  
  3.           RED = 0,  
  4.           BLACK = 1  
  5. };  
  6. struct RBTreeNode  
  7. {  
  8.            struct RBTreeNode*left, *right, *parent;  
  9.            int   key;  
  10.            int data;  
  11.            Color color;  
  12. };  

3.紅黑樹有哪些性質?

一般的,紅黑樹,滿足以下性質,即只有滿足以下全部性質的樹,我們才稱之為紅黑樹: 1)每個結點要麼是紅的,要麼是黑的。 2)根結點是黑的。 3)每個葉結點(葉結點即指樹尾端NIL指標或NULL結點)是黑的。 4)如果一個結點是紅的,那麼它的倆個兒子都是黑的。 5)對於任一結點而言,其到葉結點樹尾端NIL指標的每一條路徑都包含相同數目的黑結點。

4.紅黑樹的各種操作的時間複雜度是多少?

能保證在最壞情況下,基本的動態幾何操作的時間均為O(lgn)

5.紅黑樹相比於BST和AVL樹有什麼優點?

紅黑樹是犧牲了嚴格的高度平衡的優越條件為代價,它只要求部分地達到平衡要求,降低了對旋轉的要求,從而提高了效能。紅黑樹能夠以O(log2 n)的時間複雜度進行搜尋、插入、刪除操作。此外,由於它的設計,任何不平衡都會在三次旋轉之內解決。當然,還有一些更好的,但實現起來更復雜的資料結構能夠做到一步旋轉之內達到平衡,但紅黑樹能夠給我們一個比較“便宜”的解決方案。

相比於BST,因為紅黑樹可以能確保樹的最長路徑不大於兩倍的最短路徑的長度,所以可以看出它的查詢效果是有最低保證的。在最壞的情況下也可以保證O(logN)的,這是要好於二叉查詢樹的。因為二叉查詢樹最壞情況可以讓查詢達到O(N)。

紅黑樹的演算法時間複雜度和AVL相同,但統計效能比AVL樹更高,所以在插入和刪除中所做的後期維護操作肯定會比紅黑樹要耗時好多,但是他們的查詢效率都是O(logN),所以紅黑樹應用還是高於AVL樹的. 實際上插入 AVL 樹和紅黑樹的速度取決於你所插入的資料.如果你的資料分佈較好,則比較宜於採用 AVL樹(例如隨機產生系列數),但是如果你想處理比較雜亂的情況,則紅黑樹是比較快的

6.紅黑樹相對於雜湊表,在選擇使用的時候有什麼依據?

權衡三個因素: 查詢速度, 資料量, 記憶體使用,可擴充套件性。   總體來說,hash查詢速度會比map快,而且查詢速度基本和資料量大小無關,屬於常數級別;而map的查詢速度是log(n)級別。並不一定常數就比log(n) 小,hash還有hash函式的耗時,明白了吧,如果你考慮效率,特別是在元素達到一定數量級時,考慮考慮hash。但若你對記憶體使用特別嚴格, 希望程式儘可能少消耗記憶體,那麼一定要小心,hash可能會讓你陷入尷尬,特別是當你的hash物件特別多時,你就更無法控制了,而且 hash的構造速度較慢。

紅黑樹並不適應所有應用樹的領域。如果資料基本上是靜態的,那麼讓他們待在他們能夠插入,並且不影響平衡的地方會具有更好的效能。如果資料完全是靜態的,例如,做一個雜湊表,效能可能會更好一些。 在實際的系統中,例如,需要使用動態規則的防火牆系統,使用紅黑樹而不是散列表被實踐證明具有更好的伸縮性。Linux核心在管理vm_area_struct時就是採用了紅黑樹來維護記憶體塊的。

紅黑樹通過擴充套件節點域可以在不改變時間複雜度的情況下得到結點的秩。

7.如何擴充套件紅黑樹來獲得比某個結點小的元素有多少個?

這其實就是求節點元素的順序統計量,當然任意的順序統計量都可以需要在O(lgn)時間內確定。

在每個節點新增一個size域,表示以結點 x 為根的子樹的結點樹的大小,則有

size[x] = size[[left[x]] + size [right[x]] + 1;

這時候紅黑樹就變成了一棵順序統計樹。

利用size域可以做兩件事:

1). 找到樹中第i小的結點;

  1. OS-SELECT(x;,i)  
  2. r = size[left[x]] + 1;  
  3. if i == r  
  4.      return x  
  5. elseif i < r  
  6.      return OS-SELECT(left[x], i)  
  7. else return OS-SELECT(right[x],  i)  

思路:size[left[x]]表示在對x為根的子樹進行中序遍歷時排在x之前的個數,遞迴呼叫的深度不會超過O(lgn);

2).確定某個結點之前有多少個結點,也就是我們要解決的問題;

  1. OS-RANK(T,x)  
  2. r = x.left.size + 1;  
  3. y = x;  
  4. while y != T.root  
  5.          if y == y.p.right  
  6.                  r = r + y.p.left.size +1  
  7.          y = y.p  
  8. return r  

思路:x的秩可以視為在對樹的中序遍歷種,排在x之前的結點個數加上一。最壞情況下,OS-RANK執行時間與樹高成正比,所以為O (lgn).

8.擴充套件資料結構有什麼步驟?

1).選擇基礎資料結構;

2).確定要在基礎資料結構種新增哪些資訊;

3).驗證可用基礎資料結構上的基本修改操作來維護這些新新增的資訊;

4).設計新的操作。

9 為什麼一般hashtable的桶數會取一個素數

設有一個雜湊函式 H( c ) = c % N; 當N取一個合數時,最簡單的例子是取2^n,比如說取2^3=8,這時候 H( 11100(二進位制) ) = H( 28 ) = 4 H( 10100(二進位制) ) = H( 20 )= 4 這時候c的二進位制第4位(從右向左數)就”失效”了,也就是說,無論第c的4位取什麼值,都會導致H( c )的值一樣.這時候c的第四位就根本不參與H( c )的運算,這樣H( c )就無法完整地反映c的特性,增大了導致衝突的機率. 取其他合數時,都會不同程度的導致c的某些位”失效”,從而在一些常見應用中導致衝突. 但是取質數,基本可以保證c的每一位都參與H( c )的運算,從而在常見應用中減小衝突機率