二叉樹的實(shí)現(xiàn)以及遍歷

二叉樹的實(shí)現(xiàn)原理

  • 組成結(jié)構(gòu)
    二叉樹是由根節(jié)點(diǎn)與左右子樹連接起來的數(shù)據(jù)結(jié)構(gòu)栈顷。所以實(shí)現(xiàn)一棵二叉樹昧甘,有兩個必要結(jié)構(gòu)數(shù)據(jù):
  1. 節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu) 用來連通各個節(jié)點(diǎn)
  2. 樹結(jié)構(gòu) 用來包裹 節(jié)點(diǎn)數(shù)據(jù)
  • 節(jié)點(diǎn)數(shù)據(jù)的實(shí)現(xiàn)

節(jié)點(diǎn)數(shù)據(jù)有兩點(diǎn)主要功能:

  1. 存放數(shù)據(jù)元素
  2. 能夠連接左右節(jié)點(diǎn)

依據(jù)這些特點(diǎn)会钝,可以使用下面的方式構(gòu)建節(jié)點(diǎn)數(shù)據(jù):

typealias BinaryTreeCompare = Comparable & Equatable
class Child<E: BinaryTreeCompare> : Equatable{
    static func == (lhs: Child<E>, rhs: Child<E>) -> Bool {
        let compareResult =
        lhs.element == rhs.element &&
        lhs.left == rhs.left &&
        lhs.right == rhs.right
        
        return compareResult
    }
    
    var element: E
    var left: Child?
    var right: Child?
    
    init(element: E, left: Child?, right: Child?) {
        self.element = element
        self.left = left
        self.right = right
    }

    func isLeaf() -> Bool { left == nil && right == nil }
    func hasTwoChild() -> Bool { left != nil && right != nil }
        
}


  • 二叉樹結(jié)構(gòu)內(nèi)容的實(shí)現(xiàn)

樹的結(jié)構(gòu)是對其節(jié)點(diǎn)的包裝,有這以下幾個特點(diǎn):

  1. 每棵二叉樹都有一個 根節(jié)點(diǎn)root
  2. 每一棵二叉樹都有著其自己的容量大小 size
  3. 添加新的 節(jié)點(diǎn)
  4. 移除每一棵樹的某個節(jié)點(diǎn) 節(jié)點(diǎn)
  5. 清除一棵樹的 所有節(jié)點(diǎn)
  6. 查找某個特定節(jié)點(diǎn) 是否存在

依據(jù)其功能特點(diǎn),就抽象出了幾個必要的基礎(chǔ)接口,如下:

protocol BinaryTreeProtocol : CustomStringConvertible {
    
    associatedtype T: BinaryTreeCompare
    //根節(jié)點(diǎn)
    var root: Child<T>? { set get }
    //節(jié)點(diǎn)添加
    mutating func add(element: T)
    //清除二叉樹所有節(jié)點(diǎn)
    mutating func clear()
    //移出特定節(jié)點(diǎn)元素
    mutating func remove(element: T)
    //獲取容量
    func size() -> Int
    //是否為空
    func isEmpty() -> Bool
    //是否包含某個特定元素節(jié)點(diǎn)
    func contains(element: T) -> Bool
}



具體實(shí)現(xiàn)

結(jié)構(gòu)構(gòu)建完成之后分飞,依據(jù)抽象內(nèi)容實(shí)現(xiàn)樹的結(jié)構(gòu),直接實(shí)現(xiàn)第二步抽象出來的接口即可睹限,如下:

struct BinaryTree<E: BinaryTreeCompare>: BinaryTreeProtocol {
    
    var root: Child<E>?
    var length: Int = 0
    var binaryTreeTravelsal: BinaryTreeTraversal<E>?
    typealias T = E
    
    init() {
        root = nil
    }
    
