淺談算法和數據結構: 七 二叉查找樹

前文介紹了符號表的兩種實現,無序鏈表和有序數組,無序鏈表在插入的時候具有較高的靈活性,而有序數組在查找時具有較高的效率,本文介紹的二叉查找樹(Binary Search Tree,BST)這一數據結構綜合了以上兩種數據結構的優點。

二叉查找樹具有很高的靈活性,對其優化可以生成平衡二叉樹,紅黑樹等高效的查找和插入數據結構,后文會一一介紹。

一 定義

二叉查找樹(Binary Search Tree),也稱有序二叉樹(ordered binary tree),排序二叉樹(sorted binary tree),是指一棵空樹或者具有下列性質的二叉樹:

1. 若任意節點的左子樹不空,則左子樹上所有結點的值均小于它的根結點的值;

2. 若任意節點的右子樹不空,則右子樹上所有結點的值均大于它的根結點的值;

3. 任意節點的左、右子樹也分別為二叉查找樹。

4. 沒有鍵值相等的節點(no duplicate nodes)。

如下圖,這個是普通的二叉樹:

binary tree

在此基礎上,加上節點之間的大小關系,就是二叉查找樹:

binary search tree

二 實現

在實現中,我們需要定義一個內部類Node,它包含兩個分別指向左右節點的Node,一個用于排序的Key,以及該節點包含的值Value,還有一個記錄該節點及所有子節點個數的值Number。

public class BinarySearchTreeSymbolTable<TKey, TValue> : SymbolTables<TKey, TValue> where TKey : IComparable<TKey>, IEquatable<TValue>
{private Node root;private class Node{public Node Left { get; set; }public Node Right { get; set; }public int Number { get; set; }public TKey Key { get; set; }public TValue Value { get; set; }public Node(TKey key, TValue value, int number){this.Key = key;this.Value = value;this.Number = number;}}
...
}

查找

查找操作和二分查找類似,將key和節點的key比較,如果小于,那么就在Left Node節點查找,如果大于,則在Right Node節點查找,如果相等,直接返回Value。

?SearchhitAndSearchMissinBST

該方法實現有迭代和遞歸兩種。

遞歸的方式實現如下:

public override TValue Get(TKey key)
{TValue result = default(TValue);Node node = root;while (node != null){if (key.CompareTo(node.Key) > 0){node = node.Right;}else if (key.CompareTo(node.Key) < 0){node = node.Left;}else{result = node.Value;break;}}return result;
}

迭代的如下:

public TValue Get(TKey key)
{return GetValue(root, key);
}private TValue GetValue(Node root, TKey key)
{if (root == null) return default(TValue);int cmp = key.CompareTo(root.Key);if (cmp > 0) return GetValue(root.Right, key);else if (cmp < 0) return GetValue(root.Left, key);else return root.Value;
}

插入

插入和查找類似,首先查找有沒有和key相同的,如果有,更新;如果沒有找到,那么創建新的節點。并更新每個節點的Number值,代碼實現如下:

public override void Put(TKey key, TValue value)
{root = Put(root, key, value);
}private Node Put(Node x, TKey key, TValue value)
{//如果節點為空,則創建新的節點,并返回//否則比較根據大小判斷是左節點還是右節點,然后繼續查找左子樹還是右子樹//同時更新節點的Number的值if (x == null) return new Node(key, value, 1);int cmp = key.CompareTo(x.Key);if (cmp < 0) x.Left = Put(x.Left, key, value);else if (cmp > 0) x.Right = Put(x.Right, key, value);else x.Value = value;x.Number = Size(x.Left) + Size(x.Right) + 1;return x;
}private int Size(Node node)
{if (node == null) return 0;else return node.Number;
}

? 插入操作圖示如下:

insert into BST

下面是插入動畫效果:

insert into BST

隨機插入形成樹的動畫如下,可以看到,插入的時候樹還是能夠保持近似平衡狀態:

Insert keys random order in BST

最大最小值

如下圖可以看出,二叉查找樹的最大最小值是有規律的:

the max and min item in bst

從圖中可以看出,二叉查找樹中,最左和最右節點即為最小值和最大值,所以我們只需迭代調用即可。

public override TKey GetMax()
{TKey maxItem = default(TKey);Node s = root;while (s.Right != null){s = s.Right;}maxItem = s.Key;return maxItem;
}public override TKey GetMin()
{TKey minItem = default(TKey);Node s = root;while (s.Left != null){s = s.Left;}minItem = s.Key;return minItem;
}

