天天看點

vba 實作紅黑樹符号表RedNodeRedBlackTreeTable

紅黑樹

  • RedNode
  • RedBlackTreeTable

RedNode

Enum NodeColor
    ' 紅樹
    RED = 0
    ' 黑樹
    BLACK = 1
End Enum


' 二叉樹左樹
Private left As RedNode

' 二叉樹右數
Private right As RedNode

Private key As Variant

Private value As Variant

Private treeSize As Long

Private color As NodeColor

Property Let letColor(ByVal b As NodeColor)
    color = b
End Property

Property Let letLeft(ByRef l As RedNode)
    Set left = l
End Property

Property Let letRight(ByRef r As RedNode)
    Set right = r
End Property

Property Let letKey(ByRef k As Variant)
    assign key, k
End Property

Property Let letValue(ByRef v As Variant)
    assign value, v
End Property

Property Let letTreeSize(i As Long)
    treeSize = i
End Property


Property Get getColor() As Integer
    getColor = color
End Property

Property Get getLeft() As RedNode
    Set getLeft = left
End Property

Property Get getRight() As RedNode
    Set getRight = right
End Property

Property Get getKey() As Variant
    assign getKey, key
End Property

Property Get getValue() As Variant
    assign getValue, value
End Property

Property Get getTreeSize() As Long
    getTreeSize = treeSize
End Property



Private Sub assign(ByRef x, ByVal y)

    If IsObject(y) Then
        Set x = y
    Else
        x = y
    End If

End Sub




           

RedBlackTreeTable

' 使用紅黑樹完成的符号表


Private root As RedNode

Private n As Long


' 暫時省略輔助的增加和縮減,如有需要,參照以前的部落格
' 基本的符号表api


Public Sub putValue(key As Variant, val As Variant)
    Set root = doPut(root, key, val)
    root.letColor = BLACK
End Sub


' 數量
Function size() As Long
    size = doSize(root)
End Function

Private Function doSize(ByRef x As RedNode) As Long
    If x Is Nothing Then doSize = 0: Exit Function
    
    doSize = x.getTreeSize
    
End Function

Private Function doPut(x As RedNode, key As Variant, val As Variant) As RedNode
    Dim xKey As Variant
    
    ' 如果節點為空
    If x Is Nothing Then
        Dim newNode As New RedNode
        newNode.letKey = key
        newNode.letValue = val
        newNode.letColor = RED
        newNode.letTreeSize = 1
        Set doPut = newNode
        Exit Function
    End If
    
    xKey = x.getKey
    
    If key > xKey Then
        x.letRight = doPut(x.getRight, key, val)
    ElseIf key < xKey Then
        x.letLeft = doPut(x.getLeft, key, val)
    Else
        x.letValue = val
    End If
    
    '1. 如果右邊是紅樹,且左邊不是紅樹,就左旋,類似于插入3-中間值的第一次處理,交由步驟2處理,
    If isRed(x.getRight) Then
        If Not isRed(x.getLeft) Then Set x = rotateLeft(x)
    End If
    
    '2. 如果左邊是紅樹,左邊的左邊還是紅樹,就對左邊的樹進行右旋,類似于插入3-最小的值,由步驟3将節點變為黑樹
    If isRed(x.getLeft) Then
        If isRed(x.getLeft.getLeft) Then Set x = rotateRight(x)
    End If
    
    ' 如果左邊也是紅樹,右邊也是紅樹,将兩個節點變為黑樹,類似于插入最大值,最簡單的模型
    If isRed(x.getRight) Then
        If isRed(x.getLeft) Then flipColors x
    End If
    
    x.letTreeSize = 1 + doSize(x.getLeft) + doSize(x.getRight)
    Set doPut = x
End Function


' 擷取鍵的值,如果鍵為空,就傳回空
Function getValue(key As Variant) As Variant
    Dim node As RedNode
    
    
    Set node = doGet(key, root)
    If node Is Nothing Then getValue = Null: Exit Function
     getValue = node.getValue
End Function

Private Function doGet(key As Variant, x As RedNode) As RedNode
    Dim xKey As Variant
    
    If x Is Nothing Then Set doGet = Nothing: Exit Function
    xKey = x.getKey
    
    If key < xKey Then
        Set doGet = doGet(key, x.getLeft)
    ElseIf key > xKey Then
        Set doGet = doGet(key, x.getRight)
    Else
        Set doGet = x
    End If
End Function


' 删除最小的鍵
Sub deleteMin()
    Set root = doDeleteMin(root)
End Sub


Function doDeleteMin(x As RedNode) As RedNode
    Dim lNode As RedNode
    
    Set lNode = x.getLeft
    
    ' 如果左邊為空,說明已經到了最小值,傳回右邊的節點,即使是空
    If lNode Is Nothing Then
        Set doDeleteMin = x.getRight
    Else
        ' 如果左邊不為空,則說明沒有到最小的節點,繼續查找,将傳回的節點挂入左邊,如果最小節點右邊沒有節點,将傳回nothing,也删除了最小節點,如果最小節點右邊有節點,則傳回的右邊節點,挂入目前節點的左節點
        x.letLeft = doDeleteMin(x.getLeft)
        ' 删除一個,數量遞歸減去一個
        x.letThreeSize = x.getThreeSize - 1
        Set doDeleteMin = x
    End If

