算法:非平衡二叉搜索树(UnBalanced Binary Search Tree)

背景

很多场景下都需要将元素存储到已排序的集合中。用数组来存储,搜索效率非常高: O(log n),但是插入效率比较低:O(n)。用链表来存储,插入效率和搜索效率都比较低:O(n)。如何能提供插入和搜索效率呢?这就是二叉搜索树的由来,本文先介绍非平衡二叉搜索树。

非平衡二叉搜索树

规则

所有节点的左节点小于节点,所有节点的右节点大于等于自身,即:node.value >  node.left.value && node.value <= node.right.value。

示例

根据上面的规则,我们也很容易找到最大值和最小值,后面也会用到这种算法。最大值可以通过递归方法 node.right 得到,最小值可以递归 node.left 得到。

为什么叫非平衡?

说明:下图变成链表了,这会导致效率非常低,后面找机会再介绍平衡算法。

实现

搜索、遍历(前序、中序和后序)、添加算法都比较简单,可以直接看后面的代码,这里重点介绍一下删除算法。

如何删除元素?

第一步:要找到删除的元素(current)。

第二步:判断 current 满足如下哪种场景:

  1. current.Right == null
    示例

    代码
     1                     if (parent == null)
     2                     {
     3                         this.Root = current.Left;
     4                     }
     5                     else if (isLeft)
     6                     {
     7                         parent.Left = current.Left;
     8                     }
     9                     else
    10                     {
    11                         parent.Right = current.Left;
    12                     }

    结果

  2. current.Right != null && current.Right.Left == null
    示例

    代码
     1                     current.Right.Left = current.Left;
     2 
     3                     if (parent == null)
     4                     {
     5                         this.Root = current.Right;
     6                     }
     7                     else if (isLeft)
     8                     {
     9                         parent.Left = current.Right;
    10                     }
    11                     else
    12                     {
    13                         parent.Right = current.Right;
    14                     }

    结果

  3. current.Right != null && current.Right.Left != null
    示例

    代码
     1                     Node<T> currentRightSmallestParent = current.Right;
     2                     var currentRightSmallest = current.Right.Left;
     3 
     4                     this.FindSmallest(ref currentRightSmallestParent, ref currentRightSmallest);
     5 
     6                     currentRightSmallestParent.Left = currentRightSmallest.Right;
     7                     currentRightSmallest.Left = current.Left;
     8                     currentRightSmallest.Right = current.Right;
     9                     if (parent == null)
    10                     {
    11                         this.Root = currentRightSmallest;
    12                     }
    13                     else if (isLeft)
    14                     {
    15                         parent.Left = currentRightSmallest;
    16                     }
    17                     else
    18                     {
    19                         parent.Right = currentRightSmallest;
    20                     }

    结果

    说明
    这里的重点是 FindSmallest,找出 current.Right.Left 子树中最小的元素,然后用它替换 current。