以下是遞歸的版本:

public TKey GetMaxRecursive()
{return GetMaxRecursive(root);
}private TKey GetMaxRecursive(Node root)
{if (root.Right == null) return root.Key;return GetMaxRecursive(root.Right);
}public TKey GetMinRecursive()
{return GetMinRecursive(root);
}private TKey GetMinRecursive(Node root)
{if (root.Left == null) return root.Key;return GetMinRecursive(root.Left);
}

Floor和Ceiling

查找Floor(key)的值就是所有<=key的最大值,相反查找Ceiling的值就是所有>=key的最小值,下圖是Floor函數的查找示意圖:

floor  function in BST

以查找Floor為例,我們首先將key和root元素比較,如果key比root的key小,則floor值一定在左子樹上;如果比root的key大,則有可能在右子樹上,當且僅當其右子樹有一個節點的key值要小于等于該key;如果和root的key相等,則floor值就是key。根據以上分析,Floor方法的代碼如下,Ceiling方法的代碼類似,只需要把符號換一下即可:

public TKey Floor(TKey key)
{Node x = Floor(root, key);if (x != null) return x.Key;else return default(TKey);
}private Node Floor(Node x, TKey key)
{if (x == null) return null;int cmp = key.CompareTo(x.Key);if (cmp == 0) return x;if (cmp < 0) return Floor(x.Left, key);else{Node right = Floor(x.Right, key);if (right == null) return x;else return right;}
}

刪除

刪除元素操作在二叉樹的操作中應該是比較復雜的。首先來看下比較簡單的刪除最大最小值得方法。

以刪除最小值為例,我們首先找到最小值,及最左邊左子樹為空的節點,然后返回其右子樹作為新的左子樹。操作示意圖如下:

delete minimun in BST

代碼實現如下:

public void DelMin()
{root = DelMin(root);
}private Node DelMin(Node root)
{if (root.Left == null) return root.Right;root.Left = DelMin(root.Left);root.Number = Size(root.Left) + Size(root.Right) + 1;return root;
}

刪除最大值也是類似。

現在來分析一般情況,假定我們要刪除指定key的某一個節點。這個問題的難點在于:刪除最大最小值的操作,刪除的節點只有1個子節點或者沒有子節點,這樣比較簡單。但是如果刪除任意節點,就有可能出現刪除的節點有0個,1 個,2個子節點的情況,現在來逐一分析。

當刪除的節點沒有子節點時,直接將該父節點指向該節點的link設置為null。

?delete node which has 0 childrens

當刪除的節點只有1個子節點時,將該自己點替換為要刪除的節點即可。

delete node which has 1 childrens

當刪除的節點有2個子節點時,問題就變復雜了。

假設我們刪除的節點t具有兩個子節點。因為t具有右子節點,所以我們需要找到其右子節點中的最小節點,替換t節點的位置。這里有四個步驟:

1. 保存帶刪除的節點到臨時變量t

2. 將t的右節點的最小節點min(t.right)保存到臨時節點x

3. 將x的右節點設置為deleteMin(t.right),該右節點是刪除后,所有比x.key最大的節點。

4. 將x的做節點設置為t的左節點。

整個過程如下圖:

delete node which has 2 childrens

對應代碼如下:

public void Delete(TKey key)
{root =Delete(root, key);}private Node Delete(Node x, TKey key)
{int cmp = key.CompareTo(x.Key);if (cmp > 0) x.Right = Delete(x.Right, key);else if (cmp < 0) x.Left = Delete(x.Left, key);else{if (x.Left == null) return x.Right;else if (x.Right == null) return x.Left;else{Node t = x;x = GetMinNode(t.Right);x.Right = DelMin(t.Right);x.Left = t.Left;}}x.Number = Size(x.Left) + Size(x.Right) + 1;return x;
}private Node GetMinNode(Node x)
{if (x.Left == null) return x;else return GetMinNode(x.Left); 
}

以上二叉查找樹的刪除節點的算法不是完美的,因為隨著刪除的進行,二叉樹會變得不太平衡,下面是動畫演示。

delete node in BST

三 分析

二叉查找樹的運行時間和樹的形狀有關,樹的形狀又和插入元素的順序有關。在最好的情況下,節點完全平衡,從根節點到最底層葉子節點只有lgN個節點。在最差的情況下,根節點到最底層葉子節點會有N各節點。在一般情況下,樹的形狀和最好的情況接近。

BST Tree shape

在分析二叉查找樹的時候,我們通常會假設插入的元素順序是隨機的。對BST的分析類似與快速排序中的查找:

BST and quick sort partition

BST中位于頂部的元素就是快速排序中的第一個劃分的元素,該元素左邊的元素全部小于該元素,右邊的元素均大于該元素。

對于N個不同元素,隨機插入的二叉查找樹來說,其平均查找/插入的時間復雜度大約為2lnN,這個和快速排序的分析一樣,具體的證明方法不再贅述,參照快速排序。

?

四 總結

有了前篇文章 二分查找的分析,對二叉查找樹的理解應該比較容易。下面是二叉查找樹的時間復雜度:

analysis of binary search tree

它和二分查找一樣,插入和查找的時間復雜度均為lgN,但是在最壞的情況下仍然會有N的時間復雜度。原因在于插入和刪除元素的時候,樹沒有保持平衡。我們追求的是在最壞的情況下仍然有較好的時間復雜度,這就是后面要講的平衡查找樹的內容了。下文首先講解平衡查找樹的最簡單的一種:2-3查找樹。

希望本文對您了解二叉查找樹有所幫助。

轉載于:https://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/377188.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377188.shtml
英文地址,請注明出處:http://en.pswp.cn/news/377188.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

scala部分應用函數_Scala中的部分函數

scala部分應用函數Scala部分功能 (Scala partial functions) A partial function is a function that returns values only for a specific set of values i.e. this function is not able to return values for some input values. This function is defined so that only som…

《MySQL——備庫多線程復制策略。》

目錄備庫并行復制能力MySQL5.6版本 并行復制策略MariaDB 并行復制策略MySQL5.7版本 并行復制策略MySQL5.7.22版本 并行復制策略總結備庫并行復制能力 主要涉及兩個方面的并行度&#xff1a; 1、客戶端寫入主庫的能力 2、備庫上sql_thread執行中轉日志relay log 1的并行能力…

人臉是門大生意

我們正處在一個新時代的入口。人有70%的能量是被大腦消耗&#xff0c;大腦90%的能量用來處理視覺信息&#xff0c;人臉則承載了絕大部分的視覺信息。我們要討論的是一個比Google Glass更酷的世界。文/程苓峰-云科技網易郵箱的用戶已經可以用人臉而不是密碼來驗證登陸。安卓4.0實…

【SQL】sql版Split函數。用于拆分字符串為單列表格

【SQL】sql版Split函數。用于拆分字符串為單列表格 功能與.net版string.Split函數類似&#xff0c;只不過.net返回的是數組&#xff0c;這個返回的是一個單列表格&#xff0c;每個拆分出來的子串占一行。可選是否移除空格子串和重復項。市面上類似的函數不算少&#xff0c;但大…

線描算法

線描算法 (Line drawing algorithms) The equation for a straight line is ymxb 直線方程為y mx b In this m represent a slope of a line which can be calculated by the my2-y1/x2-x1 where (x1, y1) are the starting position of the points and (x2, y2) are the end…

為移動端網頁構造快速響應按鈕

背景 在谷歌&#xff0c;我們不斷地推測手機網頁應用的可能性。像HTML5這樣的技術使我們網頁版的應用以及運行在手機設備上的原生應用。而這些技術的成就之一就是我們開發了一種新的創建按鈕的方法&#xff0c;使按鈕的響應時間遠遠快于一般的HTML按鈕。在此之前的按鈕或者其他…

Red Gate系列之一 SQL Compare 10.4.8.87 Edition 數據庫比較工具 完全破解+使用教程

Red Gate系列之一 SQL Compare 10.4.8.87 Edition 數據庫比較工具 完全破解使用教程 Red Gate系列文章&#xff1a; Red Gate系列之一 SQL Compare 10.4.8.87 Edition 數據庫比較工具 完全破解使用教程 Red Gate系列之二 SQL Source Control 3.0.13.4214 Edition 數據庫版本控制…

《MySQL——基于位點orGTID的主備切換協議》

一主多從的設置&#xff0c;用于讀寫分離&#xff0c;主庫負責所有的寫入和一部分讀&#xff0c;其他讀請求則由從庫分擔。 一主多從架構下&#xff0c;主庫故障后的主備切換問題。相比于一主一備&#xff0c;多了從庫指向新主庫的過程。 基于位點的主備切換同步 把節點B設…

數據科學和統計學_數據科學中的統計