    init(travelsal: BinaryTreeTraversal<E>?) {
        self.init()
        self.binaryTreeTravelsal = BinaryTreeTraversal<E>()
    }

    
    mutating func add(element: E) {
        
        if root == nil {
            root = Child(element: element, left: nil, right: nil)
            return
        }
        
        var child = root
        var parent = root
        var compareResult: BinaryTreeCompareResult?
        
        while child != nil {
            parent = child
            if element > child!.element {
                child = child?.right
                compareResult = .BiggerThanRight
            }else if element < child!.element {
                child = child?.left
                compareResult = .BiggerThanLeft
            }else {
                compareResult = .Equal
            }
        }
        
        let childNode = Child(element: element, left: nil, right: nil)
        if BinaryTreeCompareResult.BiggerThanLeft == compareResult {
            parent?.left = childNode
            length += 1
        }else if BinaryTreeCompareResult.BiggerThanRight == compareResult {
            parent?.right = childNode
            length += 1
        }
        
    }
    
    mutating func clear() {
        root = nil
        length = 0
    }

    mutating func remove(element: E) {
        if root == nil { return }
        var child = root
        var parent = root
        var compareResult: BinaryTreeCompareResult?
        while child != nil {
            
            if element > child!.element {
                parent = child
                child = child?.right
                compareResult = .BiggerThanRight
            }else if element < child!.element{
                parent = child
                child = child?.left
                compareResult = .BiggerThanLeft
            }else {
                
                if compareResult == .BiggerThanRight {
                    parent?.right = nil
                }else if compareResult == .BiggerThanLeft {
                    parent?.left = nil
                }
                length -= 1
                break
            }
        }
        
    }

    func size() -> Int { length }

    func isEmpty() -> Bool { length == 0 }

    func contains(element: E) -> Bool {
        var child = root
        while child != nil {
            if element > child!.element {
                child = child!.left
            }else if element < child!.element {
                child = child!.right
            }else {
                return true
            }
        }
        return false
    }
    
    var description: String {""}
    
}

二叉樹的遍歷

二叉樹的遍歷方式有以下幾種:

  1. 前序遍歷:頭節(jié)點(diǎn) 先遍歷譬猫,再依次遍歷 左子樹右子樹
  2. 中序遍歷:左子樹 先遍歷,再遍歷 頭節(jié)點(diǎn) 邦泄,最后遍歷 右子樹
  3. 后序遍歷:左子樹 先遍歷删窒,再依次遍歷 右子樹 裂垦,最后遍歷 頭節(jié)點(diǎn)
  4. 層序遍歷:按照 二叉樹的層級一層一層的往下遍歷(這個在實(shí)現(xiàn) 樹的深度顺囊,以及判斷是否是一棵完全二叉樹中查找中序遍歷的前驅(qū)節(jié)點(diǎn)與后繼節(jié)點(diǎn) 等中用到,很重要)

這里的 前序蕉拢、中序特碳、后序 是指節(jié)點(diǎn)的遍歷的先后順序。這幾種遍歷可以通過 遞歸來實(shí)現(xiàn)晕换,而最后的 層序遍歷 午乓,在進(jìn)行每一層 遍歷 的時候,有著隊(duì)列的 先進(jìn)先出的特點(diǎn)闸准,可以考慮將 節(jié)點(diǎn)放入到隊(duì)列中益愈,然后通過隊(duì)列的 出隊(duì) 間接實(shí)現(xiàn)層級的遍歷,而終止條件就是 隊(duì)列的長度為空 的時候。

/*
 二叉搜索樹遍歷的幾種方法:
 1. 前序遍歷 (PreOrder Traversal)
 2. 中序遍歷 (InOrder Traversal)
 3. 后序遍歷(Postorder Traversal)
 4. 層序遍歷(Level Order Traversal)
 */

class BinaryTreeTraversal<E : Equatable & Comparable> {
    
    /*
     前序遍歷
     */
    func preOrderTraversal(root: Child<E>?) {
        
        if root == nil {
            return
        }
        
        print(root!.element,terminator: " ")
//        print(_ items: Any..., separator: String = " ", terminator: String = "\n")
        preOrderTraversal(root: root?.left)
        preOrderTraversal(root: root?.right)
    }

