前言
這個系列是畢業找工作的復習筆記,希望可以和廣大正准備畢業的童鞋一起打牢基礎,迎接各種筆試……為了應付中英文筆試,關鍵詞都用英文進行標注,這樣就不怕面對英文題目了。之所以開始這一系列是因為之前在參加微軟筆試的時候,被一道stable sorting的選擇題給卡住了,才發現自己的基本功什么時候變得這么差了。既然要找工作就要好好復習,從最基礎的開始。算法的部分來自《The Algorithm Design Manual》的筆記。
結構特點
二叉搜索樹的特點是,小的值在左邊,大的值在右邊,即
比如:
這樣的結構有一個好處是很容易獲得最大值(Maximum)、最小值(minimum)、某元素的前驅(Precursor)、某元素的后繼(Successor)。
最大值:樹的最右節點。
最小值:樹的最左節點。
某元素前驅:左子樹的最右。
某元素的后繼:右子樹的最左。
基本操作
二叉搜索樹的基本操作包括searching、traversal、insertion以及deletion。
(代碼為了省地方沒有按照規范來寫,真正寫代碼的時候請一定遵照規范)
① searching
tree * search_tree(tree *l, item_type x){ if(l == null) return NULL; if(l->item == x) return l; if(x < l->item){ return (search_tree(l->left, x)); } if(x > l->item){ return (search_tree(l->right, x)); } }
時間復雜度為O(h),h為樹的高度。
② traversal
由於小的節點在左邊,大的節點在右邊,因此使用中序(in-order)遍歷可以方便的得到一個sorted list。
void traverse_tree(tree *l){ if(l != NULL){ traverse_tree(l->left); process_item(l->item); traverse_tree(l->right); } }
時間復雜度為O(n),n為樹的總結點數。
③ insertion
insert_tree(tree **l, item_type x, tree *parent){ tree *p; /*temporary pointer*/ if(*l == NULL){ p = malloc(sizeof(tree)); p->item = x; p->left = p->right = NULL; p->parent = parent; *l = p; return; } if(x < (*l)->item){ insert_tree(&((*l)->left), x, *l); }else{ insert_tree(&((*l)->right), x, *l); } }
時間復雜度為O(h),h為樹的高度。
④deletion
在刪除節點時有三種情況:
1)要刪除的節點為葉節點
那么直接刪除即可。
2)要刪除的節點有一個子節點
那么刪除掉該節點,並用其唯一的子節點代替自己的位置即可。
3)要刪除的節點有兩個子節點
那么首先要找到該節點的右子樹的最小值節點k,然后將該k替換掉待刪除節點。
最壞情況下,時間復雜度為O(h)+指針的移動開銷。
進階
由上可知,二叉搜索樹的dictionary operation(包括search、insertion、deletion)的時間復雜度均與O(h)相關,h為樹的高度(log n),如果按照上述的insertion方法構建樹,那么構建出來的樹的形狀各異,特別是當輸入序列有序時,更會退化到鏈表的程度。所以,如果能用某種方法,將樹的高度降低到最小,那么其dictionary operation的時間開銷均可以降低,不過相對而言構建樹的開銷將增大。為了降低二叉搜索樹的高度而提出了平衡二叉樹(Balanced Binary Tree)的概念。它要求左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。這樣就可以將搜索樹的高度盡量減小。常用算法有紅黑樹、AVL、Treap、伸展樹等。