二叉查找樹
二叉查找樹是二叉樹中最常用的一種類型,也叫二叉搜索樹。
二叉查找樹要求,在樹中的任意一個節點,其左子的每個節點的值,都要小于這個節點的值,而右子樹節點的值都大于這個節點的值。
二叉查找樹
二叉查找樹的查找操作
- 對比當前節點(首個是根節點),相等則返回。
- 大于則從右子樹查找
- 小于則從左子樹查找
- 直到葉子節點,如果不存在,則返回空,存在則返回。
查找
代碼實現:
public class BinarySearchTree
{
private Node tree;
public Node Find(int data)
{
Node p = tree;
while (p != null)
{
if (data < p.data) p = p.left;
else if (data > p.data) p = p.right;
else return p;
}
return null;
}
public class Node
{
public int data;
public Node left;
public Node right;
public Node(int data)
{
this.data = data;
}
}
}
二叉查找樹的插入操作
插入操作有點類似查找操作。
- 如果要插入的數據比當前節點的數據大,并且節點的右子樹為空,則新數據直接插入右子節點的位置
- 如果不為空,再遞歸遍歷右子樹,查找插入位置
- 同理,插入的數據比當前節點的數據要小,并且節點的左子樹為空,則插入左子節點的位置。
- 如果不為空,再遞歸遍歷左子樹,查找插入位置。
插入
代碼實現:
public void Insert(int data)
{
if (tree == null)
{
tree = new Node(data);
return;
}
Insert(tree, data);
}
private void Insert(Node node, int data)
{
if (data > node.data)
{
if (node.right == null)
node.right = new Node(data);
else
Insert(node.right, data);
}
else if (data < node.data)
{
if (node.left == null)
node.left = new Node(data);
else
Insert(node.left, data);
}
}
二叉查找樹的刪除操作
相比二叉查找樹的查找和插入操作,刪除操作要復雜不少,有三種情況:
- 情況一:刪除節點沒有沒有子節點,父節點的左節點(或右節點)設置為null。
- 情況二:刪除節點只有一個子節點,父節點的左節點(或右節點)直接連接此葉子節點。
- 情況三:刪除節點有兩個子節點,則查找刪除節點的右子樹的最小節點,替換到刪除節點的位置,然后再刪除掉這個最小節點。
刪除
代碼實現:
public void Delete(int data)
{
Node p = tree;
Node pp = null;
while (p != null)
{
if (data < p.data)
{
pp = p;
p = p.left;
}
else if (data > p.data)
{
pp = p;
p = p.right;
}
else
{
break;
}
}
if (p == null) return;
// 刪除的結點有兩個結點
if (p.left != null && p.right != null)
{
// 查找右子樹中最小節點
Node minP = p.right;
Node minPP = p; // minPP表示minP的父節點
while (minP.left != null)
{
minPP = minP;
minP = minP.left;
}
p.data = minP.data; // 將minP的數據替換到p中
p = minP; // 下面就變成了刪除minP了
pp = minPP;
}
// 刪除的是結點是葉子結點或僅有一個結點
Node child;
if (p.left != null) child = p.left;
else if (p.right != null) child = p.right;
else child = null;
if (pp == null) tree = null; // 如果pp=null,表示樹只有一個結點
else if (pp.left == p) pp.left = child;
else pp.right = child;
}
二叉查找樹的其他操作
除了插入、刪除、查找操作之外,二叉查找樹還可以支持快速查找最大節點和最早小節點、前驅節點和后繼節點。
另外還有一個重要的特性,就是中序遍歷二叉查找樹,可以輸出有序的數據序列,時間復雜度是O(n)。因此,二叉查找樹也叫作二叉排序樹。
二叉查找樹的時間復雜度分析
image
最壞情況,二叉查找樹退化成鏈表,時間復雜度為O(n)。
最好情況,二叉查找樹是完全二叉樹(或滿二叉樹),時間復雜度為O(height)。而完全二叉樹的高度是log2(n),即時間復雜度是O(logn)。
課后思考
今天我講了二叉樹高度的理論分析方法,給出了粗略的數量級。如何通過編程,求出一棵給 定二叉樹的確切高度呢?
使用廣度遍歷,通過兩個隊列,一個記錄廣度遍歷的順序,一個記錄廣度遍歷的節點的高度。遍歷過程中,找到最大的高度。
代碼實現:
public int GetHeight()
{
if (tree == null) return 0;
// 廣度遍歷,通過兩個隊列,queueN記錄廣度遍歷的順序,queueH記錄queueN相對應節點的高度
Queue<Node> queueN = new Queue<Node>();
Queue<int> queueH = new Queue<int>();
queueN.Enqueue(tree);
queueH.Enqueue(1);
int maxHeight = 1;
while (queueN.Count > 0)
{
Node p = queueN.Dequeue();
int height = queueH.Dequeue();
if (maxHeight < height) maxHeight = height;
if (p.left != null) { queueN.Enqueue(p.left); queueH.Enqueue(height + 1); }
if (p.right != null) { queueN.Enqueue(p.right); queueH.Enqueue(height + 1); }
}
return maxHeight;
}