    /*
     中序遍歷
     */
    func inOrderTraversal(root: Child<E>?) {
        if root == nil {
            return
        }
        inOrderTraversal(root: root?.left)
        print(root!.element,terminator: " ")
        inOrderTraversal(root: root?.right)
    }


    /*
     后序遍歷
     */
    func postOrderTraversal(root: Child<E>?) {
        if root == nil {
            return
        }
        postOrderTraversal(root: root?.left)
        postOrderTraversal(root: root?.right)
        print(root!.element,terminator: " ")
    }


    /*
     層序遍歷(一層一層遍歷)
     */
    func levelOrderTraversal(root: Child<E>) {
        var queue = SignalQueue<Child<E>>()
        queue.enQueue(element: root)
        var travelsalStr = "["
        while !queue.isEmpty() {
            let node = queue.deQueue()
            travelsalStr.append("\(node!.element) ")
            if let left = node?.left {
                queue.enQueue(element: left)
            }
            if let right = node?.right {
                queue.enQueue(element: right)
            }
        }
        travelsalStr.removeLast()
        travelsalStr.append("]")
        print("\(travelsalStr)")
    }
    
    /*
     層序遍歷(一層一層遍歷)
     */
    func levelOrderTraversalCustom(root: Child<E>, enumtorEachClosure: (Child<E>)-> Bool) {
        var queue = SignalQueue<Child<E>>()
        queue.enQueue(element: root)

        while !queue.isEmpty() {
            let node = queue.deQueue()
            if enumtorEachClosure(node!) {
                return
            }
            if let left = node?.left {
                queue.enQueue(element: left)
            }
            if let right = node?.right {
                queue.enQueue(element: right)
            }
        }
    }

}


關(guān)于搜索二叉樹的節(jié)點(diǎn)刪除(刪除判斷依據(jù)是按照該節(jié)點(diǎn)的度進(jìn)行相應(yīng)的調(diào)整)

二叉樹的節(jié)點(diǎn)刪除不要理解為是節(jié)點(diǎn)連同節(jié)點(diǎn)的左右子樹一起刪除蒸其,二叉樹的節(jié)點(diǎn)刪除是僅僅刪除某個節(jié)點(diǎn)敏释,但是刪除節(jié)點(diǎn)的孩子節(jié)點(diǎn)還需要保留。所以針對于要刪除的節(jié)點(diǎn)分為三種情況:

  1. 擁有兩個孩子節(jié)點(diǎn)的情況(度為2)
  2. 擁有一個孩子節(jié)點(diǎn)的情況(度為1)
  3. 刪除的節(jié)點(diǎn)是葉子結(jié)點(diǎn)(度為0)
  4. 刪除的節(jié)點(diǎn)是根節(jié)點(diǎn)且只有一個子樹

針對于三種情況下的刪除摸袁,每種情況單獨(dú)分析:

  • 擁有兩個孩子節(jié)點(diǎn)的情況

刪除該節(jié)點(diǎn)之后由于沒有了以當(dāng)前節(jié)點(diǎn)為樹的 根節(jié)點(diǎn)钥顽,所以需要找一個節(jié)點(diǎn)去 取代當(dāng)前節(jié)點(diǎn)的位置,由于 二叉搜索樹 有順序大小靠汁,所以按照搜索樹的特點(diǎn)蜂大,刪除節(jié)點(diǎn)之后,這個順序依然能夠保持蝶怔。
依據(jù)這個特點(diǎn)奶浦,可以通過二叉搜索樹的 前驅(qū)節(jié)點(diǎn) 以及 后繼節(jié)點(diǎn) 來替換要 刪除的節(jié)點(diǎn),同時能夠保證其正常替代節(jié)點(diǎn)的功能添谊。

  • 擁有一個孩子節(jié)點(diǎn)的情況

