日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

c# treeview查找并选中节点_最通俗易懂的二叉查找树(BST)详解

發布時間:2023/12/13 C# 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c# treeview查找并选中节点_最通俗易懂的二叉查找树(BST)详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原來來自 呆萌數據結構-06二叉查找樹?imoegirl.com

二叉查找樹(Binary Search Tree),簡寫BST,是滿足某些條件的特殊二叉樹。任何一個節點的左子樹上的點,都必須小于當前節點。任何一個節點的右子樹上的點,都必須大于當前節點。任何一棵子樹,也都滿足上面兩個條件。另外二叉查找樹中,是不存在重復節點的。

上圖中的二叉查找樹,我們從Root節點3開始看,它的左子樹(1,2) 和右子樹(6,4,9,7)分別滿足條件,左子樹上的點,都小于當前節點,右子樹上的點,都大于當前節點。

繼續,我們以6作為起點,來看一下這棵子樹,6的左子樹(4),右子樹(9,7)也滿足上面兩條規則。

整棵樹中,任何一個點下面的子樹,都滿足上面提到的兩條規則。你現在是不是對Binary Search Tree已經有一個大概的形象概念了。

為什么叫做二叉查找樹呢

因為在BST中搜索一個值是非常簡單和高效的。

看上面的樹,假設要搜索7這個節點。首先從Root節點出發,我們知道7大于3,所以會走到右子樹6,然后因為7也大于6,所以會繼續往右子樹走,到了9,因為7小于9,所以會向左子樹走,走到7,發現7等于7,所以找到要搜索的節點。

二叉樹的一些性質

  • 將任何一個點看作Root節點,則這個點的左子樹也是 Binary Search Tree
  • 將任何一個點看作Root節點,則這個點的右子樹也是 Binary Search Tree
  • Binary Search Tree中的最小節點,一定是整棵樹中最左下的葉子節點(從Root開始一直順著左子樹往下走,直到某一個點沒有左子節點,則這個點就是最小的)
  • Binary Search Tree中的最大節點,一定是整棵樹中最右下的葉子節點(從Root開始一直順著右子樹往下走,直到某一個點沒有右子節點,則這個點就是最大的)

怎樣構建和插入節點

向BST中插入一個節點,也是一個構建的過程,和上面的搜索思路基本一樣。首先從Root開始,如果Root點為空,則直接構建Root點。如果Root點不為空,則要判斷要插入的值,比Root點的值大還是小,如果小,則往左子樹走,如果大,則往右子樹走。直到走到某一個點,我們稱為點X,發現要插入的值,小于那個點X的值,并且點X沒有左子樹,則要插入的點作為X的左子節點。或者,要插入的點大于X,并且X沒有右子樹,則要插入的點作為X的右子節點。

下面是代碼實現(為了方便后面的刪除邏輯,我們每一個點,包含了指向左子樹,右子樹,以及父節點的引用)

// 這里先定義出節點的結構 class Node {public int data;public Node parent;public Node left;public Node right;public Node(int _data){this.data = _data;} }// 定義二叉搜索樹結構 class BST {private Node root;// 這個函數是 private 的,遞歸調用,插入節點private Node RecursionInsert(Node node, int data){if (node == null){return new Node(data);}if (data < node.data){node.left = RecursionInsert(node.left, data);node.left.parent = node;}else if (data > node.data){node.right = RecursionInsert(node.right, data);node.right.parent = node;}return node;}// 對外開放的 插入 接口public void Insert(int data){if (root == null){root = RecursionInsert(root, data);}else{RecursionInsert(root, data);}}// 按層序打印二叉樹public void LevelOrderTraversal(){Queue<Node> q = new Queue<Node>();q.Enqueue(root);while (q.Count > 0){Node currNode = q.Dequeue();if (currNode.left != null){q.Enqueue(currNode.left);}if (currNode.right != null){q.Enqueue(currNode.right);}// 括號里面是父節點的值string msg = string.Format("{0}({1})", currNode.data, currNode.parent != null ? currNode.parent.data.ToString() : "null");Debug.Log(msg);}} }// 創建一個二叉搜索樹 class Program {/* Let us create following BST50/ 30 70/ / 20 40 60 80 */static void Main(string[] args){BST bst = new BST();bst.Insert(50);bst.Insert(30);bst.Insert(20);bst.Insert(40);bst.Insert(70);bst.Insert(60);bst.Insert(80);bst.LevelOrderTraversal();} }

上面的代碼,首先定義了每一個 Node 節點的數據結構,然后定義了二叉查找樹的結構類,最后是C#調用BST的插入和打印方法。插入節點這里使用了遞歸的機結構,還可以使用非遞歸,循環的形式去插入。

從最簡單的開始,刪除一個節點

從 BST 中刪除節點,是相比來說比較復雜的,復雜,也只是相對于插入來說。只要理清幾種不同的情況,也就不復雜了??催^很多教程,一上來就是羅列各個情況,然后上代碼展示,如果是第一次學習 BST,可能會有一點抽象。我們先不考慮代碼怎么實現,先從語言上把這個事情講明白,最后再看代碼。