End Function

' 删除最大的鍵
Sub deleteMax()
    Set root = doDeleteMax(root)
End Sub

Function doDeleteMax(x As RedNode) As RedNode
    Dim rNode As RedNode
    
    
    Set rNode = x.getRight
    
    If rNode Is Nothing Then
        Set doDeleteMax = x.getLeft
    Else
        x.letRight = doDeleteMax(x.getRight)
        x.letThreeSize = x.getThreeSize - 1
        Set doDeleteMax = x
    End If
End Function

' 從表中删除主鍵
Sub delete(key As Variant)

    
End Sub


Function doDelete(x As RedNode, key As Variant) As RedNode
    
    
End Function


' 主鍵是否存在于符号表中
Function contains(key As Variant) As Boolean

End Function

' 是否為空
Function isEmpty() As Boolean
    isEmpty = n = 0
End Function

' 最小的主鍵
Function min() As Variant
    Dim minNode As RedNode
    
    Set minNode = doMin(root)
    
    assign min, minNode.getValue
End Function

Function doMin(x As RedNode) As RedNode
    If x.getLeft Is Nothing Then
        Set doMin = x
    Else
        Set doMin = doMin(x.getLeft)
    End If
End Function

' 最大的主鍵
Function max() As Variant
    Dim maxNode As RedNode
    
    Set maxNode = doMax(root)
    
    assign max, maxNode.getValue
End Function

Function doMax(x As RedNode) As RedNode
    If x.getRight Is Nothing Then
        Set doMax = x
    Else
        Set doMax = doMax(x.getRight)
    End If
End Function

' 小于等于key的最大鍵
Function floor(key As Variant) As Variant
    
    Dim n As RedNode
    
    Set n = doFloor(root, key)
    
    If n Is Nothing Then assign floor, Null: Exit Function
    
    assign floor, n.getValue
    

End Function

' 抽象出通用操作
' 需要取出小于等于key的最大值
' 如果相等,傳回節點,這沒什麼說的,後面不讨論相等情況
' 如果值從左邊傳回,結果為null,那麼就是沒有找到,因為需要找到比key小的 key,排除相等,必須找到一個比key小的key,然後取右邊節點。左邊傳回null,隻能說明整顆樹或者子樹中都沒有找到
' 如果右邊傳回null,說明就是目前節點,直接傳回目前節點
Function doFloor(x As RedNode, key As Variant) As RedNode
    Dim xKey As Variant
    
    Dim temp As RedNode
    
    If x Is Nothing Then
        Set doFloor = Nothing
    Else
        assign xKey, x.getKey
        
        If key = xKey Then Set doFloor = x: Exit Function
        
        If key < xKey Then Set doFloor = doFloor(x.getLeft, key): Exit Function
        
        If key > xKey Then
            Set temp = doFloor(x.getRight, key)
            If temp Is Nothing Then
                Set doFloor = x
            Else
                Set doFloor = temp
            End If
        End If
        
    End If
    
End Function

' 大于等于key的最小鍵
Function ceiling(key As Variant) As Variant
    Dim n As RedNode
    
    Set n = doCeiling(root, key)
    If n Is Nothing Then assign ceiling, Null: Exit Function
    
    assign ceiling, n.getValue
    
End Function


' 抽象出通用操作
' 需要取出大于等于key的最小值
' 如果相等,傳回節點,這沒什麼說的,後面不讨論相等情況
' 如果值從右邊傳回,結果為null,那麼就是沒有找到,因為需要找到比key大的 key,排除相等,必須找到一個比key大的key,然後取左邊節點。右邊傳回null,隻能說明整顆樹或者子樹中都沒有找到
' 如果左邊傳回null,說明就是目前節點,直接傳回目前節點
Function doCeiling(x As RedNode, key As Variant) As RedNode
    
    Dim xKey As Variant
    
    Dim temp As RedNode
    
    If x Is Nothing Then
        Set doCeiling = Nothing
    Else
        assign xKey, x.getKey
        If key = xKey Then Set doCeiling = x: Exit Function
        
        If key > xKey Then
            Set doCeiling = doCeiling(x.getRight, key): Exit Function
        End If
        
        If key < xKey Then
            Set temp = doCeiling(x.getLeft, key)
            
            If temp Is Nothing Then
                Set doCeiling = x
            Else
                Set doCeiling = temp
            End If
            
        End If
        
        
    
    End If
    
End Function

' 小于key的數量
Function rank(key As Variant) As Long
    rank = doRank(root, key)
End Function


