'Updating a binary tree using a pointer

I'm trying to update a self-balancing binary tree. Normally, you can update it by 1) searching a node, 2) deleting it, 3) and inserting the tree with a new node. But, I want to see if this is possible simply by retaining a pointer to a node from the first step and updating it so that I can bypass the deletion and insertion and improve the time complexity, especially when it comes to a large number of nodes.

The tree itself is standard binary search tree.

public class TreeNode<T: Comparable>: Equatable {
    public typealias Node = TreeNode<T>
    
    var key: T?
    var leftChild: Node?
    var rightChild: Node?
    fileprivate weak var parent: Node?
    
    var isNullLeaf: Bool {
        return key == nil && isLeaf
    }
    
    var isLeaf: Bool {
        return rightChild == nil && leftChild == nil
    }
    
    public init(key: T?, leftChild: Node?, rightChild: Node?, parent: Node?) {
        self.key = key
        self.leftChild = leftChild
        self.rightChild = rightChild
        self.parent = parent
        
        self.leftChild?.parent = self
        self.rightChild?.parent = self
    }
    
    /// Null leaf
    public convenience init() {
        self.init(key: nil, leftChild: nil, rightChild: nil, parent: nil)
    }
    
    static public func == <T>(lhs: TreeNode<T>, rhs: TreeNode<T>) -> Bool {
        return lhs.key == rhs.key
    }
}

public final class Tree<T: Comparable> {
    public typealias Node = TreeNode<T>
    
    fileprivate(set) var root: Node
    fileprivate let nullLeaf = Node()
    
    public init() {
        root = nullLeaf
    }
    
    func search(key: T, f: (inout Node) -> Void) {
        search(key: key, node: &root, f:  f)
    }

    fileprivate func search(key: T, node: inout Node, f: (inout Node) -> Void) {
        if !node.isNullLeaf {
            if let nodeKey = node.key {
                /// When a node is found, pass by reference as an argument to a closure so that it retains the connection to the node when it's being update.
                if key == nodeKey {
                    f(&node)
                } else if key < nodeKey {
                    guard node.leftChild != nil else {
                        return
                    }
                    search(key: key, node: &node.leftChild!, f: f)
                } else {
                    guard node.rightChild != nil else {
                        return
                    }
                    search(key: key, node: &node.rightChild!, f: f)
                }
            }
        }
    }

    public func insert(key: T) {
       /// insertion logic
    }
    
    /// Other operations
}

My idea was to search the tree through recursion and when a node is found, pass it as an argument to a closure function, which will ultimately be called to update the node. Also, the found node would be pass by reference.

class Test<T: Comparable> {
    private(set) var tree = Tree<T>()
    
    func insert(key: T) {
        tree.insert(key: key)
    }
    
    func update(for node: T, with newNode: T) {
        tree.search(key: node) { foundNode in
            foundNode.key = newNode
        }
    }
}

let test = Test<MyNode>()
let node = MyNode()
let anotherNode = MyNode()
test.insert(key: node)
test.update(for: node, with: anotherNode)

The problem is the update doesn't happen. If I search for newly updated node in the tree, it doesn't exist.

Update

Above code is a modified version of a Red-Black tree, specifically modifying the search method to use a pointer instead.

I've tried my idea on a simplified version of a binary search tree below and it seems to be updating the value of a specified node.

public final class BinaryTree<T: Comparable> {
    public final class Node<T> {
        public var value: T
        public var leftChild: Node<T>?
        public var rightChild: Node<T>?
        
        public init(value: T, leftChild: Node<T>? = nil, rightChild: Node<T>? = nil) {
            self.value = value
            self.leftChild = leftChild
            self.rightChild = rightChild
        }
    }
    
    public var rootNode: Node<T>
    
    public init(rootNode: Node<T>) {
        self.rootNode = rootNode
    }
    
    public func addNodes(to parent: Node<T>, leftChild: Node<T>?, rightChild: Node<T>?) {
        parent.leftChild = leftChild
        parent.rightChild = rightChild
    }
    
    public func searchTree(_ value: T, node: inout Node<T>?, f: (inout Node<T>?) -> Void) {
        if node == nil || value == node?.value {
            f(&node)
        } else if value < node!.value {
            searchTree(value, node: &node!.leftChild, f: f)
        } else {
            searchTree(value, node: &node!.rightChild, f: f)
        }
    }
}

Tested here.

var rootNode: BinaryTree<Int>.Node<Int>? = BinaryTree<Int>.Node(value: 100, leftChild: nil, rightChild: nil)
let tree = BinaryTree(rootNode: rootNode!)

/// add new nodes. This is not a self-balancing tree so the left child's value has to be smaller than the parent and the right child's value greater than the parent.
let leftChild = BinaryTree<Int>.Node(value: 0, leftChild: nil, rightChild: nil)
let rightChild = BinaryTree<Int>.Node(value: 200, leftChild: nil, rightChild: nil)
tree.addNodes(to: rootNode!, leftChild: leftChild, rightChild: rightChild)

/// the node argument is the starting point of the search so let's start from the root node.
/// the found node will be updated with a new node with a value 50
tree.searchTree(0, node: &rootNode) { foundNode in
    let newNode = BinaryTree<Int>.Node(value: 50, leftChild: nil, rightChild: nil)
    foundNode = newNode
}

/// The node with a value as 0 is now gone.
tree.searchTree(0, node: &rootNode) { foundNode in
    print(foundNode) /// nil
}

/// The node has bee properly updated.
tree.searchTree(50, node: &rootNode) { foundNode in
    print(foundNode) /// node with 50 as the value found
}

But can't seem to figure out why the original code isn't updating a node by pointer.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source