1 #Region "Microsoft.VisualBasic::6abf7be1188e9cce30e4aa8d6f07be60, Microsoft.VisualBasic.Core\ComponentModel\Algorithm\BinaryTree\NaiveBinaryTree.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Class NaiveBinaryTree
35     
36     '         Properties: Length
37     
38     '         Constructor: (+2 OverloadsSub New
39     
40     '         Function: add, drawNode, findSuccessor, FindSymbol, insert
41     '                   ToString
42     
43     '         Sub: clear, delete, KillTree
44     
45     
46     ' /********************************************************************************/
47
48 #End Region
49
50 Imports System.Runtime.CompilerServices
51
52 ' Software License Agreement (BSD License)
53 '* 
54 '* Copyright (c) 2003, Herbert M Sauro
55 '* All rights reserved.
56 '*
57 '* Redistribution and use in source and binary forms, with or without
58 '* modification, are permitted provided that the following conditions are met:
59 '*     * Redistributions of source code must retain the above copyright
60 '*       notice, this list of conditions and the following disclaimer.
61 '*     * Redistributions in binary form must reproduce the above copyright
62 '*       notice, this list of conditions and the following disclaimer in the
63 '*       documentation and/or other materials provided with the distribution.
64 '*     * Neither the name of Herbert M Sauro nor the
65 '*       names of its contributors may be used to endorse or promote products
66 '*       derived from this software without specific prior written permission.
67 '*
68 '* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
69 '* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
70 '* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
71 '* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
72 '* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
73 '* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
74 '* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
75 '* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
76 '* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
77 '* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
78 '
79
80 Namespace ComponentModel.Algorithm.BinaryTree
81
82     ''' <summary>
83     ''' The Binary tree itself. 朴素二叉树
84     ''' 
85     ''' A very basic Binary Search Tree. Not generalized, stores
86     ''' name/value pairs in the tree nodes. name is the node key.
87     ''' The advantage of a binary tree is its fast insert and lookup
88     ''' characteristics. This version does not deal with tree balancing.
89     ''' (二叉搜索树,用于建立对repository的索引文件)
90     ''' </summary>
91     ''' <remarks></remarks>
92     Public Class NaiveBinaryTree(Of K, V) : Inherits TreeBase(Of K, V)
93
94         ''' <summary>
95         ''' Returns the number of nodes in the tree
96         ''' </summary>
97         ''' <returns>Number of nodes in the tree</returns>
98         Public ReadOnly Property Length As Integer
99             <MethodImpl(MethodImplOptions.AggressiveInlining)>
100             Get
101                 Return stack.Count
102             End Get
103         End Property
104
105         Public Sub New(key As K, value As V, compares As Comparison(Of K), Optional views As Func(Of K, String) = Nothing)
106             Call MyBase.New(compares, views)
107             Call Me.insert(key, value)
108         End Sub
109
110         Public Sub New(compares As Comparison(Of K), Optional views As Func(Of K, String) = Nothing)
111             MyBase.New(compares, views)
112         End Sub
113
114         ' Recursive destruction of binary search tree, called by method clear
115         ' and destroy. Can be used to kill a sub-tree of a larger tree.
116         ' This is a hanger on from its Delphi origins, it might be dispensable
117         ' given the garbage collection abilities of .NET
118         Private Sub KillTree(ByRef p As BinaryTree(Of K, V))
119             If p IsNot Nothing Then
120                 KillTree(p.Left)
121                 KillTree(p.Right)
122                 p = Nothing
123             End If
124         End Sub
125
126         ''' <summary>
127         ''' Clear the binary tree.
128         ''' </summary>
129         Public Sub clear()
130             Call KillTree(root)
131             Call stack.Clear()
132         End Sub
133
134         ''' <summary>
135         ''' Find name in tree. Return a reference to the node
136         ''' if symbol found else return null to indicate failure.
137         ''' </summary>
138         ''' <param name="key">Name of node to locate</param>
139         ''' <returns>Returns null if it fails to find the node, else returns reference to node</returns>
140         Public Function FindSymbol(key As K, Optional ByRef parent As BinaryTree(Of K, V) = NothingAs BinaryTree(Of K, V)
141             Dim np As BinaryTree(Of K, V) = root
142             Dim cmp As Integer
143
144             parent = Nothing
145
146             While np IsNot Nothing
147                 cmp = compares(key, np.Key)
148
149                 If cmp = 0 Then
150                     ' found !
151                     Return np
152                 Else
153                     parent = np
154                 End If
155
156                 If cmp < 0 Then
157                     np = np.Left
158                 Else
159                     np = np.Right
160                 End If
161             End While
162
163             Return null to indicate failure to find name
164             Return Nothing
165         End Function
166
167         ''' <summary>
168         ''' Recursively locates an empty slot in the binary tree and inserts the node
169         ''' </summary>
170         ''' <param name="node"></param>
171         ''' <param name="tree"></param>
172         ''' <returns>
173         ''' 当处于append模式下,append值的时候不会返回节点,而是返回nothing
174         ''' </returns>
175         Private Function add(node As BinaryTree(Of K, V), ByRef tree As BinaryTree(Of K, V), append As BooleanAs BinaryTree(Of K, V)
176             Do While True
177                 If we find a node with the same name then it's 
178                 ' a duplicate and we can't continue
179                 Dim comparison As Integer = compares(node.Key, tree.Key)
180
181                 If comparison = 0 Then
182                     ' Duplicated node was found!
183                     If append Then
184                         ' clustering
185                         DirectCast(tree!values, List(Of V)).Add(node.Value)
186                         Return Nothing
187                     Else
188                         ' Value replace when not append
189                         tree.Value = node.Value
190                         Return node
191                     End If
192                 ElseIf comparison < 0 Then
193                     If Not tree.Left Is Nothing Then
194                         tree = tree.Left
195                     Else
196                         tree.Left = node
197                         Return node
198                     End If
199                 Else
200                     If Not tree.Right Is Nothing Then
201                         tree = tree.Right
202                     Else
203                         tree.Right = node
204                         Return node
205                     End If
206                 End If
207             Loop
208
209             Throw New NotImplementedException("This exception will never happends!")
210         End Function
211
212         ''' <summary>
213         ''' Add a symbol to the tree if it's a new one. Returns reference to the new
214         ''' node if a new node inserted, else returns null to indicate node already present.
215         ''' </summary>
216         ''' <returns> Returns reference to the new node is the node was inserted.
217         ''' If a duplicate node (same name was located then returns null</returns>
218         Public Function insert(key As K, obj As V, Optional append As Boolean = TrueAs BinaryTree(Of K, V)
219             Dim node As New BinaryTree(Of K, V)(key, obj, toString:=views)
220
221             Try
222                 If root Is Nothing Then
223                     _root = node
224                 Else
225                     node = add(node, root, append)
226                 End If
227
228                 If Not node Is Nothing Then
229                     Call stack.Add(node)
230                 End If
231
232                 Return node
233             Catch generatedExceptionName As Exception
234                 Dim ex = New Exception(node.ToString, generatedExceptionName)
235                 Return App.LogException(ex)
236             End Try
237         End Function
238
239         ''' <summary>
240         ''' Find the next ordinal node starting at node startNode.
241         ''' Due to the structure of a binary search tree, the
242         ''' successor node is simply the left most node on the right branch.
243         ''' </summary>
244         ''' <param name="startNode">Name key to use for searching</param>
245         ''' <param name="parent">Returns the parent node if search successful</param>
246         ''' <returns>Returns a reference to the node if successful, else null</returns>
247         Public Function findSuccessor(startNode As BinaryTree(Of K, V), ByRef parent As BinaryTree(Of K, V)) As BinaryTree(Of K, V)
248             parent = startNode
249             ' Look for the left-most node on the right side
250             startNode = startNode.Right
251             While startNode.Left IsNot Nothing
252                 parent = startNode
253                 startNode = startNode.Left
254             End While
255             Return startNode
256         End Function
257
258         ''' <summary>
259         ''' Delete a given node. This is the more complex method in the binary search
260         ''' class. The method considers three senarios, 1) the deleted node has no
261         ''' children; 2) the deleted node as one child; 3) the deleted node has two
262         ''' children. Case one and two are relatively simple to handle, the only
263         ''' unusual considerations are when the node is the root node. Case 3) is
264         ''' much more complicated. It requires the location of the successor node.
265         ''' The node to be deleted is then replaced by the sucessor node and the
266         ''' successor node itself deleted. Throws an exception if the method fails
267         ''' to locate the node for deletion.
268         ''' </summary>
269         ''' <param name="key">Name key of node to delete</param>
270         Public Sub delete(key As K)
271             Dim parent As BinaryTree(Of K, V) = Nothing
272             ' First find the node to delete and its parent
273             Dim nodeToDelete As BinaryTree(Of K, V) = FindSymbol(key, parent)
274
275             If nodeToDelete Is Nothing Then
276                 Throw New Exception("Unable to delete node: " & key.ToString())
277             End If
278             ' can't find node, then say so 
279             ' Three cases to consider, leaf, one child, two children
280
281             If it is a simple leaf then just null what the parent is pointing to
282             If (nodeToDelete.Left Is NothingAndAlso (nodeToDelete.Right Is NothingThen
283                 If parent Is Nothing Then
284                     _root = Nothing
285                     Return
286                 End If
287
288                 ' find out whether left or right is associated 
289                 ' with the parent and null as appropriate
290                 If parent.Left Is nodeToDelete Then
291                     parent.Left = Nothing
292                 Else
293                     parent.Right = Nothing
294                 End If
295
296                 Call stack.Remove(nodeToDelete)
297
298                 Return
299             End If
300
301             One of the children is null, in this case
302             ' delete the node and move child up
303             If nodeToDelete.Left Is Nothing Then
304                 ' Special case if we're at the root
305                 If parent Is Nothing Then
306                     _root = nodeToDelete.Right
307                     Return
308                 End If
309
310                 ' Identify the child and point the parent at the child
311                 If parent.Left Is nodeToDelete Then
312                     parent.Right = nodeToDelete.Right
313                 Else
314                     parent.Left = nodeToDelete.Right
315                 End If
316                 nodeToDelete = Nothing
317                 ' Clean up the deleted node
318                 Call stack.Remove(nodeToDelete)
319
320                 Return
321             End If
322
323             One of the children is null, in this case
324             ' delete the node and move child up
325             If nodeToDelete.Right Is Nothing Then
326                 ' Special case if we're at the root
327                 If parent Is Nothing Then
328                     _root = nodeToDelete.Left
329                     Return
330                 End If
331
332                 ' Identify the child and point the parent at the child
333                 If parent.Left Is nodeToDelete Then
334                     parent.Left = nodeToDelete.Left
335                 Else
336                     parent.Right = nodeToDelete.Left
337                 End If
338                 nodeToDelete = Nothing
339                 ' Clean up the deleted node
340                 Call stack.Remove(nodeToDelete)
341
342                 Return
343             End If
344
345             ' Both children have nodes, therefore find the successor, 
346             ' replace deleted node with successor and remove successor
347             ' The parent argument becomes the parent of the successor
348             Dim successor As BinaryTree(Of K, V) = findSuccessor(nodeToDelete, parent)
349             ' Make a copy of the successor node
350             Dim tmp As New BinaryTree(Of K, V)(successor.Key, successor.Value)
351             ' Find out which side the successor parent is pointing to the
352             ' successor and remove the successor
353             If parent.Left Is successor Then
354                 parent.Left = Nothing
355             Else
356                 parent.Right = Nothing
357             End If
358
359             ' Copy over the successor values to the deleted node position
360             Call nodeToDelete.Copy(tmp)
361             Call stack.Remove(nodeToDelete)
362         End Sub
363
364         ' Simple 'drawing' routines
365         Private Function drawNode(node As BinaryTree(Of K, V)) As String
366             If node Is Nothing Then
367                 Return "empty"
368             End If
369
370             If (node.Left Is NothingAndAlso (node.Right Is NothingThen
371                 Return views(node.Key)
372             End If
373             If (node.Left IsNot NothingAndAlso (node.Right Is NothingThen
374                 Return views(node.Key) & "(" & drawNode(node.Left) & ", _)"
375             End If
376
377             If (node.Right IsNot NothingAndAlso (node.Left Is NothingThen
378                 Return views(node.Key) & "(_, " & drawNode(node.Right) & ")"
379             End If
380
381             Return views(node.Key) & "(" & drawNode(node.Left) & ", " & drawNode(node.Right) & ")"
382         End Function
383
384         ''' <summary>
385         ''' Return the tree depicted as a simple string, useful for debugging, eg
386         ''' 50(40(30(20, 35), 45(44, 46)), 60)
387         ''' </summary>
388         ''' <returns>Returns the tree</returns>
389         Public Overrides Function ToString() As String
390             Return drawNode(root)
391         End Function
392     End Class
393 End Namespace