【面試題】二叉樹相關
1.二叉樹
- 二叉樹是每個節點最多有兩個子樹的樹結構
- 滿二叉樹:除葉子節點外,所有節點的度都為2
完全二叉樹:葉子結點只能出現在最下兩層;最下層的葉子一定集中在左部連續位置;倒數二層,若有葉子結點,一定都在右部連續位置;如果結點度為1 ,則該結點只有左孩子,即不存在只有右子樹的情況;同樣結點數的二叉樹,完全二叉樹的深度最小。
哈夫曼樹是一種帶權路徑長度最短的二叉樹,也稱為最優二叉樹
2.前中序遍歷的程式碼實現(遞迴與非遞迴)
//建立二叉樹(前序遍歷)定義字串
int index = 0;
public Node init(String s) {
if (index >= s.length()) {
return null;
}
char ch = s.charAt(index++);
if (ch == '#') {
return null;
}
Node node = new Node(ch);
node.setLchild(init(s));
node.setRchild(init(s));
this.root = node;
return root;
}
//遞迴前序遍歷
public void preOrderTraverse(Node root) {
if (root == null) {
return;
}
System.out.print(root.getData()+" ");
preOrderTraverse(root.getLchild());
preOrderTraverse(root.getRchild());
}
//遞迴中序遍歷
public void inOrderTraverse(Node root) {
if (root == null ) {
return;
}
inOrderTraverse(root.getLchild());
System.out.print(root.getData()+" ");
inOrderTraverse(root.getRchild());
}
//遞迴後序遍歷
public void postOrderTraverse(Node root) {
if (root == null) {
return;
}
postOrderTraverse(root.getLchild());
postOrderTraverse(root.getRchild());
System.out.print(root.getData()+" ");
}
//非遞迴方式前序遍歷
public void preOrder(Node root) {
Stack<Node> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
System.out.print(root.getData()+" ");
stack.push(root);
root = root .getLchild();
}
if (!stack.isEmpty()) {
root = stack.pop();
root = root.getRchild();
}
}
}
//非遞迴中序遍歷
public void inOrder(Node root) {
Stack<Node> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.getLchild();
}
if (!stack.isEmpty()) {
root = stack.pop();
System.out.print(root.getData()+" ");
root = root.getRchild();
}
}
}
//非遞迴後序遍歷一
public void postOrder(Node root){
Node preNode = null;//記錄之前遍歷的右結點
Stack<Node> stack = new Stack<Node>();
while(root != null || !stack.isEmpty()){
//左子樹一直入棧
while(root != null){
stack.push(root);
root = root.getLchild();
}
if (!stack.isEmpty()) {
root = stack.peek();//獲得棧頂節點但不出棧
//如果右結點為空,或者右結點之前遍歷過,列印根結點
if (root.getRchild() == null || root.getRchild() == preNode) {
System.out.print(root.getData() + " ");
root = stack.pop();
preNode = root;
root = null;
} else {
root = root.getRchild();
}
}
}
}
//非遞迴遍歷二
public void postOrder2(Node root) {
Node pre = null;
Stack<Node> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.peek();
if ((root.getLchild() == null && root.getRchild() == null) ||
((pre != null)&&(root.getRchild() == pre || root.getLchild() == pre))) {
System.out.print(root.getData()+" ");
pre = stack.pop();
}else {
if (root.getRchild() != null) {
stack.push(root.getRchild());
}
if (root.getLchild() != null) {
stack.push(root.getLchild());
}
}
}
}
3.AVL樹
平衡二叉樹是一種高度平衡的二叉排序樹(父節點大於左子樹、小於右子樹),它的每一個節點的左子樹和右子樹的高度差至多等於1 ,二叉樹上結點的左子樹深度減去右子樹深度的值稱為平衡因子BF ,取值為-1,0,或1。最小不平衡子樹:距離插入結點最近的,且平衡因子的絕對值大於1 的結點為根的子樹。
單左旋與單右旋程式碼實現
//單左旋
public Node leftRotation(Node root) {
Node p = root.right;
root.right = p.left;
p.left = root;
return p;
}
//單右旋
public Node rightRotation(Node root) {
Node p = root.left;
root.left = p.right;
p.right = root;
return p;
}
4.B 樹與 B+樹
B樹又稱為多路平衡查詢樹,它是一個節點可以擁有多於2個子節點的二叉查詢樹。可以保持資料排序,它能夠儲存資料、對其進行排序並允許以O(log n)的時間複雜度執行進行查詢、順序讀取、插入和刪除的資料結構。運用在資料庫和檔案系統。
m階B 樹的性質:樹中每個結點最多含有m個孩子(m>=2);若根結點不是葉子結點,則至少有2個孩子;除根結點和葉子結點外,其它每個結點至少有[ceil(m / 2)]個孩子;所有葉子結點都出現在同一層,葉子結點不包含任何關鍵字資訊;每個非終端結點中包含有n個關鍵字資訊,並且以升序排列。
B+樹與B樹區別
- B+樹中有n棵子樹的結點中含有n個關鍵字,而B 樹是n棵子樹有n-1個關鍵字
- B+樹所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,所有的葉子結點和相連的節點使用連結串列按從小到大的順序相連,便於區間查詢和遍歷。而B 樹的葉子節點並沒有包括全部需要查詢的資訊。
- B+樹所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B 樹的非終節點也包含需要查詢的有效資訊)
- B+樹的葉子結點都是相鏈的,因此對整棵樹的便利只需要一次線性遍歷葉子結點即可。而B樹則需要進行每一層的遞迴遍歷。相鄰的元素可能在記憶體中不相鄰,所以快取命中性沒有B+樹好。
- B+樹在內部節點上不包含資料資訊,因此在記憶體頁中能夠存放更多的key。 資料存放的更加緊密,具有更好的空間區域性性。因此訪問葉子幾點上關聯的資料也具有更好的快取命中率。B樹的優點:由於B樹的每一個節點都包含key和value,因此經常訪問的元素可能離根節點更近,因此訪問也更迅速
B*樹在B+ 樹的非根和非葉子結點再增加指向兄弟的指標
5.紅黑樹
紅黑樹是2-3樹的一種簡單高效的實現,他巧妙地使用顏色標記來替代2-3樹中比較難處理的3-node節點問題。統計效能要好於AV樹。紅黑樹的查詢,插入(三種情況)和刪除(四種情況)都是O(logN)的,原因就是整個紅黑樹的高度是logN,查詢從根到葉,走過的路徑是樹的高度
紅黑樹的性質:節點是紅色或黑色。根節點是黑色。所有葉子節點都是黑色(葉子是NIL節點)。每個紅色節點的子節點和父節點都是黑色。從任一節點到其每個葉子的所有簡單路徑 都包含相同數目的黑色節點。
RBT的左旋與右旋
//左旋轉
private Node RotateLeft(Node root)
{
Node p = root.Right;
//將p的左節點複製給root右節點
root.Right = p.Left;
//將root複製給p右節點
p.Left = root;
p.Color = root.Color;
root.Color = RED;
return p;
}
//右旋轉
private Node RotateRight(Node root)
{
Node p = root.Left;
root.Left = p.Right;
p.Right = root;
p.Color = root.Color;
root.Color = RED;
return p;
}
6.hash衝突
雜湊技術是在記錄的儲存位置和它的關鍵字之間建立一個確定的對應關係f,使得每個關鍵字key 對應一個儲存位置f (key),對應關係f 稱為雜湊函式, 將記錄儲存在一塊連續的儲存空間中,這塊連續儲存空間稱為散列表
雜湊函式的構造方法:直接定址法、數字分析法、平方取中法、摺疊法、隨機數法、除留取餘法。
處理hash衝突的方法:開放定址法、再雜湊函式法、公共溢位區法、鏈地址法。
hash演算法:加、乘、除、位運算、查表、混合。
本人才疏學淺,若有錯,請指出,謝謝!
如果你有更好的建議,可以留言我們一起討論,共同進步!
衷心的感謝您能耐心的讀完本篇博文!