如果 刪除的節(jié)點(diǎn)node 只擁有一個孩子節(jié)點(diǎn)财喳,需要用 子節(jié)點(diǎn)child 去替換刪除的 節(jié)點(diǎn)node。這里要區(qū)分孩子 節(jié)點(diǎn)是左節(jié)點(diǎn) 還是 右節(jié)點(diǎn)

  1. 如果 child 是左節(jié)點(diǎn)
child.parent = node.parent
node.parent.left = child
  1. 如果 child 是右節(jié)點(diǎn)
child.parent = node.parent
node.parent.right = child

  • 刪除的節(jié)點(diǎn)是葉子節(jié)點(diǎn)

如果刪除的節(jié)點(diǎn)是葉子結(jié)點(diǎn)斩狱,可以直接進(jìn)行刪除

node.parent = nil
  • 刪除的是根節(jié)點(diǎn)
root = child
child.parent = nil

綜合上面的所有情況耳高,可以實(shí)現(xiàn)刪除方法

mutating func deleteNode(node: inout Child<E>){
    
    //含有兩個孩子(度為2的情況)
    if node.hasTwoChild() {
        if let behindNode = behindChildInOrderTree(node) {
            //放入到根節(jié)點(diǎn)
            node.element = behindNode.element
            //指向當(dāng)前的后繼節(jié)點(diǎn)
            node = behindNode
        }
    }
    
    //節(jié)點(diǎn)度為1或者0的情況
    let replaceNode = node.left != nil ? node.left : node.right
    
    if replaceNode != nil {
        //更換刪除的子節(jié)點(diǎn)的父節(jié)點(diǎn)為刪除子節(jié)點(diǎn)的父節(jié)點(diǎn)
        replaceNode?.parent = replaceNode?.parent
        if node.parent == nil{
            root = replaceNode
            replaceNode?.parent = nil
        }else if node == replaceNode?.parent?.left {
            replaceNode?.parent?.left = replaceNode
        }else if node == replaceNode?.parent?.right{
            replaceNode?.parent?.right = replaceNode
        }
        
    }
    //葉子節(jié)點(diǎn)
    else {
        if node == node.parent?.left {
            node.parent?.left = nil
        }else {
            node.parent?.right = nil
        }
    }
    
}

如何判斷一棵樹是否為完全二叉樹

完全二叉樹 的特點(diǎn):可以理解為 滿二叉樹 多一層不滿的情況,同時 滿二叉樹 也是一棵 完全二叉樹。通過 層序遍歷 樹的節(jié)點(diǎn)來完成判定:

  1. 當(dāng)前節(jié)點(diǎn)有兩個孩子節(jié)點(diǎn),繼續(xù)向后遍歷
  2. 當(dāng)前節(jié)點(diǎn)不含有左孩子只含有右孩子庆揪,不是完全二叉樹膀捷,直接返回
  3. 當(dāng)前節(jié)點(diǎn) 不含有左孩子,也不含有右孩子缺前,遍歷后序節(jié)點(diǎn),后序節(jié)點(diǎn)需要 滿足均為葉子節(jié)點(diǎn)才能滿足其是完全二叉樹
  4. 當(dāng)前節(jié)點(diǎn) 含有左孩子,不含有右孩子修壕,遍歷后序節(jié)點(diǎn),后序節(jié)點(diǎn)需要 滿足均為葉子節(jié)點(diǎn)才能滿足其是完全二叉樹

按照判定標(biāo)準(zhǔn)遏考,可以實(shí)現(xiàn)其內(nèi)容:

/*
 通過直接判斷當(dāng)前節(jié)點(diǎn)是否滿足情況慈鸠,正常方案
 */
