485 lines
18 KiB
VB.net
485 lines
18 KiB
VB.net
#Region "Microsoft.VisualBasic::d486254ea1db0f16c2ef966337dc5cf3, Microsoft.VisualBasic.Core\ComponentModel\DataStructures\Tree\BinaryTree\BinaryTree.vb"
|
||
|
||
' 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 BinaryTree
|
||
'
|
||
' Properties: Length, Root
|
||
'
|
||
' Constructor: (+3 Overloads) Sub New
|
||
'
|
||
' Function: drawNode, findParent, findSuccessor, FindSymbol, GetAllNodes
|
||
' (+2 Overloads) insert, ToString
|
||
'
|
||
' Sub: add, (+2 Overloads) Add, clear, delete, KillTree
|
||
'
|
||
'
|
||
' /********************************************************************************/
|
||
|
||
#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>
|
||
Public Class NaiveBinaryTree(Of K, V)
|
||
|
||
''' <summary>
|
||
''' The root of the tree.
|
||
''' </summary>
|
||
''' <returns></returns>
|
||
Public ReadOnly Property Root As TreeNode(Of T)
|
||
|
||
''' <summary>
|
||
''' Points to the root of the tree
|
||
''' </summary>
|
||
''' <remarks></remarks>
|
||
Dim _counts As Integer = 0
|
||
|
||
Public Sub New()
|
||
End Sub
|
||
|
||
Sub New(root As TreeNode(Of T))
|
||
Me.Root = root
|
||
Me._counts = root.Count
|
||
End Sub
|
||
|
||
''' <summary>
|
||
''' 初始化有一个根节点
|
||
''' </summary>
|
||
''' <param name="ROOT"></param>
|
||
''' <param name="obj"></param>
|
||
Sub New(ROOT As String, obj As T)
|
||
Call Me.New
|
||
Call Me.insert(ROOT, obj)
|
||
End Sub
|
||
|
||
' 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
|
||
Private Sub KillTree(ByRef p As TreeNode(Of T))
|
||
If p IsNot Nothing Then
|
||
KillTree(p.Left)
|
||
KillTree(p.Right)
|
||
p = Nothing
|
||
End If
|
||
End Sub
|
||
|
||
Public Function GetAllNodes() As TreeNode(Of T)()
|
||
Dim list = Root.AllChilds
|
||
Call list.Insert(Scan0, Root)
|
||
Return list.ToArray
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' Clear the binary tree.
|
||
''' </summary>
|
||
Public Sub clear()
|
||
Call KillTree(Root)
|
||
_counts = 0
|
||
End Sub
|
||
|
||
''' <summary>
|
||
''' Manual add tree node
|
||
''' </summary>
|
||
''' <param name="parent"></param>
|
||
''' <param name="node"></param>
|
||
''' <param name="left"></param>
|
||
Public Sub Add(parent$, node As TreeNode(Of T), left As Boolean)
|
||
Dim parentNode = FindSymbol(parent)
|
||
If left Then
|
||
parentNode.Left = node
|
||
Else
|
||
parentNode.Right = node
|
||
End If
|
||
End Sub
|
||
|
||
''' <summary>
|
||
''' Manual add tree node
|
||
''' </summary>
|
||
''' <param name="parent"></param>
|
||
''' <param name="node"></param>
|
||
Public Sub Add(parent As String, node As TreeNode(Of T))
|
||
Dim parentNode = FindSymbol(parent)
|
||
parentNode += node
|
||
End Sub
|
||
|
||
''' <summary>
|
||
''' Returns the number of nodes in the tree
|
||
''' </summary>
|
||
''' <returns>Number of nodes in the tree</returns>
|
||
Public ReadOnly Property Length As Integer
|
||
<MethodImpl(MethodImplOptions.AggressiveInlining)>
|
||
Get
|
||
Return _counts
|
||
End Get
|
||
End Property
|
||
|
||
''' <summary>
|
||
''' Find name in tree. Return a reference to the node
|
||
''' if symbol found else return null to indicate failure.
|
||
''' </summary>
|
||
''' <param name="name">Name of node to locate</param>
|
||
''' <returns>Returns null if it fails to find the node, else returns reference to node</returns>
|
||
Public Function FindSymbol(Name As String) As TreeNode(Of T)
|
||
Dim np As TreeNode(Of T) = Root
|
||
Dim cmp As Integer
|
||
|
||
While np IsNot Nothing
|
||
cmp = NameCompare(Name, np.Name)
|
||
|
||
If cmp = 0 Then
|
||
' found !
|
||
Return np
|
||
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>
|
||
''' <param name="[overrides]">
|
||
''' 0不复写,函数自动处理
|
||
''' <0 LEFT
|
||
''' >0 RIGHT
|
||
''' </param>
|
||
Private Sub add(node As TreeNode(Of T), ByRef tree As TreeNode(Of T), [overrides] As Integer)
|
||
If tree Is Nothing Then
|
||
tree = node
|
||
Else
|
||
' If we find a node with the same name then it's
|
||
' a duplicate and we can't continue
|
||
Dim comparison As Integer
|
||
|
||
If [overrides] = 0 Then
|
||
comparison = NameCompare(node.Name, tree.Name)
|
||
|
||
If comparison = 0 Then
|
||
Throw New Exception("Duplicated node was found!")
|
||
End If
|
||
Else
|
||
comparison = [overrides]
|
||
End If
|
||
|
||
' 2018-1-11
|
||
' overrides 应该一直被传递下去,而不是使用comparison结果,否则会一直被错误的overrides下去的
|
||
' 导致构建出来的树不平衡
|
||
If comparison < 0 Then
|
||
Add(node, tree.Left, [overrides]:=[overrides])
|
||
tree.Left.Parent = tree
|
||
Else
|
||
Add(node, tree.Right, [overrides]:=[overrides])
|
||
tree.Right.Parent = tree
|
||
End If
|
||
End If
|
||
End Sub
|
||
|
||
''' <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>
|
||
''' <param name="name">Name of node to add to tree</param>
|
||
''' <param name="d">Value of node</param>
|
||
''' <returns> Returns reference to the new node is the node was inserted.
|
||
''' If a duplicate node (same name was located then returns null</returns>
|
||
Public Function insert(name As String, d As T, left As Boolean) As TreeNode(Of T)
|
||
Dim node As New TreeNode(Of T)(name, d)
|
||
|
||
Try
|
||
If Root Is Nothing Then
|
||
_Root = node
|
||
Else
|
||
Add(node, Root, If(left, -1, 1))
|
||
End If
|
||
_counts += 1
|
||
Return node
|
||
Catch generatedExceptionName As Exception
|
||
Dim ex = New Exception(node.ToString, generatedExceptionName)
|
||
Return App.LogException(ex)
|
||
End Try
|
||
End Function
|
||
|
||
''' <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>
|
||
''' <param name="name">Name of node to add to tree</param>
|
||
''' <param name="d">Value of node</param>
|
||
''' <returns> Returns reference to the new node is the node was inserted.
|
||
''' If a duplicate node (same name was located then returns null</returns>
|
||
Public Function insert(name As String, d As T) As TreeNode(Of T)
|
||
Dim node As New TreeNode(Of T)(name, d)
|
||
Try
|
||
If Root Is Nothing Then
|
||
_Root = node
|
||
Else
|
||
Add(node, Root, 0)
|
||
End If
|
||
_counts += 1
|
||
Return node
|
||
Catch generatedExceptionName As Exception
|
||
Dim ex = New Exception(node.ToString, generatedExceptionName)
|
||
Return App.LogException(ex)
|
||
End Try
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' Searches for a node with name key, name. If found it returns a reference
|
||
''' to the node and to the nodes parent. Else returns null.
|
||
''' </summary>
|
||
''' <param name="name"></param>
|
||
''' <param name="parent"></param>
|
||
''' <returns></returns>
|
||
Private Function findParent(name As String, ByRef parent As TreeNode(Of T)) As TreeNode(Of T)
|
||
Dim np As TreeNode(Of T) = Root
|
||
parent = Nothing
|
||
Dim cmp As Integer
|
||
While np IsNot Nothing
|
||
cmp = NameCompare(name, np.Name)
|
||
If cmp = 0 Then
|
||
' found !
|
||
Return np
|
||
End If
|
||
|
||
If cmp < 0 Then
|
||
parent = np
|
||
np = np.Left
|
||
Else
|
||
parent = np
|
||
np = np.Right
|
||
End If
|
||
End While
|
||
Return Nothing
|
||
' Return null to indicate failure to find name
|
||
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>
|
||
Public Function findSuccessor(startNode As TreeNode(Of T), ByRef parent As TreeNode(Of T)) As TreeNode(Of T)
|
||
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
|
||
''' 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.
|
||
''' 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>
|
||
Public Sub delete(key As String)
|
||
Dim parent As TreeNode(Of T) = Nothing
|
||
' First find the node to delete and its parent
|
||
Dim nodeToDelete As TreeNode(Of T) = findParent(key, parent)
|
||
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
|
||
_Root = Nothing
|
||
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
|
||
_counts -= 1
|
||
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
|
||
_Root = nodeToDelete.Right
|
||
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
|
||
_counts -= 1
|
||
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
|
||
_Root = nodeToDelete.Left
|
||
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
|
||
_counts -= 1
|
||
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
|
||
Dim successor As TreeNode(Of T) = findSuccessor(nodeToDelete, parent)
|
||
' Make a copy of the successor node
|
||
Dim tmp As New TreeNode(Of T)(successor.Name, successor.Value)
|
||
' 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
|
||
nodeToDelete.Name = tmp.Name
|
||
nodeToDelete.Value = tmp.Value
|
||
_counts -= 1
|
||
End Sub
|
||
|
||
' Simple 'drawing' routines
|
||
Private Function drawNode(node As TreeNode(Of T)) As String
|
||
If node Is Nothing Then
|
||
Return "empty"
|
||
End If
|
||
|
||
If (node.Left Is Nothing) AndAlso (node.Right Is Nothing) Then
|
||
Return node.Name
|
||
End If
|
||
If (node.Left IsNot Nothing) AndAlso (node.Right Is Nothing) Then
|
||
Return node.Name & "(" & drawNode(node.Left) & ", _)"
|
||
End If
|
||
|
||
If (node.Right IsNot Nothing) AndAlso (node.Left Is Nothing) Then
|
||
Return node.Name & "(_, " & drawNode(node.Right) & ")"
|
||
End If
|
||
|
||
Return node.Name & "(" & drawNode(node.Left) & ", " & drawNode(node.Right) & ")"
|
||
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
|
||
Return drawNode(Root)
|
||
End Function
|
||
End Class
|
||
End Namespace |