美文网首页
二叉树算法总结

二叉树算法总结

作者: Breezes | 来源:发表于2022-01-22 17:53 被阅读0次

    迭代

    前序遍历

    根据栈的属性对二叉树进行遍历,注意先右节点入栈,随后左节点入栈
    1.根节点入栈
    2.当栈不为空时,栈顶元素出栈,如果栈顶元素右节点不为空,右节点入栈,如果左节点不为空,左节点入栈,
    3.将当前出栈的栈顶元素的值加入数组

        //二叉树的前序遍历
        func stackPreorder(root: TreeNode?) -> [Int] {
            var list = [Int]()
            guard root != nil else {
                return list
            }
            
            var stack = Stack()
            
            //1.根节点入栈
            stack.push(root)
            
            while !stack.isEmpty {
                //2.栈顶元素出栈
                if let temp = stack.pop() {
                    //右节点入栈
                    if temp.right != nil {
                        stack.push(temp.right)
                    }
                    
                    //左节点入栈
                    if temp.left != nil {
                        stack.push(temp.left)
                    }
                    
                    //3.添加栈顶元素的值到数组
                    list.append(temp.val)
                }
            }
            return list
        }
    

    中序遍历

    1.指针cur不断向左移动,并且依次入栈
    2.直到cur为空,开始出栈,依次将出栈元素加入到数组,
    3.最后将cur指向出栈元素的right指针,迭代执行上述操作

        func stackInOrder(root: TreeNode?) -> [Int] {
            var list = [Int]()
            guard root != nil else {
                return list
            }
            
            var cur: TreeNode?
            
            var stack = Stack()
            
            cur = root
            
            while !stack.isEmpty || cur != nil {
                //1.向左移动,依次入栈
                while cur != nil {
                    stack.push(cur)
                    cur = cur?.left
                }
                
                //2.cur为空,开始出栈。
                if let temp = stack.pop() {
                    list.append(temp.val)
                    //3.指针cur指向右子树
                    cur = temp.right
                }
            }
            return list
        }
        
    

    后序遍历

    1.定义cur指针(当前遍历的节点)和pre指针(上一次遍历的节点)
    2.指针cur不断向左移动,并且依次入栈
    3.直到cur为空,开始出栈,cur指向出栈的元素
    4.判断cur.right == nilcur.right == pre
    (1)、当cur.right == nil说明cur没有右节点需要遍历,val直接加入list即可;当cur.right == pre,说明cur的右节点已经遍历过,同样加入list即可
    (2)、当cur.right != nil说明cur有右节点需要遍历,因此再次将cur节点入栈,并且把cur指向它的右节点cur = cur.right
    总结:从根节点开始到左子节点依次入栈,随后出栈并遍历出栈元素,发现有右子节点时(当发现右节点已被遍历时,就不遍历),将当前出栈元素重新入栈,继而遍历右节点,循环执行前述步骤

       func stackLaOrder(root: TreeNode?) -> [Int] {
            var list = [Int]()
            guard root != nil else {
                return list
            }
            
            //1.定义cur和pre指针
            var cur: TreeNode? = root
            var pre: TreeNode?
            
            var stack = Stack()
            
            while !stack.isEmpty || cur != nil {
                //2.cur向左移动,入栈
                while cur != nil {
                    stack.push(cur)
                    cur = cur?.left
                }
                
                //3.cur此时为空,开始出栈
                cur = stack.pop()
                
                if cur != nil {
                    //4.(1)当前出栈元素没有右节点或者右节点已被遍历完成
                    if cur!.right == nil || cur?.right == pre {
                        list.append(cur!.val)
                        pre = cur
                        cur = nil
                    } else {
                        //(2)当前出栈元素右节点没有被遍历,将当前元素重新入栈,并开始遍历右节点
                        stack.push(cur)
                        cur = cur?.right
                    }
                }
            }
            return list
        }
    

    Morris

    这篇文章讲得非常好

    Morris遍历原则

    当前节点定义为cur

    • cur无左孩子,cur右移(cur = cur.right)
    • cur有左孩子,找到cur左子树上的最右节点,记作mostRight
      • mostRight无右节点,mostRight.right = cur,cur左移(cur = cur.left)
      • mostRight有右节点,mostRight.right = nil,cur右移(cur = cur.right)

    注意:查找cur左子树上的最右节点时应该判断不等于cur

    Morris遍历实质

    建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次

    二叉树.png

    前序遍历

    1.节点cur的左节点为空,添加cur的值到数组,并右移
    2.cur左节点不为空就查找最右节点mostRight,如果mostRight的右节点为空,就指定mostRight.right=cur并添加cur的值到数组,cur左移;如果mostRight的右节点不为空,就指定mostRight.right=nil,cur右移

    具体流程⬇️:

    1.cur=1,左孩子=2,找到2的的最右节点mostRight=5mostRight.right=nil,所以5.right=1,cur左移cur = 1.left
    2.cur=2,左孩子=4,找到4的的最右节点mostRight=9mostRight.right=nil,所以9.right=2,cur左移cur = 2.left
    3.cur=4,左孩子=8,找到8的的最右节点mostRight=8mostRight.right=nil,所以8.right=4,cur左移cur = 4.left
    4.cur=8,无左孩子,所以cur右移,cur=8.right(注意第3步8的右节点已经是4了)
    5.cur=4,有左孩子=8,找到8的的最右节点mostRight=4,所以使8.right=nil,并且cur右移cur = 4.right
    6.cur=9,无左孩子,所以cur右移,cur=9.right(注意第2步9的右节点已经是2了)
    7.cur=2,左孩子=4,找到4的的最右节点mostRight=9,所以使9.right=nil,并且cur右移cur = 2.right
    ....

        func morrisPerorder(root: TreeNode?) -> [Int] {
            var list = [Int]()
            guard root != nil else {
                return list
            }
            var cur = root
            var mostRight: TreeNode?
            
            while cur != nil {
                mostRight = cur?.left
                
                if mostRight != nil { //有左孩子
                    
                    //查找最右节点
                    while mostRight?.right != nil && mostRight?.right != cur {
                        mostRight = mostRight?.right
                    }
                    
                    if mostRight?.right == nil {//最右节点的right=nil,所以mostRight.right=cur,cur左移
                        if let val = cur?.val {
                            list.append(val)
                        }
                        mostRight?.right = cur
                        cur = cur?.left
                    } else {//最右节点的right!=nil,所以mostRight.right=nil,cur右移
                        mostRight?.right = nil
                        cur = cur?.right
                    }
                } else { //无左孩子,右移
                    list.append(cur!.val)
                    cur = cur?.right
                }
            }
            return list
        }
    

    中序遍历

    1.节点cur的左节点为空,添加cur的值到数组,并右移
    2.cur左节点不为空就查找最右节点mostRight,如果mostRight的右节点为空,就指定mostRight.right=cur,cur左移并退出此次循环;如果mostRight的右节点不为空,就指定mostRight.right=nil,cur右移并添加cur的值到数组

     func morrisInOrder(root: TreeNode?) -> [Int] {
            var list = [Int]()
            guard root != nil else {
                return list
            }
            
            var cur:TreeNode? = root
            var mostRight: TreeNode?
            
            while cur != nil {
                mostRight = cur!.left
                if mostRight != nil {
                    while mostRight!.right != nil && mostRight!.right != cur {
                        mostRight = mostRight!.right
                    }
                    
                    if mostRight!.right == nil {//右节点为空,指定右节点=cur,左移cur并退出此次循环
                        mostRight!.right = cur
                        cur = cur!.left
                        continue
                    } else {
                        mostRight!.right = nil//右节点不为空,指定右节点=nil,
                    }
                }
                list.append(cur!.val)
                cur = cur?.right
            }
            
            return list
        }
    

    后序遍历

    这里讲的很详细

    morris后序遍历和中序遍历相似,区别在于⬇️
    1.节点cur的左节点为空时,只需要右移cur(cur = cur.right)
    2.cur的左节点不为空时候查找最右节点mostRight,如果mostRight的右节点不为空除了将mostRight.right=nil外,进行反转链表并遍历
    3.最后记得还要反转根节点为头部的链表并遍历
    具体流程⬇️:
    ....前面省略
    1.当cur=4,左孩子=8,mostRight=8mostRight.right = 4,所以将mostRight.right=nil,并反转遍历以cur.left(也就是8)为head链表,然后右移cur
    2.cur=9,无左孩子,右移
    3.cur=2,左孩子=4,mostRight=9mostRight.right = 2,所以将mostRight.right=nil,并反转遍历以cur.left(也就是4)为head链表,然后右移cur
    .
    .
    .
    最后一步,是将以1为head的(1->3->7)链表反转遍历完成

        func morrisPostorder(root: TreeNode?) -> [Int]? {
            guard root != nil else {
                return nil
            }
            var list = [Int]()
            var cur: TreeNode? = root
            var mostRight: TreeNode?
            
            while cur != nil {
                mostRight = cur?.left
                
                if mostRight != nil {
                    
                    while mostRight?.right != nil && mostRight?.right != cur {
                        mostRight = mostRight?.right
                    }
                    
                    if mostRight?.right == nil {
                        mostRight?.right = cur
                        cur = cur?.left
                        continue
                    } else {
                        mostRight?.right = nil
                        traverseReverse(cur?.left, &list)
                    }
                }
                cur = cur?.right
            }
            //以根节点为起点的链表
            traverseReverse(root, &list)
            return list
        }
        
        func traverseReverse(_ head: TreeNode?,_ list: inout [Int]) {
            //翻转链表
            let reverseNode = reverseList(head: head)
            var cur = reverseNode
            
            //从后往前遍历
            while cur != nil {
                list.append(cur!.val)
                cur = cur?.right
            }
            //最后记得反转回来
            _ = reverseList(head: reverseNode)
        }
        
        func reverseList(head: TreeNode?) -> TreeNode? {
            guard head != nil else {
                return nil
            }
            
            var cur = head
            var pre: TreeNode?
            while cur != nil {
                let next = cur?.right
                cur?.right = pre
                pre = cur
                cur = next
            }
            return pre
        }
    

    相关文章

      网友评论

          本文标题:二叉树算法总结

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