func compeleteTreeNormalMethod(root: Child<Int>) -> Bool {
    
    var travelsalQueue = SignalQueue<Child<Int>>()
    travelsalQueue.enQueue(element: root)
    
    var isJudgeLeaf = false
    
    while !travelsalQueue.isEmpty() {
        let child = travelsalQueue.deQueue()
        
        //判斷是否是葉子節(jié)點(diǎn)
        if isJudgeLeaf && !child!.isLeaf() {
            return false
        }
        
        //正常節(jié)點(diǎn),繼續(xù)遍歷下面的節(jié)點(diǎn)
        if child!.hasTwoChild(){
            travelsalQueue.enQueue(element: root.left!)
            travelsalQueue.enQueue(element: root.right!)
        }else if child?.left == nil && child?.right != nil {
            return false
        }else {
            //注意當(dāng)前如果左孩子不為空灌具,需要將左孩子加入到隊(duì)列以全部節(jié)點(diǎn)能夠正常遍歷
            if child?.left != nil {
                travelsalQueue.enQueue(element: child!.left!)
            }
            isJudgeLeaf = true
        }
        
    }
    
    return true
    
}

以上的方案可以優(yōu)化一下青团,可以直接 判定節(jié)點(diǎn)的左右孩子 進(jìn)而保證只需要依次遍歷左右孩子就能完成一次判定,不會像上面的方案 重復(fù)多次判定左右孩子節(jié)點(diǎn)為空的操作咖楣。

func isCompeleteTree(root: Child<Int>) -> Bool {
    
    var travelsalQueue = SignalQueue<Child<Int>>()
    travelsalQueue.enQueue(element: root)
    
    var judgeIsLeaf = false
    
    while !travelsalQueue.isEmpty() {
        
        let child = travelsalQueue.deQueue()
        if  judgeIsLeaf && !child!.isLeaf() {
            return false
        }
        
        if child?.left != nil {
            travelsalQueue.enQueue(element: child!.left!)
        }else if child?.right != nil{
            return false
        }
        
        //左節(jié)點(diǎn)為nil
        if child?.right != nil {
            travelsalQueue.enQueue(element: child!.right!)
        }else{
            //此時包含兩種情況:1.左節(jié)點(diǎn)有值 2.左節(jié)點(diǎn)沒有值督笆,在這兩種情況下,只需要判斷后序的節(jié)點(diǎn)是否是葉子節(jié)點(diǎn)即可
            //包含兩種情況:1. 左節(jié)點(diǎn)為nil 右節(jié)點(diǎn)為nil  2. 左節(jié)點(diǎn)非nil 诱贿,右節(jié)點(diǎn)nil 該節(jié)點(diǎn)為分界節(jié)點(diǎn)娃肿,后序節(jié)點(diǎn)需要滿足均為葉子節(jié)點(diǎn)的特征
            judgeIsLeaf = true
        }
        
        //經(jīng)過一次循環(huán)遍歷之后,此時已經(jīng)過濾如下情況:
        //1. 左節(jié)點(diǎn)為nil、右節(jié)點(diǎn)含有child
        //2. 左節(jié)點(diǎn)有值料扰,右節(jié)點(diǎn)含有值
        //3. 左節(jié)點(diǎn)與右節(jié)點(diǎn)同時都沒有值锨阿。
        
    }
    
    return true
}

如何翻轉(zhuǎn)二叉樹

二叉樹的翻轉(zhuǎn)是左右子樹的元素節(jié)點(diǎn)進(jìn)行交換,如下:

          50
        /    \
       30    60
      /  \     \
     20  40     70
    /  \
  10  25

前序: 50, 30, 20, 10, 25, 40, 60, 70
中序: 10, 20, 25, 30, 40, 50, 60, 70
后序: 10, 25, 20, 40, 30, 70, 60, 50

=========== 翻轉(zhuǎn)結(jié)果 ===========
          50
        /    \
       60     30
      /      /  \
     70     40  20
           /  \
          25  10

思想:
翻轉(zhuǎn)是 交換左子樹與右子樹 的位置记罚,所以通過 遍歷來交換兩者的位置 即可墅诡,
遍歷的方法有 前序遍歷、中序遍歷桐智、后序遍歷以及層序遍歷末早,都是能滿足條件的

/*
 前序遍歷實(shí)現(xiàn)
 */