從 BST 中刪除節點其實很容易,只要改變一下指針就可以了,重點是,刪除了一個節點后,還要讓整棵 BST 依然保持一個正確的結構,這就是我們要做的。一切從最簡單開始。

上面的圖中,我們要刪除一個葉子節點,就是左邊的數據為3的那個節點。這個節點的父節點是4,我們只需要將4這個節點中指向左子樹的指針設置為空,就可以了。這是很容易理解的。而刪除右邊的葉子節點,也是一樣的。就像下面的圖。

我們刪除18這個節點,只需要將13這個節點中指向右子樹的指針設置為空,即可。

上面說的,就是刪除操作中最簡單的一種情況,刪除葉子節點。還有一個很重要的點要注意,在刪除的時候,要判斷要刪除的節點是不是 Root 節點,也就是說,整棵樹只有一個節點的情況,這樣的話還需要將 Root 節點設為空。Root節點的父節點,是永遠為空的。

注意: 記住,現在不要考慮代碼實現的問題,一定要先理解思路,文章的最后,會上代碼的。

關于節點刪除,加大一點點難度

接下來我們加大一點點難度。看下面的圖 (可以忽略圖中的紅色字,只看樹的節點結構)

我們要刪除左邊圖中5這個節點,而5這個節點只擁有一個子樹,就是左子樹。而5的父節點是2,它是2的右子樹。我們要刪除5,只需要將2的右子樹,指向5的子樹就可以 (這里其實不太關心5的子樹是左子樹還是右子樹)。簡單來說,就是將2原本指向5的指針,改為指向5的子樹,即可。就是右邊圖的樣子。

我們剛才刪除的5節點,只有左子樹,再看一個要刪除的節點只有右子樹的情況。

上面的圖中, 我們要刪除3這個節點,而這個節點只有右子樹。3的父親節點是2,所以,我們只需要將2原本指向3節點的指針,改為指向3的子樹即可。就像右邊的圖那樣。

不要著急,慢慢體會一下。在上面兩種情況沒有徹底理解思路之前,先不要往下看,否則可能會更困惑。

注意: 因為我們的 Node 結構中加入了一個指向父節點的指針,所以在刪除節點的時候,還要注意更新某些節點的 parent 指針指向。

更復雜的情況,先聊一下后繼節點

如果上面只是小打小鬧,那接下來,就是直面恐懼,噢不,直面復雜時刻啦,哈哈哈~

最復雜的一種情況,就是要刪除的節點,即有左子樹,又有右子樹。在說這種情況怎么操作之前,我們先來說一個前提概念,叫做后繼節點。一個節點的后繼節點,嚴肅點說就是在中序遍歷的時候,遍歷完當前節點后,下一個要訪問的節點,就是當前的節點的后繼節點。好吧,通俗點來講,就是假設在遍歷一棵樹時,訪問完 1 號節點,如果接下來訪問的是3號節點,那3號節點就是1的后繼節點。

那一個點的后繼節點怎么找呢?這個就很簡單了。假設我們要找節點 A 的后繼節點,那就是從 A 這個點的右子樹開始,一路向左走,走到某一個節點沒有左子樹可以往下走了,那這個節點,就是 A 的后繼節點 (注意,這個后繼節點有可能是葉子節點,也有可能不是)。看下面的圖。

我們先看左邊部分的圖,我們要找節點 9 的后繼節點。按上面的規則,從節點 9 的右子樹 15 開始,依次往左走,先到達 15 判斷一下是否可以走,可以,我們走到 13,再判斷一下是否可以繼續往左走,可以,走到 11,然后再看是否可以繼續往左走,發現不可以了,那 11 就是節點 9 的后繼節點。

再看右邊部分的圖,我們要找節點 6 的后繼節點。按上面的規則從右子樹 11 開始,判斷是否可以往左走,可以,走到 8,再判斷是一下是否可以繼續往左走,發現 8 已經沒有左子樹可以往下走了,所以 8 就是節點 6 的后繼節點。

最后一種刪除節點的情況

理解了后繼節點,就可以來說最后一種,也是最復雜的一種刪除節點的情況了。就是要刪除的節點,即有左子樹,又有右子樹。操作的流程是這樣的。假設我們要刪除的節點是 A,第一步,我們要找到 A 的后繼節點。第二步,用 A 的后繼節點數據,替換要刪除的節點 A 的數據。第三步,刪除后繼節點。(因為后繼節點要么是葉子節點,要么只有右子樹,所以刪除比較簡單,就按之前聊過的刪除方法即可)。下面用示例解釋

上圖中,從第一個圖開始看,我們要刪除數據為 9 的節點。第一步,將要刪除的節點使用一個指針指向。第二步,看第二個圖,找到 9 節點的后繼節點,也就是 11。第三步,看第三張圖,用后繼節點的數據,替換要刪除的節點的數據,也就是用 11 替換 9。第四步,也就是最后一個圖,刪除后繼節點 11。到此,刪除操作完成。

