美文网首页
6.二叉树的遍历、翻转二叉树

6.二叉树的遍历、翻转二叉树

作者: LucXion | 来源:发表于2022-01-06 11:33 被阅读0次

    根据节点访问顺序的不同,常见的二叉树遍历方式有4种。

    前序遍历(Preorder Traversal)

    树状结构打印

    • 先访问根节点,前序遍历左子树,前序遍历右子树
        func PreorderTraversal(){
            self .PreorderTraversalNote(note: root)
        }
        func PreorderTraversalNote(note:Note?){
            // 打印当前值
            print(note?.value ?? "")
            // 先遍历左子树
            if let leftNote = note?.leftNote {
                PreorderTraversalNote(note: leftNote)
            }
            // 再遍历右子树
            if let rightNote = note?.rightNote {
                PreorderTraversalNote(note: rightNote)
            }
            // 无子树,中止调用
        }
    

    中序遍历(Inorder Traversal)

    根节点在中间,左右子树的先后顺序可变

    二叉搜索树按序处理节点

    • 中序遍历左子树,根节点,中序遍历右子树(二叉搜索树的访问结果为从小到大)

    • 中序遍历右子树,根节点,中序遍历左子树(二叉搜索树的访问结果为从大到小)

        func InorderTraversal(){
            InorderTraversalNote(note: root)
        }
        func InorderTraversalNote(note:Note?){
            // 先遍历左子树
            if let leftNote = note?.leftNote {
                InorderTraversalNote(note: leftNote)
            }
            // 打印当前值
            print(note?.value ?? "")
            // 再遍历右子树
            if let rightNote = note?.rightNote {
                InorderTraversalNote(note: rightNote)
            }
            // 无子树,中止调用
        }
    

    后序遍历(Postorder Traversal)

    根节点在最后

    先子后父的操作

    • 后序遍历左子树,后序遍历右子树,根节点

    层序遍历(Level Order Traversal)

    从上到下,从左到右,一层一层访问

    计算二叉树高度、判断一棵树是否为完全二叉树

        func LevelOrderTraversal(){
            // 通过队列来实现,按层入队,节点出队,对应的子节点入队
            let queue = NSMutableArray.init()
            var tempNote:Note
            queue.add(root!)
            // 遍历,如果下一层还有元素,那就继续遍历
            while(queue.count > 0){
                tempNote = queue.firstObject as! BinarySearchTree.Note
                print(tempNote.value)
                // 打印出首个,入队它的子树
                queue.removeObject(at: 0)
                if let leftNote = tempNote.leftNote {
                    queue.add(leftNote)
                }
                if let rightNote = tempNote.rightNote {
                    queue.add(rightNote)
                }
            }
        }
    

    树的高度

    // 通过递归获取树的高度
        func recursionGetHeight()->Int {
            return recursionGetHeightWithNote(note: root)
        }
        func recursionGetHeightWithNote(note:Note?)->Int{
            if(note == nil){
                return 0
            }
            // 当前节点的高度为最大子节点高度 + 1
            return 1 + max(recursionGetHeightWithNote(note: note?.leftNote), recursionGetHeightWithNote(note: note?.rightNote))
        }
    
    // 通过层序遍历的方式获取树的高度
    func height()->Int{
            guard root != nil else { return 0 }
            // 通过队列的方式来层序遍历
            let queue = NSMutableArray.init()
            queue.add(root!)
            // 当前层数,因为以根节点开始,层数默认为1
            var height = 1
            // 当层节点个数
            var levelOrderElement = 1
            while(queue.count > 0){
                // 移除第一个节点,将子节点添加
                let note:Note = queue.firstObject as! Note
                // 层序遍历
                queue.removeObject(at: 0)
                // 当前层数节点数 - 1
                levelOrderElement -= 1
                if let leftNote = note.leftNote {
                    queue.add(leftNote)
                }
                if let rightNote = note.rightNote {
                    queue.add(rightNote)
                }
                if(levelOrderElement == 0 && queue.count > 0){
                    // 当前层数节点遍历结束,并且还有下一层节点需要遍历
                    height += 1
                    // 此时队列中的节点数为下一层节点总数
                    levelOrderElement = queue.count
                }
            }
            return height
        }
    

    判断是否为完全二叉树

    层序遍历,确定每个节点都满足完全二叉树的特点

    /// 判断是否为完全二叉树
        /// - Returns: <#description#>
        func isCompleteTree()->Bool{
            // 空树非完全二叉树
            guard root != nil else { return false }
            
            // 通过队列的方式来层序遍历
            let queue = NSMutableArray.init()
            queue.add(root!)
            /*
             当左右子节点不为空,那么继续层序遍历
             当左子节点不为空,右子节点为空,那么后面的所有节点都是叶子节点①
             当左子节点为空,右子节点也为空,那么后面所有节点都是叶子节点①
             当左子节点为空,右子节点不为空,直接返回失败②
             */
            var allLastLeafNote = false// 后面的节点是否都是叶子节点
            while(queue.count > 0){
                // 移除第一个节点,将子节点添加
                let note:Note = queue.firstObject as! Note
                var leftNull = true// 为空
                var rightNull = true
                // 层序遍历
                queue.removeObject(at: 0)
                if let leftNote = note.leftNote {
                    if(allLastLeafNote == true){
                        return false // ①后面的节点应该都是叶子节点,但出现了子节点
                    }
                    queue.add(leftNote)
                    leftNull = false
                }
                if let rightNote = note.rightNote {
                    if(allLastLeafNote == true){
                        return false // ①后面的节点应该都是叶子节点,但出现了子节点
                    }
                    queue.add(rightNote)
                    rightNull = false
                }
                if(leftNull == true && rightNull == false){
                    return false // ②无左子节点,但是有右子节点
                }else if(leftNull == true && rightNull == true){
                    allLastLeafNote = true//无左右子节点,后面都是叶子节点
                }else if(leftNull == false && rightNull == true){
                    allLastLeafNote = true//有左子节点无右子节点,后面都是叶子节点
                }
            }
            return true// 顺利遍历结束,全部满足条件
        }
    

    翻转二叉树

    所有节点的左右子树交换,关键在于遍历到左右的子节点,且不要重复遍历,所以任何一种遍历方式都可以实现。

    相关文章

      网友评论

          本文标题:6.二叉树的遍历、翻转二叉树

          本文链接:https://www.haomeiwen.com/subject/kvglcrtx.html