完整代码

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 namespace DataStuctureStudy.Trees
  8 {
  9     class UnBalancedBinarySearchTree
 10     {
 11         class Node<T>
 12             where T : IComparable<T>
 13         {
 14             public T Value { get; set; }
 15 
 16             public Node<T> Left { get; set; }
 17 
 18             public Node<T> Right { get; set; }
 19 
 20             public void InOrderTraverse(Action<T> action)
 21             {
 22                 if (this.Left != null)
 23                 {
 24                     this.Left.InOrderTraverse(action);
 25                 }
 26 
 27                 action(this.Value);
 28 
 29                 if (this.Right != null)
 30                 {
 31                     this.Right.InOrderTraverse(action);
 32                 }
 33             }
 34 
 35             public int Depth()
 36             {
 37                 var leftDepth = 0;
 38                 var rightDepth = 0;
 39 
 40                 if (this.Left != null)
 41                 {
 42                     leftDepth = this.Left.Depth();
 43                 }
 44                 if (this.Right != null)
 45                 {
 46                     rightDepth = this.Right.Depth();
 47                 }
 48 
 49                 return
 50                     leftDepth > rightDepth
 51                     ? leftDepth + 1
 52                     : rightDepth + 1;
 53             }
 54         }
 55 
 56         public class Tree<T>
 57              where T : IComparable<T>
 58         {
 59             private Node<T> Root { get; set; }
 60 
 61             public void Display()
 62             {
 63                 Console.WriteLine();
 64 
 65                 if (this.Root == null)
 66                 {
 67                     return;
 68                 }
 69 
 70                 var depth = this.Root.Depth();
 71                 var buffers = new string[depth][];
 72                 for (int i = 0; i < buffers.Length; i++)
 73                 {
 74                     buffers[i] = new string[(int)(Math.Pow(2, depth) - 1)];
 75                 }
 76 
 77                 this.BuildArray(this.Root, depth, buffers, 0, 0);
 78 
 79                 for (int i = 0; i < buffers.Length; i++)
 80                 {
 81                     for (int j = 0; j < buffers[i].Length; j++)
 82                     {
 83                         if (buffers[i][j] == null)
 84                         {
 85                             Console.Write(new string(' ', 5));
 86                         }
 87                         else
 88                         {
 89                             var leftPad = (5 - buffers[i][j].Length) / 2;
 90                             Console.Write(buffers[i][j]
 91                                 .PadLeft(leftPad + buffers[i][j].Length)
 92                                 .PadRight(5));
 93                         }
 94                     }
 95                     Console.WriteLine();
 96                     Console.WriteLine();
 97                 }
 98             }
 99 
100             private void BuildArray(Node<T> node, int nodeDepth, string[][] buffers, int row, int startColumn)
101             {
102                 if (node == null)
103                 {
104                     return;
105                 }
106 
107                 var nodeWidth = Math.Pow(2, nodeDepth) - 1;
108                 var column = (int)(startColumn + nodeWidth / 2);
109 
110                 buffers[row][column] = node.Value.ToString();
111 
112                 this.BuildArray(node.Left, nodeDepth - 1, buffers, row + 1, startColumn);
113                 this.BuildArray(node.Right, nodeDepth - 1, buffers, row + 1, column + 1);
114             }
115 
116             public bool Contains(T item)
117             {
118                 var current = this.Root;
119 
120                 while (current != null)
121                 {
122                     if (item.CompareTo(current.Value) == 0)
123                     {
124                         return true;
125                     }
126                     else if (item.CompareTo(current.Value) < 0)
127                     {
128                         current = current.Left;
129                     }
130                     else
131                     {
132                         current = current.Right;
133                     }
134                 }
135 
136                 return false;
137             }
138 
139             public void InOrderTraverse(Action<T> action)
140             {
141                 if (this.Root != null)
142                 {
143                     this.Root.InOrderTraverse(action);
144                 }
145             }
146 
147             public void Insert(T item)
148             {
149                 var node = new Node<T> { Value = item };
150 
151                 Node<T> parent = null;
152                 var current = this.Root;
153                 var isLeft = false;
154 
155                 while (current != null)
156                 {
157                     parent = current;
158 
159                     if (item.CompareTo(current.Value) < 0)
160                     {
161                         current = current.Left;
162                         isLeft = true;
163                     }
164                     else
165                     {
166                         current = current.Right;
167                         isLeft = false;
168                     }
169                 }
170 
171                 if (parent == null)
172                 {
173                     this.Root = node;
174                 }
175                 else if (isLeft)
176                 {
177                     parent.Left = node;
178                 }
179                 else
180                 {
181                     parent.Right = node;
182                 }
183             }
184 
185             public bool Delete(T item)
186             {
187                 Node<T> parent = null;
188                 var current = this.Root;
189                 var isLeft = false;
190 
191                 this.Find(item, ref parent, ref current, ref isLeft);
192 
193                 if (current == null)
194                 {
195                     return false;
196                 }
197 
198                 if (current.Right == null)
199                 {
200                     if (parent == null)
201                     {
202                         this.Root = current.Left;
203                     }
204                     else if (isLeft)
205                     {
206                         parent.Left = current.Left;
207                     }
208                     else
209                     {
210                         parent.Right = current.Left;
211                     }
212                 }
213                 else if (current.Right != null && current.Right.Left == null)
214                 {
215                     current.Right.Left = current.Left;
216 
217                     if (parent == null)
218                     {
219                         this.Root = current.Right;
220                     }
221                     else if (isLeft)
222                     {
223                         parent.Left = current.Right;
224                     }
225                     else
226                     {
227                         parent.Right = current.Right;
228                     }
229                 }
230                 else
231                 {
232                     Node<T> currentRightSmallestParent = current.Right;
233                     var currentRightSmallest = current.Right.Left;
234 
235                     this.FindSmallest(ref currentRightSmallestParent, ref currentRightSmallest);
236 
237                     currentRightSmallestParent.Left = currentRightSmallest.Right;
238                     currentRightSmallest.Left = current.Left;
239                     currentRightSmallest.Right = current.Right;
240                     if (parent == null)
241                     {
242                         this.Root = currentRightSmallest;
243                     }
244                     else if (isLeft)
245                     {
246                         parent.Left = currentRightSmallest;
247                     }
248                     else
249                     {
250                         parent.Right = currentRightSmallest;
251                     }
252                 }
253 
254                 return true;
255             }
256 
257             private void Find(T item, ref Node<T> parent, ref Node<T> current, ref bool isLeft)
258             {
259                 while (current != null)
260                 {
261                     if (item.CompareTo(current.Value) == 0)
262                     {
263                         break;
264                     }
265 
266                     parent = current;
267 
268                     if (item.CompareTo(current.Value) < 0)
269                     {
270                         current = current.Left;
271                         isLeft = true;
272                     }
273                     else
274                     {
275                         current = current.Right;
276                         isLeft = false;
277                     }
278                 }
279             }
280 
281             private void FindSmallest(ref Node<T> parent, ref Node<T> current)
282             {
283                 while (current != null)
284                 {
285                     if (current.Left == null)
286                     {
287                         break;
288                     }
289 
290                     parent = current;
291                     current = current.Left;
292                 }
293             }
294         }
295     }
296 }

备注

学完这个树的最大收获就是,找到了一种输出树形结构相对高效的方法,比我之前用的高效,这种算法可以用在组织结构图的生成中。

 

posted on 2013-12-24 16:02  幸福框架  阅读(1674)  评论(2编辑  收藏  举报

导航

我要啦免费统计