Swift

【数据结构与算法 - Swift实现】07 - 二叉搜索树 (B

2019-05-07  本文已影响0人  Lebron_James

二叉搜索树 (BST)可以提高查找、插入和删除的效率,时间复杂度都为O(log n)。成为二叉搜索树必须满足下面两个条件:

二叉搜索树和数组的对比

我们先来看看二叉搜索树和数组在查找、插入和删除操作的效率对比。假设我们有下列数组和二叉搜索树:

查找

假设我们要在这两个数据结构中找到 6:

插入

假设我们要在这两个数据结构中插入 -1:

移除

假设我们要在这两个数据结构中移除 4:

实现

首先简单定义BinarySearchTree

struct BinarySearchTree<Element: Comparable> {
    private(set) var root: BinaryTreeNode<Element>?
    init() {  }
}

因为二叉搜索树的元素有大小之分,所以Element必须遵循Comparable。这里我们用到了【数据结构与算法 - Swift实现】06 - 二叉树 (Binary Tree)BinaryTreeNode,可以到我在 github 的 demo 查看。

查找

判断树中是否包含某个值:

extension BinarySearchTree {
    func contains(_ value: Element) -> Bool {
        var current = root
        while let node = current {
            if node.value == value {
                return true
            }
            if value < node.value {
                current = node.leftChild
            } else {
                current = node.rightChild
            }
        }
        return false
    }
}

使用while循环,从 root 开始,如果当前节点的值等于要查找的值,则返回 true;如果要查找的值小于当前节点的值,把当前节点的左节点作为下一轮循环的节点,否则把当前节点的右节点作为下一轮循环的节点。

插入

extension BinarySearchTree {
    mutating func insert(_ value: Element) {
        root = insert(from: root, value: value)
    }
    
    private func insert(from node: BinaryTreeNode<Element>?,
                        value: Element) -> BinaryTreeNode<Element> {
        guard let node = node else {
            return BinaryTreeNode(value)
        }
        if value < node.value {
            node.leftChild = insert(from: node.leftChild, value: value)
        } else {
            node.rightChild = insert(from: node.rightChild, value: value)
        }
        return node
    }
}

在私有的insert方法实现中:

我们来使用一下这个插入方法:

var bst1 = BinarySearchTree<Int>()
bst1.insert(0)
bst1.insert(2)
bst1.insert(3)
bst1.insert(5)
bst1.insert(7)
bst1.insert(9)

上面的代码创建了这样一个树结构:

我们在来看一个例子:

var bst2 = BinarySearchTree<Int>()
bst2.insert(4)
bst2.insert(2)
bst2.insert(6)
bst2.insert(1)
bst2.insert(3)
bst2.insert(5)
bst2.insert(7)

上面的代码创建了这样一个树结构:

对比刚刚创建的两个树结构,可以发现bst1是不平衡的,而bst2则是完美地平衡。下一篇文章我们再讨论是否平衡的问题。

移除

移除操作比较复杂,需要考虑这几种情况:1)移除叶节点;2)移除只有一个子节点的节点;3)移除有两个子节点的节点。下面我们一个个来看。

移除叶节点

这是最简单的情况,直接把它移除即可

移除只有一个子节点的节点

移除只有一个子节点的节点后,要它的子节点连接到它的父节点。

移除有两个子节点的节点

如下图,要移除3:直接移除3之后,1和5就没有父节点了,这时我们应该怎么办?

如果把1和5连接到9的话,9就有三个子节点了,不符合二叉树的要求。

通常我们用3右边的最小值4来代替3的位置,替代之后,依然符合二叉搜索树的要求。树结构变为:

通过分析之后,我们来看看具体实现:

extension BinaryTreeNode {
    var minNode: BinaryTreeNode {
        return leftChild?.minNode ?? self
    }
}

extension BinarySearchTree {
    mutating func remove(_ value: Element) {
        root = remove(node: root, value: value)
    }
    
    private func remove(node: BinaryTreeNode<Element>?,
                value: Element) -> BinaryTreeNode<Element>? {
        
        guard let node = node else { return nil }
        
        if node.value == value {
            // 左右子节点都为空
            if node.leftChild == nil && node.rightChild == nil {
                return nil
            }
            // 左右子节点中,有其中一个为空
            if node.leftChild == nil {
                return node.rightChild
            }
            if node.rightChild == nil {
                return node.leftChild
            }
            // 左右子节点都不为空
            node.value = node.rightChild!.minNode.value // 把当前 `node` 的值更新为右子节点的最小值
            node.rightChild = remove(node: node.rightChild, value: node.value) // 把右子节点的最小值删除
            
        } else if value < node.value {
            node.leftChild = remove(node: node.leftChild, value: value)
        } else {
            node.rightChild = remove(node: node.rightChild, value: value)
        }
        
        return node
    }
}

验证一下我们刚刚的实现:

var bst = BinarySearchTree<Int>()
(0...12).forEach {
    bst.insert($0)
}
bst.remove(3)
bst.root?.traverseInOrder { print($0) }

// 结果
0
1
2
4
5
6
7
8
9
10
11
12

先插入0到12,接着把3移除,最后用中序遍历(左根右),3被移除,并且所有的节点还是按照对小到大排列,说明我们写的移除方法没错。

总结

二叉搜索树是一个非常强大的数据结构,在处理有序的数据时有很高的效率。查找、插入和移除的时间复杂度都为O(log n)

完整代码 >>

参考资料

Data Structures and Algorithms in Swift --- raywenderlich.com,如果想看原版书籍,请点击链接购买。

欢迎加入我管理的Swift开发群:536353151

下一篇文章:【数据结构与算法 - Swift实现】08 - 平衡二叉搜索树 (AVL Tree)

上一篇下一篇

猜你喜欢

热点阅读