func invertTreeUsePreOrder(root: Child<Int>?) {
    
    if root == nil { return }
    
    let tmp = root?.left
    root?.left = root?.right
    root?.right = tmp

    invertTreeUsePreOrder(root: root!.left)
    invertTreeUsePreOrder(root: root!.right)
}

/*
 后序遍歷實(shí)現(xiàn)
 */
func invertTreeUsePostOrder(root: Child<Int>?) {

    if root == nil { return }

    invertTreeUsePostOrder(root: root?.left)
    invertTreeUsePostOrder(root: root?.right)
    
    let tmp = root?.left
    root?.left = root?.right
    root?.right = tmp
    
}

/*
 中序遍歷實(shí)現(xiàn)
 注意中序遍歷的時候,右子樹再進(jìn)行子節(jié)點(diǎn)交換前说庭,同級別的節(jié)點(diǎn)先交換了左右子樹然磷,導(dǎo)致右子樹變成了左子樹,所以此種情況下應(yīng)該依舊交換左子樹內(nèi)容
 */
func invertTreeUseInOrder(root: Child<Int>?) {

    if root == nil { return }

    invertTreeUseInOrder(root: root?.left)
    
    let tmp = root?.left
    root?.left = root?.right
    root?.right = tmp
    
    invertTreeUseInOrder(root: root?.left)
}

/*
 使用層序遍歷來完成二叉樹的翻轉(zhuǎn)
 */
func invertTreeUseLevel(root: Child<Int>) {
    var travelQueue = SignalQueue<Child<Int>>()
    travelQueue.enQueue(element: root)
    
    while !travelQueue.isEmpty() {
        let child = travelQueue.deQueue()
        let tmp = child?.left
        child?.left = child?.right
        child?.right = tmp
        
        if child?.left != nil {
            travelQueue.enQueue(element: child!.left!)
        }
        
        if child?.right != nil {
            travelQueue.enQueue(element: child!.right!)
        }
    }

}

如何依據(jù)遍歷結(jié)果重構(gòu)一棵二叉樹

能夠根據(jù)遍歷結(jié)果來重新構(gòu)建一棵唯一的二叉樹的刊驴,有以下兩種情況:

  1. 中序遍歷 + 前序遍歷
  2. 中序遍歷 + 后序遍歷

特點(diǎn)就是兩種都必須有 中序遍歷姿搜, 中序遍歷 是用來確定左右子樹的,所以對于重構(gòu)唯一的二叉樹是必不可少的捆憎。由于 前序遍歷 + 后序遍歷 結(jié)果組合是無法確定 左右子樹 的舅柜,所以也就不能夠確定唯一的 二叉樹

比如:

前序: 50, 30, 20, 10, 25, 40, 60, 70
中序: 10, 20, 25, 30, 40, 50, 60, 70

根據(jù)前序順序,確定根節(jié)點(diǎn)為 50躲惰,在中序遍歷中找到根節(jié)點(diǎn)位置致份,中序遍歷中,50左邊的節(jié)點(diǎn)為 左子樹節(jié)點(diǎn)础拨,右邊的節(jié)點(diǎn)為 右子樹節(jié)點(diǎn)氮块,然后依據(jù)前序遍歷的第二個節(jié)點(diǎn)30得出中序遍歷30的位置,確定 50左子樹的根節(jié)點(diǎn)為30诡宗,循環(huán)查找構(gòu)建出一棵完整的樹滔蝉。

          50
        /    \
       30    60
      /  \     \
     20  40    70
    /  \
   10  25

二叉樹的前驅(qū)節(jié)點(diǎn)與后繼節(jié)點(diǎn)查找