接下來再看一個示例

上圖中,我們要刪除節點 6,還是先找到 6 的后繼節點 8,然后用節點 8 的數據,替換我們要刪除的節點 6 的數據(第三個小圖)。接下來就是刪除后繼節點 8,這里要注意,我們跟著箭頭的方向,看第四個小圖。在刪除后繼節點 8 時,我們發現節點 8 不是葉子節點,而是有右子樹,所以我們需要將節點 8 的父節點,原本指向 8 的指針,改為指向 8 的右子樹。也就是上圖中將節點 11 的左指針,改為指向節點 9,然后就是最后一個小圖的情況。(因為我們的 Node 結構中擁有 parent 指針,所以要記得把節點 9 的parent 指針從原來指向 8 改為現在指向 9)。到此,刪除節點結束。

接下來,我們展示完整的代碼

using System; using System.Collections.Generic;static class Debug {public static void Log(string msg){Console.WriteLine(msg);} }class Node {public int data;public Node parent;public Node left;public Node right;public Node(int _data){this.data = _data;Console.WriteLine("Insert: " + this.data);} }class BST {private Node root;private Node RecursionInsert(Node node, int data){if (node == null){return new Node(data);}if (data < node.data){node.left = RecursionInsert(node.left, data);node.left.parent = node;}else if (data > node.data){node.right = RecursionInsert(node.right, data);node.right.parent = node;}return node;}// 插入一個數據public void Insert(int data){if (root == null){root = RecursionInsert(root, data);}else{RecursionInsert(root, data);}}// 刪除節點public void DeleteNode(int data){Node delNode = root;// 首先要找到待刪除的節點while (delNode != null){if (delNode.data == data){break;}if (data < delNode.data){delNode = delNode.left;}else if (data > delNode.data){delNode = delNode.right;}}if (delNode == null){Debug.Log("Not found " + data);return;}// 要刪除的節點即沒有左子樹,也沒有右子樹,是葉子節點,或者是 Root 節點if (delNode.left == null && delNode.right == null){Node parent = delNode.parent;if (parent == null){root = null;}else{if (parent.left == delNode){parent.left = null;}else{parent.right = null;}}}else if (delNode.left != null && delNode.right == null){// 要刪除的節點只有左子樹的情況Node parent = delNode.parent;Node child = delNode.left;if (parent == null){root = child;root.parent = null;}else{if (parent.left == delNode){parent.left = child;}else{parent.right = child;}child.parent = parent;}}else if (delNode.right != null && delNode.left == null){// 要刪除的節點只有右子樹的情況Node parent = delNode.parent;Node child = delNode.right;if (parent == null){root = child;root.parent = null;}else{if (parent.left == delNode){parent.left = child;}else{parent.right = child;}child.parent = parent;}}else if (delNode.left != null && delNode.right != null){// 要刪除的節點即有左子樹,也有右子樹的情況// 首先找到后繼節點Node successorNode = FindMinimumLeftValue(delNode.right);delNode.data = successorNode.data;// 如果后繼節點是葉子節點,則直接刪除即可if (successorNode.left == null && successorNode.right == null){if (successorNode.parent.left == successorNode){successorNode.parent.left = null;}else{successorNode.parent.right = null;}}else{// 如果后繼節點不是葉子節點,要將后繼節點的父節點指向后繼節點的子樹,// 同時,修改子樹父節點的指針Node successorChild = successorNode.left != null ? successorNode.left : successorNode.right;Node parent = successorNode.parent;if (parent.left == successorNode){parent.left = successorChild;}else{parent.right = successorChild;}successorChild.parent = parent;}}}// 找到一顆子樹的最小左節點public Node FindMinimumLeftValue(Node fromNode){Node opt = fromNode;while (opt.left != null){opt = opt.left;}return opt;}public void LevelOrderTraversal(){Queue<Node> q = new Queue<Node>();q.Enqueue(root);while (q.Count > 0){Node currNode = q.Dequeue();if (currNode.left != null){q.Enqueue(currNode.left);}if (currNode.right != null){q.Enqueue(currNode.right);}string msg = string.Format("{0}({1})", currNode.data, currNode.parent != null ? currNode.parent.data.ToString() : "null");Debug.Log(msg);}} }class Program {/* Let us create following BST50/ 30 70/ / 20 40 60 80 */static void Main(string[] args){BST bst = new BST();bst.Insert(50);bst.Insert(50);bst.Insert(30);bst.Insert(20);bst.Insert(40);bst.Insert(70);bst.Insert(60);bst.Insert(80);bst.LevelOrderTraversal();bst.DeleteNode(70);bst.LevelOrderTraversal();} }

好了,終于講完了二叉查找樹最基本的知識。這篇博客內容很長,如果第一遍沒讀懂,也沒關系,先休息一下,過段時間再讀一遍,可能就會更容易理解。


歡迎關注微信公眾號 萌一小棧

總結

以上是生活随笔為你收集整理的c# treeview查找并选中节点_最通俗易懂的二叉查找树(BST)详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。