Function doRank(x As RedNode, key As Variant) As Long
    Dim xKey As Variant
    Dim lSize As Long
    
    If x Is Nothing Then doRank = 0: Exit Function
    
    lSize = doSize(x.getLeft)
    
    xKey = x.getKey
    
    If key > xKey Then
        doRank = lSize + 1 + doRank(x.getRight, key)
    ElseIf key < xKey Then
        doRank = doRank(x.getLeft, key)
    Else
        doRank = lSize
    End If
    
    
End Function

' 排名為key的鍵
Function find(k As Long) As Variant
    
    Dim fNode As RedNode
    
    Set fNode = doFind(root, k)
    
    If fNode Is Nothing Then assign find, Null
    
    assign find, fNode.getKey
    
    
End Function


Function doFind(x As RedNode, k As Long) As RedNode
    Dim lSize As Long
    
    ' 正常情況下,是不可能到這個地方的。在某個節點必有滿足k = lSize的情況
    If x Is Nothing Then Set doFind = Nothing: Exit Function
    
    lSize = doSize(x.getLeft)
    
    If lSize > k Then
        ' 完全滿足,可以放心繼續探查
        Set doFind = doFind(x.getLeft, k)
        
    ElseIf lSize < k Then
        ' 如果lSize < k 說明左邊的節點不能滿足k排名,需要向右邊繼續查找,不過不需要在查找key,需要查找k - lSize - 1
        Set doFind = doFind(x.getRight, k - lSize - 1)
    Else
        Set doFind = x
    End If
    
    
End Function


' 鍵之間的數量
Function sizeRange(low As Variant, high As Variant)
    sizeRange = doSizeRange(root, low, high)
End Function

Function doSizeRange(x As RedNode, low As Variant, high As Variant) As Long
    If x Is Nothing Then doSizeRange = 0: Exit Function
    
    Dim xKey As Variant
    Dim size As Long
    
    assign xKey, x.getKey
    
    If xKey > low Then
        size = size + doSizeRange(x.getLeft, low, high)
    End If
    
    If xKey >= low And xKey <= high Then
        size = size + 1
    End If
    
    If xKey < high Then
        size = size + doSizeRange(x.getRight, low, high)
    End If
    doSizeRange = size
    
End Function
' 鍵之間的所有主鍵
Function keysRange(low As Variant, high As Variant) As Collection
    Dim c As New Collection
    
    doKeysRange root, c, low, high
    Set keysRange = c
End Function

' 中序周遊
Sub doKeysRange(x As RedNode, c As Collection, low As Variant, high As Variant)
    If x Is Nothing Then Exit Sub
    
    Dim xKey As Variant
    
    assign xKey, x.getKey
    
    If xKey > low Then
        doKeysRange x.getLeft, c, low, high
    End If
    
    If xKey >= low And xKey <= high Then
        c.add xKey
    End If
    
    If xKey < high Then
        doKeysRange x.getRight, c, low, high
    End If
End Sub

' 所有主鍵
Function keysAll() As Collection
    Dim c As New Collection
    
    doKeysRange root, c, min(), max()
    
    Set keysAll = c
End Function






'---------- 保證有序性和平衡性的輔助方法----------------------

' --------- 放入公共部分,如果放入節點,太占空間,因為節點可能有很多---------

' 是否是紅連接配接
Public Function isRed(x As RedNode) As Boolean
    If x Is Nothing Then isRed = False: Exit Function
    Dim color As NodeColor
    color = x.getColor
    isRed = color = RED
End Function

' 節點左旋轉
Public Function rotateLeft(x As RedNode) As RedNode
    Dim rNode As RedNode
    Set rNode = x.getRight
    '  介于兩者之間的節點(rNode.getLeft)需要挂到合适的節點,在這裡是傳回節點的左節點的右子節點,可以畫圖分析
    x.letRight = rNode.getLeft
    
    rNode.letLeft = x
    rNode.letColor = x.getColor
    ' 自己成為紅節點
    x.letColor = RED

    ' 右邊節點的數量繼承自己的數量
    rNode.letTreeSize = treeSize
    '
    x.letTreeSize = 1 + doSize(x.getRight) + doSize(x.getLeft)
    Set rotateLeft = rNode
End Function

' 節點右旋轉
Public Function rotateRight(x As RedNode) As RedNode
    Dim lNode As RedNode
    
    Set lNode = x.getLeft
    
    ' 介于兩者之間的節點需要挂到合适的節點,在這裡是傳回節點的右節點的左子節點,可以畫圖分析
    x.letLeft = lNode.getRight
    
    lNode.letRight = x
    lNode.letColor = x.getColor
    x.letColor = RED

    lNode.letTreeSize = treeSize
    
    x.letTreeSize = 1 + doSize(x.getLeft) + doSize(x.getRight)
    
    Set rotateRight = lNode
End Function

' 兩個節點變黑,由于自己處于中間節點,上升了一層,所有自己變紅
Sub flipColors(x As RedNode)
    x.letColor = RED
    x.getRight.letColor = BLACK
    x.getLeft.letColor = BLACK
End Sub

Private Sub assign(ByRef x, ByVal y)

    If IsObject(y) Then
        Set x = y
    Else
        x = y
    End If

End Sub


           

繼續閱讀