二叉樹的 前驅(qū)節(jié)點(diǎn) 是針對中序遍歷順序來說的,指的 中序遍歷 的節(jié)點(diǎn)的 前一個節(jié)點(diǎn)塔沃。有著以下幾個特點(diǎn):

  1. 如果存在左子樹蝠引,前驅(qū)節(jié)點(diǎn)就是左子樹最后一個遍歷的節(jié)點(diǎn)(while child != nil { child.left.right.right }
  2. 如果不存在左子樹,那么前驅(qū)節(jié)點(diǎn)是按照當(dāng)前節(jié)點(diǎn)的層層父節(jié)點(diǎn)往上找芳悲,直到當(dāng)前的節(jié)點(diǎn)為父節(jié)點(diǎn)的右子樹節(jié)點(diǎn)的時候立肘,那么往上找到的節(jié)點(diǎn)的父節(jié)點(diǎn)就是前驅(qū)節(jié)點(diǎn)(while child.parent.right != child { child = child.parent })
中序遍歷順序

二叉樹的 后繼節(jié)點(diǎn)是中序遍歷節(jié)點(diǎn)的 后一個節(jié)點(diǎn)
后繼節(jié)點(diǎn) 幾點(diǎn)重要特性(與前驅(qū)節(jié)點(diǎn)恰恰相反):

  1. 如果存在右子樹边坤,那么 右子樹的最左邊的child 就是后繼節(jié)點(diǎn)
  2. 如果 不存在右子樹名扛,直接找父節(jié)點(diǎn),直到 父節(jié)點(diǎn)的左孩子節(jié)點(diǎn)是當(dāng)前節(jié)點(diǎn) 的時候茧痒,那么該父節(jié)點(diǎn)就是其后繼節(jié)點(diǎn)

結(jié)合上面的內(nèi)容肮韧,可以大致實(shí)現(xiàn)其方法:

func preChildInOrderTree(_ target: Child<Int>) -> Child<Int>? {
    var child = target
    
    if child.left != nil {
        child = child.left!
        while child.right != nil {
            child = child.right!
        }
        
        return child
    }
    
    //沒有左子樹
    else {
        
        while child.parent != nil && child.parent?.right != child  {
            child = child.parent!
        }
        
        return child.parent
    }
    
}


func behindChildInOrderTree(_ target: Child<Int>) -> Child<Int>? {
    var child = target
    
    if child.right != nil {
        child = child.right!
        while child.left != nil {
            child = child.left!
        }
        
        return child
    }
    
    //沒有右子樹
    else {
        while child.parent != nil && child.parent?.left != child  {
            child = child.parent!
        }
        return child.parent
    }
    
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子弄企,更是在濱河造成了極大的恐慌超燃,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拘领,死亡現(xiàn)場離奇詭異意乓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)约素,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門届良,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人圣猎,你說我怎么就攤上這事士葫。” “怎么了送悔?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵慢显,是天一觀的道長。 經(jīng)常有香客問我欠啤,道長荚藻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任洁段,我火速辦了婚禮鞋喇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眉撵。我一直安慰自己侦香,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布纽疟。 她就那樣靜靜地躺著罐韩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪污朽。 梳的紋絲不亂的頭發(fā)上散吵,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音蟆肆,去河邊找鬼矾睦。 笑死,一個胖子當(dāng)著我的面吹牛炎功,可吹牛的內(nèi)容都是我干的枚冗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛇损,長吁一口氣:“原來是場噩夢啊……” “哼赁温!你這毒婦竟也來了坛怪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤股囊,失蹤者是張志新(化名)和其女友劉穎袜匿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚疹,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡居灯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了内狗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穆壕。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖其屏,靈堂內(nèi)的尸體忽然破棺而出喇勋,到底是詐尸還是另有隱情,我是刑警寧澤偎行,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布川背,位于F島的核電站,受9級特大地震影響蛤袒,放射性物質(zhì)發(fā)生泄漏熄云。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一妙真、第九天 我趴在偏房一處隱蔽的房頂上張望缴允。 院中可真熱鬧,春花似錦珍德、人聲如沸练般。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薄料。三九已至,卻和暖如春泵琳,著一層夾襖步出監(jiān)牢的瞬間摄职,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工获列, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谷市,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓击孩,卻偏偏與公主長得像迫悠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子溯壶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容