BinarySearchTree(二叉搜索树)-Swift实现(类方法)

在上一篇文章 Swift实现-Tree(树)、BaniryTree(二叉树)、BinarySearchTree(二叉搜索树)中,我们通过值类型(即枚举类型),实现了二叉搜索树的基本结构,及查找,插入等功能,现在我们通过类方法,来创建一个功能更加完善的二叉搜索树,这也是为了后面的红黑树等结构来坐下铺垫,因为上一篇文章已经对二叉搜索树进行了基本的介绍,这里就不多说了,有不清楚的,可以到上一篇文章去看看,下面直接上代码

基本实现

class ClassBinarySearchTree<T: Comparable> {
    private(set) var value: T
    private(set) var parent: ClassBinarySearchTree?
    private(set) var leftChild: ClassBinarySearchTree?
    private(set) var rightChild: ClassBinarySearchTree?
    
    ///是否为根节点
    var isRoot: Bool {
        return parent == nil
    }
    ///是否为叶子节点
    var isLeaf: Bool {
        return leftChild == nil && rightChild == nil
    }
    var isLeftChild: Bool {
        return parent?.leftChild === self
    }
    var isRightChild: Bool {
        return parent?.rightChild === self
    }
    
    var hasLeftChild: Bool {
        return leftChild != nil
    }
    
    var hasRightChild: Bool {
        return rightChild != nil
    }
    
    var hasAnyChild: Bool {
        return hasLeftChild || hasRightChild
    }
    
    var hasBothChildren: Bool {
        return hasLeftChild && hasRightChild
    }
    
    var count: Int {
        return (leftChild?.count ?? 0) + 1 + (rightChild?.count ?? 0)
    }
    
    init(_ value: T) {
        self.value = value
    }
}
复制代码

上面是对树的一个节点的实现,它拥有对父,左子及右子的引用。同时他还拥有一些辅助的功能

插入

func insert(_ node: ClassBinarySearchTree) {
        //1
        if node.value > self.value {
            //2
            guard let right = rightChild else {
                self.rightChild = node
                node.parent = self
                return
            }
            right.insert(node)
        }else {
            guard let left = leftChild else {
                self.leftChild = node
                node.parent = self
                return
            }
            left.insert(node)
        }
    }
复制代码
  • 1.如果插入的值,大于当前值,则应该插入到右子树中
  • 2.如果没有右子树,则用插入的值创建一个数,并成为右子树 左侧类同

类方法实现的二叉搜索树的插入操作,相对来说比通过枚举类型实现的二叉搜索树,做插入操作要容易的多,具体的可以到上一篇文章去查看 ###搜索

func search(_ value: T) -> ClassBinarySearchTree? {
        if value == self.value {
            return self
        }else if value > self.value {
            return self.rightChild?.search(value)
        }else {
            return self.leftChild?.search(value)
        }
    }
复制代码

搜索同样通过递归引用实现,在处理树形结构时,很多操作都能通过递归实现,并且更加的易懂及方便,后面会写一篇关于递归应用的总结,有兴趣的也可以看看《图解算法》一书中,关于“分而治之”的章节,感觉讲解的很透彻及有趣 ###删除 由于删除节点后,仍然要维持二叉搜索树,所以操作有些复杂,它分为下面的几种情况: 假定要删除的是T树中的节点z 1、 如果z是叶子节点,则直接删除,将父节点对应的孩子指为nil 2、如果z只有一个孩子,那么删除z,用z的孩子替代z,即重新制定z孩子的父节点等等 3、z有左右两个子树,那么就需要找到z节点的后继,该后继一定在右子树中,且没有左孩子的节点,就是右子树数中最小的那个节点,假定这个节点是y,我们需要用y的有子树,来替代y,然后用y替代z

简单总结就是,如果有右子树,则从右子树中找最小的节点y,来替代要被删除的节点z,删除y,同时从y的右子树中,找最小的节点y1来替换y,循环往复 在这个过程中,如果没有右子树,只有左子树,那么就从左子树中找最大的节点y,循环往复的进行

有右找右,没右找左

extension ClassBinarySearchTree {
    //1
    private func reconnectParentToNode(node: ClassBinarySearchTree?) {
        if let parent = parent {
            if isLeftChild {
                parent.leftChild = node
            } else {
                parent.rightChild = node
            }
        }
        node?.parent = parent
    }
    //2
    private func minNode() -> ClassBinarySearchTree {
        var node = self
        while let left = node.leftChild {
            node = left
        }
        return node
    }
    //3
    private func maxNode() -> ClassBinarySearchTree {
        var node = self
        while let right = node.rightChild {
            node = right
        }
        return node
    }
}
//4
extension ClassBinarySearchTree: CustomStringConvertible {
    var description: String {
        let value = self.value
        let left: String = self.leftChild?.description ?? "空"
        let right: String = self.rightChild?.description ?? "空"
        
        return "\(value) " + "[\(value)左孩子 \(left)]"  + "[\(value)右孩子 \(right)]"
    }
}
复制代码

上面是一些辅助方法

  • 1这个方法就是删除自己后,用新的节点代替自己
  • 2查找最小的子节点
  • 3查找最大的子节点
  • 4方便调试
var replaceMent: ClassBinarySearchTree?
        if let right = rightChild {
            replaceMent = right.minNode()
        }else if let left = leftChild {
            replaceMent = left.maxNode()
        }else {
            replaceMent = nil
        }
        
        let _ =  replaceMent?.remove()
        
        replaceMent?.leftChild = leftChild
        replaceMent?.rightChild = rightChild
        rightChild?.parent = replaceMent
        leftChild?.parent = replaceMent
        reconnectParentToNode(node: replaceMent)
        parent = nil
        leftChild = nil
        rightChild = nil
        return replaceMent
复制代码
  • 获得前驱,后继
func predecessor() -> ClassBinarySearchTree? {
        if let left = leftChild {
            return left.maxNode()
        }else {
            var node = self
            while let parent = node.parent {
                if parent.value < node.value {
                    return parent
                }
                node = parent
            }
        }
        return nil
    }
    
    func successor() -> ClassBinarySearchTree? {
        if let right = rightChild {
            return right.minNode()
        }else {
            var node = self
            while let parent = node.parent {
                if parent.value > node.value {
                    return parent
                }
                node = parent
            }
        }
        return nil
    }
复制代码

以中序遍历为例,一个节点的前驱,不一定会是该节点的左孩子,后继,也不一定是该节点的右孩子。上面的两个方法直接回返回节点的前驱和后继 前驱为例,如果该节点有左子树,那么前驱一定是左子树中最大的节点,如果没有左子树,那么前驱一定是父节点中最小的。 后继同样的道理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值