你曾實現過二叉樹嗎


雖然 .NET/C# 中的各種集合類已經實現了最優的排序設計,但了解基本的算法實現有助於軟件開發中的各種權衡和選擇。

比如,如果你實現過 B+ 樹排序和查找,並將樹節點序列化至二進制文件塊,則你應該已經了解了各種數據庫索引的基本設計。

什么是二叉樹?

所有的樹都呈現了一些共有的特性:

  1. 只有一個根節點;
  2. 除了根節點,所有其他節點有且只有一個父節點;
  3. 無環產生。從任意一個節點開始,都沒有回到該起始節點的路徑。正是前兩個特性保證了無環的成立。

二叉樹(Binary Tree)是一種特殊的樹類型,因為它的所有節點最多只能有兩個子節點。對於二叉樹中指定的節點,它的兩個子節點分別稱為左孩子(left child)和右孩子(right child)。

上圖中,二叉樹 (a) 包含 8 個節點,其中節點 1 為它的根節點。節點 1 的左孩子為節點 2,右孩子為節點 3。注意,節點並不要求同時具有左孩子和右孩子。例如,二叉樹 (a) 中,節點 4 就只有一個右孩子 6。此外,節點也可以沒有孩子節點。例如,二叉樹 (b) 中,節點 4、5、6、7 都沒有孩子節點。

沒有孩子的節點稱為葉節點(Leaf Node),有孩子的節點稱為內節點(Internal Node)。如上圖中,二叉樹 (a) 中節點 6、8 為葉節點,節點 1、2、3、4、5、7 為內節點。

不幸的是,.NET 中並沒有直接提供二叉樹的實現,我們需要自己來實現 BinaryTree 類

二叉樹節點類定義

節點類 BinaryTreeNode 抽象地表示了樹中的一個節點。二叉樹中節點應包括兩部分內容:

  1. 數據;
  2. 子節點:0個、1個、2個;

節點存儲的數據依賴於你的實際需要。就像數組可以存儲整型、字符串和其他類類型的實例一樣,節點也應該如此。這里我們使用泛型來支持這樣的功能。

View Code
  1   /// <summary>
  2   /// 二叉樹節點
  3   /// </summary>
  4   /// <typeparam name="T">The item type</typeparam>
  5   public class BinaryTreeNode<T>
  6   {
  7     #region Constructors
  8 
  9     /// <summary>
 10     /// 二叉樹節點
 11     /// </summary>
 12     public BinaryTreeNode()
 13     {
 14     }
 15 
 16     /// <summary>
 17     /// 二叉樹節點
 18     /// </summary>
 19     /// <param name="value">節點中的值</param>
 20     public BinaryTreeNode(T value)
 21     {
 22       this.Value = value;
 23     }
 24 
 25     /// <summary>
 26     /// 二叉樹節點
 27     /// </summary>
 28     /// <param name="value">節點中的值</param>
 29     /// <param name="parent">節點的父節點</param>
 30     public BinaryTreeNode(T value, BinaryTreeNode<T> parent)
 31     {
 32       this.Value = value;
 33       this.Parent = parent;
 34     }
 35 
 36     /// <summary>
 37     /// 二叉樹節點
 38     /// </summary>
 39     /// <param name="value">節點中的值</param>
 40     /// <param name="parent">節點的父節點</param>
 41     /// <param name="left">節點的左節點</param>
 42     /// <param name="right">節點的右節點</param>
 43     public BinaryTreeNode(T value, 
 44       BinaryTreeNode<T> parent, 
 45       BinaryTreeNode<T> left, 
 46       BinaryTreeNode<T> right)
 47     {
 48       this.Value = value;
 49       this.Right = right;
 50       this.Left = left;
 51       this.Parent = parent;
 52     }
 53 
 54     #endregion
 55 
 56     #region Properties
 57 
 58     /// <summary>
 59     /// 節點值
 60     /// </summary>
 61     public T Value { get; set; }
 62 
 63     /// <summary>
 64     /// 父節點
 65     /// </summary>
 66     public BinaryTreeNode<T> Parent { get; set; }
 67 
 68     /// <summary>
 69     /// 左節點
 70     /// </summary>
 71     public BinaryTreeNode<T> Left { get; set; }
 72 
 73     /// <summary>
 74     /// 右節點
 75     /// </summary>
 76     public BinaryTreeNode<T> Right { get; set; }
 77 
 78     /// <summary>
 79     /// 是否為根節點
 80     /// </summary>
 81     public bool IsRoot { get { return Parent == null; } }
 82 
 83     /// <summary>
 84     /// 是否為葉子節點
 85     /// </summary>
 86     public bool IsLeaf { get { return Left == null && Right == null; } }
 87 
 88     /// <summary>
 89     /// 是否為可訪問的
 90     /// </summary>
 91     internal bool Visited { get; set; }
 92 
 93     #endregion
 94 
 95     #region Public Overridden Functions
 96 
 97     /// <summary>
 98     /// Returns a <see cref="System.String"/> that represents this instance.
 99     /// </summary>
100     /// <returns>
101     /// A <see cref="System.String"/> that represents this instance.
102     /// </returns>
103     public override string ToString()
104     {
105       return Value.ToString();
106     }
107 
108     #endregion
109   }

