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
    }

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 目录 0.树0.1 一般树的定义0.2 二叉树的定义 1.查找树ADT 2.查找树的实现2.1 二叉查找树2.2 ...
    王侦阅读 7,176评论 0 3
  • 1 概述 二叉搜索树,顾名思义,其主要目的用于搜索,它是二叉树结构中最基本的一种数据结构,是后续理解B树、B+树、...
    CodingTech阅读 3,130评论 5 35
  • 在计算机科学中,二叉搜索树(Binary Search Tree)(有时称为有序或排序的二叉树)是一种能存...
    Leon_Geo阅读 2,891评论 0 3
  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,447评论 1 31
  • 1、前言 二叉树是非常重要的一种数据结构,二叉搜索树是其中的一种类型,其任意节点x,左子树中的关键字最大不超过x....
    某昆阅读 623评论 0 4