數據科學和統計學統計 (Statistics) Statistics are utilized to process complex issues in reality with the goal that Data Scientists and Analysts can search for important patterns and changes in Data. In straightforward words, Statistics can be utilized to ge…

java隨機數生成(固定位數)

隨機生成 a 到 b (不包含b)的整數:(int)(Math.random()*(b-a))a; 隨機生成 a 到 b (包含b)的整數:(int)(Math.random()*(b-a1))a;轉載于:https://www.cnblogs.com/zhwl/p/3624726.html

POJ 3670 Eating Together

POJ_3670 由于遞增和遞減是類似的&#xff0c;下面不妨只討論變成遞增序列的情況。 由于Di只有三個數&#xff0c;所以可以考慮將序列分割成三部分&#xff0c;第一部分全部變成1&#xff0c;第二部分全部變成2&#xff0c;第三部分全部變成3。然后我們枚舉3開始的位置&#xf…

《MySQL——如何解決一主多從的讀寫分離的過期讀問題》

目錄兩種架構兩種架構特點強制走主庫方案Sleep方案判斷主備無延遲方案配合semi-sync等主庫位點方案GTID方案兩種架構 基于一主多從的讀寫分離&#xff0c;如何處理主備延遲導致的讀寫分離問題。 讀寫分離的主要目標&#xff1a;分攤主庫壓力。 有兩種架構&#xff1a; 1、客…

json/ 發送形式_24/7的完整形式是什么?

json/ 發送形式24/7&#xff1a;二十四 (24/7: Twenty-Four Seven) 24/7 or 24-7 service, which generally marked "twenty-four seven" is service that is existing at any time and typically, every day in trade business and industry. Substitute orthograph…

《MySQL tips:并發查詢與并發連接區別》

并發連接與并發查詢&#xff0c;并不是一個概念。 在執行show processlist的結果里&#xff0c;看到了幾千個連接&#xff0c;指的是并發連接。 而"當前正在執行"的語句&#xff0c;才是并發查詢。 并發連接數多影響的是內存。 并發查詢太高對CPU不利。一個機器的…

對上拉下拉電阻的作用作個總結(想了解的過來看看)(轉載)

轉自&#xff1a;http://www.amobbs.com/thread-5475279-1-3.html 一、定義&#xff1a;上拉就是將不確定的信號通過一個電阻嵌位在高電平&#xff01;電阻同時起限流作用&#xff01;下拉同理&#xff01;上拉是對器件注入電流&#xff0c;下拉是輸出電流&#xff1b;弱強只是…

給用戶傳入的變量進行轉義操作

先看代碼實現&#xff1a; /* 對用戶傳入的變量進行轉義操作。*/ if (!get_magic_quotes_gpc()) {if (!empty($_GET)){$_GET addslashes_deep($_GET);}if (!empty($_POST)){$_POST addslashes_deep($_POST);}$_COOKIE addslashes_deep($_COOKIE);$_REQUEST addslashes_…

《MySQL——外部檢測與內部統計 判斷 主庫是否出現問題》

目錄select1判斷查表判斷更新判斷外部檢測弊端內部統計一主一備的雙M架構里&#xff0c;主備切換只需要把客戶端流量切換到備庫。 在一主多從的架構里&#xff0c;主備切換要把客戶端流量切換到備庫&#xff0c;也需要把從庫接到新主庫上。 切換有兩種場景&#xff1a;1、主動…

NIM的完整形式是什么?

NIM&#xff1a;無內部消息 (NIM: No Internal Message) NIM is an abbreviation of "No Internal Message". NIM是“無內部消息”的縮寫。 It is an expression, which is commonly used in the Gmail platform. It is written in the subject of the mail, if the…

[Json] C#ConvertJson|List轉成Json|對象|集合|DataSet|DataTable|DataReader轉成Json (轉載)...

點擊下載 ConvertJson.rar 本類實現了 C#ConvertJson|List轉成Json|對象|集合|DataSet|DataTable|DataReader轉成Json|等功能大家先預覽一下 請看代碼 /// <summary> /// 類說明&#xff1a;Assistant /// 編 碼 人&#xff1a;蘇飛 /// 聯系方式&#xff1a;361983679 …

let 只能在嚴格模式下嗎_LET的完整形式是什么?

let 只能在嚴格模式下嗎LET&#xff1a;今天早早離開 (LET: Leaving Early Today) LET is an abbreviation of "Leaving Early Today". LET是“ Leaveing Today Today”的縮寫 。 It is an expression, which is commonly used in the Gmail platform. It is writt…