二叉樹類實現

BinaryTree 類的實例包含了根節點(Root Node)實例的引用,而根節點實例又分別指向它的左右孩子節點實例,以此類推。組成二叉樹的不同的 Node 實例可以分散到 CLR 托管堆中任何位置,它們沒有必要像數組元素那樣連續的存放。

View Code
  1   /// <summary>
  2   /// 二叉樹
  3   /// </summary>
  4   /// <typeparam name="T">二叉樹中節點內容類型</typeparam>
  5   [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
  6   public class BinaryTree<T> : ICollection<T>, IEnumerable<T> where T : IComparable<T>
  7   {
  8     #region Constructor
  9 
 10     /// <summary>
 11     /// 二叉樹
 12     /// </summary>
 13     public BinaryTree()
 14     {
 15       NumberOfNodes = 0;
 16     }
 17 
 18     /// <summary>
 19     /// 二叉樹
 20     /// </summary>
 21     /// <param name="root">二叉樹根節點</param>
 22     public BinaryTree(BinaryTreeNode<T> root)
 23       : this()
 24     {
 25       this.Root = root;
 26     }
 27 
 28     #endregion
 29 
 30     #region Properties
 31 
 32     /// <summary>
 33     /// 樹的根節點
 34     /// </summary>
 35     public BinaryTreeNode<T> Root { get; set; }
 36 
 37     /// <summary>
 38     /// 樹中節點的數量
 39     /// </summary>
 40     protected int NumberOfNodes { get; set; }
 41 
 42     /// <summary>
 43     /// 樹是否為空
 44     /// </summary>
 45     public bool IsEmpty { get { return Root == null; } }
 46 
 47     /// <summary>
 48     /// 獲取樹中節點的最小值
 49     /// </summary>
 50     public T MinValue
 51     {
 52       get
 53       {
 54         if (IsEmpty)
 55           return default(T);
 56 
 57         BinaryTreeNode<T> minNode = Root;
 58         while (minNode.Left != null)
 59           minNode = minNode.Left;
 60 
 61         return minNode.Value;
 62       }
 63     }
 64 
 65     /// <summary>
 66     /// 獲取樹中節點的最大值
 67     /// </summary>
 68     public T MaxValue
 69     {
 70       get
 71       {
 72         if (IsEmpty)
 73           return default(T);
 74 
 75         BinaryTreeNode<T> maxNode = Root;
 76         while (maxNode.Right != null)
 77           maxNode = maxNode.Right;
 78 
 79         return maxNode.Value;
 80       }
 81     }
 82 
 83     #endregion
 84 
 85     #region IEnumerable<T> Members
 86 
 87     /// <summary>
 88     /// Returns an enumerator that iterates through the collection.
 89     /// </summary>
 90     /// <returns>
 91     /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> 
 92     /// that can be used to iterate through the collection.
 93     /// </returns>
 94     public IEnumerator<T> GetEnumerator()
 95     {
 96       foreach (BinaryTreeNode<T> node in Traverse(Root))
 97       {
 98         yield return node.Value;
 99       }
100     }
101 
102     #endregion
103 
104     #region IEnumerable Members
105 
106     /// <summary>
107     /// Returns an enumerator that iterates through a collection.
108     /// </summary>
109     /// <returns>
110     /// An <see cref="T:System.Collections.IEnumerator"/> 
111     /// object that can be used to iterate through the collection.
112     /// </returns>
113     IEnumerator IEnumerable.GetEnumerator()
114     {
115       foreach (BinaryTreeNode<T> node in Traverse(Root))
116       {
117         yield return node.Value;
118       }
119     }
120 
121     #endregion
122 
123     #region ICollection<T> Members
124 
125     /// <summary>
126     /// 新增節點
127     /// </summary>
128     /// <param name="item">The object to add to the 
129     /// <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
130     /// <exception cref="T:System.NotSupportedException">The 
131     /// <see cref="T:System.Collections.Generic.ICollection`1"></see> 
132     /// is read-only.</exception>
133     public void Add(T item)
134     {
135       if (Root == null)
136       {
137         Root = new BinaryTreeNode<T>(item);
138         ++NumberOfNodes;
139       }
140       else
141       {
142         Insert(item);
143       }
144     }
145 
146     /// <summary>
147     /// 清除樹
148     /// </summary>
149     public void Clear()
150     {
151       Root = null;
152     }
153 
154     /// <summary>
155     /// 樹中是否包含此節點
156     /// </summary>
157     /// <param name="item">The object to locate in the 
158     /// <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
159     /// <returns>
160     /// true if item is found in the 
161     /// <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
162     /// </returns>
163     public bool Contains(T item)
164     {
165       if (IsEmpty)
166         return false;
167 
168       BinaryTreeNode<T> currentNode = Root;
169       while (currentNode != null)
170       {
171         int comparedValue = currentNode.Value.CompareTo(item);
172         if (comparedValue == 0)
173           return true;
174         else if (comparedValue < 0)
175           currentNode = currentNode.Left;
176         else
177           currentNode = currentNode.Right;
178       }
179 
180       return false;
181     }
182 
183     /// <summary>
184     /// 將樹中節點拷貝至目標數組
185     /// </summary>
186     /// <param name="array">The array.</param>
187     /// <param name="arrayIndex">Index of the array.</param>
188     public void CopyTo(T[] array, int arrayIndex)
189     {
190       T[] tempArray = new T[NumberOfNodes];
191       int counter = 0;
192       foreach (T value in this)
193       {
194         tempArray[counter] = value;
195         ++counter;
196       }
197       Array.Copy(tempArray, 0, array, arrayIndex, Count);
198     }
199 
200     /// <summary>
201     /// 樹中節點的數量
202     /// </summary>
203     public int Count
204     {
205       get { return NumberOfNodes; }
206     }
207 
208     /// <summary>
209     /// 樹是否為只讀
210     /// </summary>
211     public bool IsReadOnly
212     {
213       get { return false; }
214     }
215 
216     /// <summary>
217     /// 移除節點
218     /// </summary>
219     /// <param name="item">節點值</param>
220     /// <returns>是否移除成功</returns>
221     public bool Remove(T item)
222     {
223       BinaryTreeNode<T> node = Find(item);
224       if (node == null)
225         return false;
226 
227       List<T> values = new List<T>();
228       foreach (BinaryTreeNode<T> l in Traverse(node.Left))
229       {
230         values.Add(l.Value);
231       }
232       foreach (BinaryTreeNode<T> r in Traverse(node.Right))
233       {
234         values.Add(r.Value);
235       }
236 
237       if (node.Parent.Left == node)
238       {
239         node.Parent.Left = null;
240       }
241       else
242       {
243         node.Parent.Right = null;
244       }
245 
246       node.Parent = null;
247 
248       foreach (T v in values)
249       {
250         this.Add(v);
251       }
252 
253       return true;
254     }
255 
256     #endregion
257 
258     #region Private Functions
259 
260     /// <summary>
261     /// 查找指定值的節點
262     /// </summary>
263     /// <param name="value">指定值</param>
264     /// <returns>
265     /// 指定值的節點
266     /// </returns>
267     protected BinaryTreeNode<T> Find(T value)
268     {
269       foreach (BinaryTreeNode<T> node in Traverse(Root))
270         if (node.Value.Equals(value))
271           return node;
272       return null;
273     }
274 
275     /// <summary>
276     /// 遍歷樹
277     /// </summary>
278     /// <param name="node">遍歷搜索的起始節點</param>
279     /// <returns>
280     /// The individual items from the tree
281     /// </returns>
282     [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
283     protected IEnumerable<BinaryTreeNode<T>> Traverse(BinaryTreeNode<T> node)
284     {
285       // 遍歷左子樹
286       if (node.Left != null)
287       {
288         foreach (BinaryTreeNode<T> left in Traverse(node.Left))
289           yield return left;
290       }
291 
292       // 中序遍歷二叉樹, 順序是 左子樹,根,右子樹
293       yield return node;
294 
295       // 遍歷右子樹
296       if (node.Right != null)
297       {
298         foreach (BinaryTreeNode<T> right in Traverse(node.Right))
299           yield return right;
300       }
301     }
302 
303     /// <summary>
304     /// 插入節點
305     /// </summary>
306     /// <param name="value">插入的節點值</param>
307     protected void Insert(T value)
308     {
309       // 從根節點開始比較
310       BinaryTreeNode<T> currentNode = Root;
311       
312       while (true)
313       {
314         if (currentNode == null)
315           throw new InvalidProgramException("The current tree node cannot be null.");
316 
317         // 比較當前節點與新節點的值
318         int comparedValue = currentNode.Value.CompareTo(value);
319         if (comparedValue < 0)
320         {
321           // 當前節點值小於新節點值
322           if (currentNode.Left == null)
323           {
324             currentNode.Left = new BinaryTreeNode<T>(value, currentNode);
325             ++NumberOfNodes;
326             return;
327           }
328           else
329           {
330             currentNode = currentNode.Left;
331           }
332         }
333         else if (comparedValue > 0)
334         {
335           // 當前節點值大於新節點值
336           if (currentNode.Right == null)
337           {
338             currentNode.Right = new BinaryTreeNode<T>(value, currentNode);
339             ++NumberOfNodes;
340             return;
341           }
342           else
343           {
344             currentNode = currentNode.Right;
345           }
346         }
347         else
348         {
349           // 當前節點值等於新節點值
350           currentNode = currentNode.Right;
351         }
352       }
353     }
354 
355     #endregion
356   }

使用舉例

 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       Console.ForegroundColor = ConsoleColor.Green;
 6 
 7       BinaryTree<string> tree = new BinaryTree<string>();
 8       tree.Add("Dennis");
 9       tree.Add("Gao");
10       tree.Add("Is");
11       tree.Add("A");
12       tree.Add("C#");
13       tree.Add("Programmer");
14 
15       Console.WriteLine("Root Node Is : " + tree.Root.ToString());
16       Console.WriteLine();
17 
18       foreach (var node in tree)
19       {
20         Console.WriteLine(node);
21       }
22 
23       Console.ReadKey();
24     }
25   }

中序遍歷二叉樹

參考資料

本文《你曾實現過二叉樹嗎》由 Dennis Gao 發表自博客園博客,任何未經作者本人允許的人為或爬蟲轉載均為耍流氓。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM