microsoft-visualbasic-runtime/ComponentModel/Algorithm/BinaryTree/NaiveBinaryTree.vb

406 lines
16 KiB
VB.net
Raw Normal View History

2019-07-29 02:05:05 +08:00
#Region "Microsoft.VisualBasic::1cab0da9ef50f6674ce07f1b33dad9fc, Microsoft.VisualBasic.Core\ComponentModel\Algorithm\BinaryTree\NaiveBinaryTree.vb"
2018-11-08 20:21:25 +08:00
' Author:
'
' asuka (amethyst.asuka@gcmodeller.org)
' xie (genetics@smrucc.org)
' xieguigang (xie.guigang@live.com)
'
' Copyright (c) 2018 GPL3 Licensed
'
'
' GNU GENERAL PUBLIC LICENSE (GPL3)
'
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' /********************************************************************************/
' Summaries:
' Class NaiveBinaryTree
'
' Properties: Length
'
' Constructor: (+2 Overloads) Sub New
'
' Function: add, drawNode, findSuccessor, FindSymbol, insert
' ToString
'
2019-03-07 22:38:12 +08:00
' Sub: Clear, delete, KillTree
2018-11-08 20:21:25 +08:00
'
'
' /********************************************************************************/
2018-10-31 20:40:37 +08:00
#End Region
Imports System.Runtime.CompilerServices
' Software License Agreement (BSD License)
'*
'* Copyright (c) 2003, Herbert M Sauro
'* All rights reserved.
'*
'* Redistribution and use in source and binary forms, with or without
'* modification, are permitted provided that the following conditions are met:
'* * Redistributions of source code must retain the above copyright
'* notice, this list of conditions and the following disclaimer.
'* * Redistributions in binary form must reproduce the above copyright
'* notice, this list of conditions and the following disclaimer in the
'* documentation and/or other materials provided with the distribution.
'* * Neither the name of Herbert M Sauro nor the
'* names of its contributors may be used to endorse or promote products
'* derived from this software without specific prior written permission.
'*
'* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
'* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
'* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
'* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
'* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
'* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
'* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
'* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
'* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
'* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
Namespace ComponentModel.Algorithm.BinaryTree
''' <summary>
''' The Binary tree itself. 朴素二叉树
'''
''' A very basic Binary Search Tree. Not generalized, stores
''' name/value pairs in the tree nodes. name is the node key.
''' The advantage of a binary tree is its fast insert and lookup
''' characteristics. This version does not deal with tree balancing.
''' (二叉搜索树用于建立对repository的索引文件)
''' </summary>
''' <remarks></remarks>
2018-10-31 21:10:03 +08:00
Public Class NaiveBinaryTree(Of K, V) : Inherits TreeBase(Of K, V)
2018-10-31 20:40:37 +08:00
''' <summary>
2018-10-31 21:10:03 +08:00
''' Returns the number of nodes in the tree
2018-10-31 20:40:37 +08:00
''' </summary>
2018-10-31 21:10:03 +08:00
''' <returns>Number of nodes in the tree</returns>
Public ReadOnly Property Length As Integer
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Get
Return stack.Count
End Get
End Property
2018-10-31 20:40:37 +08:00
2018-10-31 21:27:30 +08:00
Public Sub New(key As K, value As V, compares As Comparison(Of K), Optional views As Func(Of K, String) = Nothing)
Call MyBase.New(compares, views)
Call Me.insert(key, value)
End Sub
2018-10-31 21:10:03 +08:00
Public Sub New(compares As Comparison(Of K), Optional views As Func(Of K, String) = Nothing)
MyBase.New(compares, views)
2018-10-31 20:40:37 +08:00
End Sub
2019-02-19 18:25:51 +08:00
''' <summary>
''' Recursive destruction of binary search tree, called by method clear
''' and destroy. Can be used to kill a sub-tree of a larger tree.
''' This is a hanger on from its Delphi origins, it might be dispensable
''' given the garbage collection abilities of .NET
''' </summary>
''' <param name="p"></param>
2018-10-31 21:10:03 +08:00
Private Sub KillTree(ByRef p As BinaryTree(Of K, V))
2018-10-31 20:40:37 +08:00
If p IsNot Nothing Then
KillTree(p.Left)
KillTree(p.Right)
p = Nothing
End If
End Sub
''' <summary>
''' Clear the binary tree.
''' </summary>
2019-02-19 18:25:51 +08:00
Public Overrides Sub Clear()
2018-10-31 21:10:03 +08:00
Call KillTree(root)
Call stack.Clear()
2018-10-31 20:40:37 +08:00
End Sub
''' <summary>
''' Find name in tree. Return a reference to the node
''' if symbol found else return null to indicate failure.
''' </summary>
2018-10-31 21:10:03 +08:00
''' <param name="key">Name of node to locate</param>
2018-10-31 20:40:37 +08:00
''' <returns>Returns null if it fails to find the node, else returns reference to node</returns>
2018-10-31 21:10:03 +08:00
Public Function FindSymbol(key As K, Optional ByRef parent As BinaryTree(Of K, V) = Nothing) As BinaryTree(Of K, V)
Dim np As BinaryTree(Of K, V) = root
2018-10-31 20:40:37 +08:00
Dim cmp As Integer
2018-10-31 21:10:03 +08:00
parent = Nothing
2018-10-31 20:40:37 +08:00
While np IsNot Nothing
2018-10-31 21:10:03 +08:00
cmp = compares(key, np.Key)
2018-10-31 20:40:37 +08:00
If cmp = 0 Then
' found !
Return np
2018-10-31 21:10:03 +08:00
Else
parent = np
2018-10-31 20:40:37 +08:00
End If
If cmp < 0 Then
np = np.Left
Else
np = np.Right
End If
End While
' Return null to indicate failure to find name
Return Nothing
End Function
''' <summary>
''' Recursively locates an empty slot in the binary tree and inserts the node
''' </summary>
''' <param name="node"></param>
''' <param name="tree"></param>
2018-11-01 21:46:16 +08:00
''' <returns>
''' 当处于append模式下append值的时候不会返回节点而是返回nothing
''' </returns>
Private Function add(node As BinaryTree(Of K, V), ByRef tree As BinaryTree(Of K, V), append As Boolean) As BinaryTree(Of K, V)
2018-11-01 21:11:29 +08:00
Do While True
2018-10-31 20:40:37 +08:00
' If we find a node with the same name then it's
' a duplicate and we can't continue
2018-11-01 21:11:29 +08:00
Dim comparison As Integer = compares(node.Key, tree.Key)
If comparison = 0 Then
' Duplicated node was found!
If append Then
' clustering
DirectCast(tree!values, List(Of V)).Add(node.Value)
2018-11-01 21:46:16 +08:00
Return Nothing
2018-11-01 21:11:29 +08:00
Else
' Value replace when not append
tree.Value = node.Value
2018-11-01 21:46:16 +08:00
Return node
2018-10-31 20:40:37 +08:00
End If
2018-11-01 21:11:29 +08:00
ElseIf comparison < 0 Then
If Not tree.Left Is Nothing Then
tree = tree.Left
Else
tree.Left = node
2018-11-01 21:46:16 +08:00
Return node
2018-11-01 21:11:29 +08:00
End If
2018-10-31 20:40:37 +08:00
Else
2018-11-01 21:11:29 +08:00
If Not tree.Right Is Nothing Then
tree = tree.Right
Else
tree.Right = node
2018-11-01 21:46:16 +08:00
Return node
2018-11-01 21:11:29 +08:00
End If
2018-10-31 20:40:37 +08:00
End If
2018-11-01 21:11:29 +08:00
Loop
2018-11-01 21:46:16 +08:00
Throw New NotImplementedException("This exception will never happends!")
End Function
2018-10-31 20:40:37 +08:00
''' <summary>
''' Add a symbol to the tree if it's a new one. Returns reference to the new
''' node if a new node inserted, else returns null to indicate node already present.
''' </summary>
''' <returns> Returns reference to the new node is the node was inserted.
''' If a duplicate node (same name was located then returns null</returns>
2018-11-01 21:11:29 +08:00
Public Function insert(key As K, obj As V, Optional append As Boolean = True) As BinaryTree(Of K, V)
2018-10-31 21:10:03 +08:00
Dim node As New BinaryTree(Of K, V)(key, obj, toString:=views)
2018-10-31 20:40:37 +08:00
Try
2018-10-31 21:10:03 +08:00
If root Is Nothing Then
_root = node
2018-10-31 20:40:37 +08:00
Else
2018-11-01 21:46:16 +08:00
node = add(node, root, append)
2018-10-31 20:40:37 +08:00
End If
2018-11-01 21:46:16 +08:00
If Not node Is Nothing Then
Call stack.Add(node)
End If
2018-10-31 21:10:03 +08:00
2018-10-31 20:40:37 +08:00
Return node
Catch generatedExceptionName As Exception
Dim ex = New Exception(node.ToString, generatedExceptionName)
Return App.LogException(ex)
End Try
End Function
''' <summary>
''' Find the next ordinal node starting at node startNode.
''' Due to the structure of a binary search tree, the
''' successor node is simply the left most node on the right branch.
''' </summary>
''' <param name="startNode">Name key to use for searching</param>
''' <param name="parent">Returns the parent node if search successful</param>
''' <returns>Returns a reference to the node if successful, else null</returns>
2018-10-31 21:10:03 +08:00
Public Function findSuccessor(startNode As BinaryTree(Of K, V), ByRef parent As BinaryTree(Of K, V)) As BinaryTree(Of K, V)
2018-10-31 20:40:37 +08:00
parent = startNode
' Look for the left-most node on the right side
startNode = startNode.Right
While startNode.Left IsNot Nothing
parent = startNode
startNode = startNode.Left
End While
Return startNode
End Function
''' <summary>
''' Delete a given node. This is the more complex method in the binary search
2019-02-19 18:25:51 +08:00
''' class. The method considers three senarios,
'''
''' + 1) the deleted node has no children;
''' + 2) the deleted node as one child;
''' + 3) the deleted node has two children.
'''
''' Case one and two are relatively simple to handle, the only unusual considerations
''' are when the node is the root node. Case ``3)`` is much more complicated. It
''' requires the location of the successor node.
'''
2018-10-31 20:40:37 +08:00
''' The node to be deleted is then replaced by the sucessor node and the
''' successor node itself deleted. Throws an exception if the method fails
''' to locate the node for deletion.
''' </summary>
''' <param name="key">Name key of node to delete</param>
2018-10-31 21:10:03 +08:00
Public Sub delete(key As K)
Dim parent As BinaryTree(Of K, V) = Nothing
2018-10-31 20:40:37 +08:00
' First find the node to delete and its parent
2018-10-31 21:10:03 +08:00
Dim nodeToDelete As BinaryTree(Of K, V) = FindSymbol(key, parent)
2018-10-31 20:40:37 +08:00
If nodeToDelete Is Nothing Then
Throw New Exception("Unable to delete node: " & key.ToString())
End If
' can't find node, then say so
' Three cases to consider, leaf, one child, two children
' If it is a simple leaf then just null what the parent is pointing to
If (nodeToDelete.Left Is Nothing) AndAlso (nodeToDelete.Right Is Nothing) Then
If parent Is Nothing Then
2018-10-31 21:10:03 +08:00
_root = Nothing
2018-10-31 20:40:37 +08:00
Return
End If
' find out whether left or right is associated
' with the parent and null as appropriate
If parent.Left Is nodeToDelete Then
parent.Left = Nothing
Else
parent.Right = Nothing
End If
2018-10-31 21:10:03 +08:00
Call stack.Remove(nodeToDelete)
2018-10-31 20:40:37 +08:00
Return
End If
' One of the children is null, in this case
' delete the node and move child up
If nodeToDelete.Left Is Nothing Then
' Special case if we're at the root
If parent Is Nothing Then
2018-10-31 21:10:03 +08:00
_root = nodeToDelete.Right
2018-10-31 20:40:37 +08:00
Return
End If
' Identify the child and point the parent at the child
If parent.Left Is nodeToDelete Then
parent.Right = nodeToDelete.Right
Else
parent.Left = nodeToDelete.Right
End If
nodeToDelete = Nothing
' Clean up the deleted node
2018-10-31 21:10:03 +08:00
Call stack.Remove(nodeToDelete)
2018-10-31 20:40:37 +08:00
Return
End If
' One of the children is null, in this case
' delete the node and move child up
If nodeToDelete.Right Is Nothing Then
' Special case if we're at the root
If parent Is Nothing Then
2018-10-31 21:10:03 +08:00
_root = nodeToDelete.Left
2018-10-31 20:40:37 +08:00
Return
End If
' Identify the child and point the parent at the child
If parent.Left Is nodeToDelete Then
parent.Left = nodeToDelete.Left
Else
parent.Right = nodeToDelete.Left
End If
nodeToDelete = Nothing
' Clean up the deleted node
2018-10-31 21:10:03 +08:00
Call stack.Remove(nodeToDelete)
2018-10-31 20:40:37 +08:00
Return
End If
' Both children have nodes, therefore find the successor,
' replace deleted node with successor and remove successor
' The parent argument becomes the parent of the successor
2018-10-31 21:10:03 +08:00
Dim successor As BinaryTree(Of K, V) = findSuccessor(nodeToDelete, parent)
2018-10-31 20:40:37 +08:00
' Make a copy of the successor node
2018-10-31 21:10:03 +08:00
Dim tmp As New BinaryTree(Of K, V)(successor.Key, successor.Value)
2018-10-31 20:40:37 +08:00
' Find out which side the successor parent is pointing to the
' successor and remove the successor
If parent.Left Is successor Then
parent.Left = Nothing
Else
parent.Right = Nothing
End If
' Copy over the successor values to the deleted node position
2018-10-31 21:10:03 +08:00
Call nodeToDelete.Copy(tmp)
Call stack.Remove(nodeToDelete)
2018-10-31 20:40:37 +08:00
End Sub
2019-02-19 18:25:51 +08:00
''' <summary>
''' Simple 'drawing' routines
''' </summary>
''' <param name="node"></param>
''' <returns></returns>
2018-10-31 21:10:03 +08:00
Private Function drawNode(node As BinaryTree(Of K, V)) As String
2018-10-31 20:40:37 +08:00
If node Is Nothing Then
Return "empty"
End If
If (node.Left Is Nothing) AndAlso (node.Right Is Nothing) Then
2018-10-31 21:10:03 +08:00
Return views(node.Key)
2018-10-31 20:40:37 +08:00
End If
If (node.Left IsNot Nothing) AndAlso (node.Right Is Nothing) Then
2018-10-31 21:10:03 +08:00
Return views(node.Key) & "(" & drawNode(node.Left) & ", _)"
2018-10-31 20:40:37 +08:00
End If
If (node.Right IsNot Nothing) AndAlso (node.Left Is Nothing) Then
2018-10-31 21:10:03 +08:00
Return views(node.Key) & "(_, " & drawNode(node.Right) & ")"
2018-10-31 20:40:37 +08:00
End If
2018-10-31 21:10:03 +08:00
Return views(node.Key) & "(" & drawNode(node.Left) & ", " & drawNode(node.Right) & ")"
2018-10-31 20:40:37 +08:00
End Function
''' <summary>
''' Return the tree depicted as a simple string, useful for debugging, eg
''' 50(40(30(20, 35), 45(44, 46)), 60)
''' </summary>
''' <returns>Returns the tree</returns>
Public Overrides Function ToString() As String
2018-10-31 21:10:03 +08:00
Return drawNode(root)
2018-10-31 20:40:37 +08:00
End Function
End Class
2018-11-08 20:21:25 +08:00
End Namespace