力扣刷题第二十五天--二叉树

前言

二叉树的第一天,掌握前序中序后序遍历,及对应的递归迭代,morris写法。难度一个比一个高是吧。。。

内容

一、二叉树的前序遍历

144.二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

递归

每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var res []int
func preorderTraversal(root *TreeNode) []int {
    res=[]int{}
    dfs(root)
    return res
}

func dfs(root *TreeNode){
    if root!=nil{
        res=append(res,root.Val)
        dfs(root.Left)
        dfs(root.Right)
    }
}
// func preorderTraversal(root *TreeNode) (vals []int) {
//     var preorder func(*TreeNode)//定义了一个名为preorder的变量,它的类型是一个函数,这个函数接收一个指向TreeNode类型的指针node作为参数
//     preorder = func(node *TreeNode) {
//         if node == nil {
//             return
//         }
//         vals = append(vals, node.Val)
//         preorder(node.Left)
//         preorder(node.Right)
//     }
//     preorder(root)
//     return
// }
迭代

递归的时候隐式地维护了一个栈,迭代的时候需要显式地将这个栈模拟出来

func preorderTraversal(root *TreeNode)[]int{
    var res []int
    var stack []*TreeNode

    for len(stack)>0||root!=nil{
        for root!=nil{
            res=append(res,root.Val)
            stack=append(stack,root.Right)
            root=root.Left
        }//循环结束后,即当前节点为空时
       root=stack[len(stack)-1]
       stack=stack[:len(stack)-1]
    }
    return res
}

递归迭代两种写法: 

时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。

空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。

 Morris 遍历

参考文章Morris遍历详解

Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。

时间复杂度:O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。

空间复杂度:O(1)。只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间

如果在遍历一棵树时严令禁止修改树的结构,那么Morris遍历就用不了

func preorderTraversal(root *TreeNode) (vals []int) {
  var p1,p2 *TreeNode=root,nil
     for p1!=nil{
         p2=p1.Left
         if p2!=nil{
             for p2.Right!=nil&&p2.Right!=p1{
                 p2=p2.Right
             }

             if p2.Right==nil{
                 vals=append(vals,p1.Val)
                 p2.Right=p1
                 p1=p1.Left
                 continue
             }
             p2.Right=nil
         }else{
             vals=append(vals,p1.Val)
         }
        p1=p1.Right
     }
     return
}
二、 二叉树的中序遍历

94.二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

递归
func inorderTraversal(root *TreeNode)  (res []int) {
     var inorder func(*TreeNode)
     inorder=func(node *TreeNode){
         if node==nil{
             return
         }
         inorder(node.Left)
         res=append(res,node.Val)
         inorder(node.Right)
     }
     inorder(root)
     return
}
迭代
func inorderTraversal(root *TreeNode)  (res []int) {
     stack:=[]*TreeNode{}
     for root!=nil||len(stack)>0{
         for root!=nil{
             stack=append(stack,root)
             root=root.Left
         }
         root=stack[len(stack)-1]
         stack=stack[:len(stack)-1]
         res=append(res,root.Val)
         root=root.Right
     }
    return
}
Morris遍历
func inorderTraversal(root *TreeNode)  (res []int) {
  for root!=nil{
      if root.Left!=nil{
          // predecessor 节点表示当前 root 节点向左走一步,然后一直向右走至无法走为止的节点
          p:=root.Left
          for p.Right!=nil&&p.Right!=root{
              // 有右子树且没有设置过指向 root,则继续向右走
              p=p.Right
          }
          if p.Right==nil{
              // 将 predecessor 的右指针指向 root,这样后面遍历完左子树 root.Left 后,就能通过这个指向回到 root
              p.Right=root
              // 遍历左子树
              root=root.Left
          }else{// predecessor 的右指针已经指向了 root,则表示左子树 root.Left 已经访问完了
              res=append(res,root.Val)
              p.Right=nil// 恢复原样
              root=root.Right// 遍历右子树
          }
      }else{// 没有左子树
          res=append(res,root.Val)
          // 若有右子树,则遍历右子树
			// 若没有右子树,则整颗左子树已遍历完,root 会通过之前设置的指向回到这颗子树的父节点
          root=root.Right
      }
  }
  return
}
三、二叉树的后序遍历

145.二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

递归
func postorderTraversal(root *TreeNode) []int {
    var res []int

  var postorder func(*TreeNode)
  postorder=func(node *TreeNode){
      if node==nil{
          return 
      }
      postorder(node.Left)
      postorder(node.Right)
     res=append(res,node.Val)
  }
  postorder(root)
  return res
}
迭代
func postorderTraversal(root *TreeNode)[]int{
    var res []int
    var stack []*TreeNode

    for len(stack)>0||root!=nil{
        for root!=nil{
            res=append(res,root.Val)
            stack=append(stack,root.Left)
            root=root.Right
        }//循环结束后,即当前节点为空时
       root=stack[len(stack)-1]
       stack=stack[:len(stack)-1]
    }
    reverse(res)
    return  res
}
func reverse(a []int) {
    l, r := 0, len(a) - 1
    for l < r {
        a[l], a[r] = a[r], a[l]
       l++
       r--
    }
}
//先序 中左右 调整为中右左 反转 左右中
func postorderTraversal(root *TreeNode)(res []int){
    stack:=[]*TreeNode{}
    var prev *TreeNode
    for root!=nil||len(stack)>0{
      for root!=nil{
          stack=append(stack,root)
          root=root.Left
      }
      root=stack[len(stack)-1]
      stack=stack[:len(stack)-1]
      if root.Right==nil||root.Right==prev{
          res=append(res,root.Val)
          prev=root
          root=nil
      }else{
          stack=append(stack,root)
          root=root.Right
      }
    }
    return
}
Morris遍历
func postorderTraversal(root *TreeNode) (res []int) {
    addPath:=func(node *TreeNode){
        resSize:=len(res)
        for ;node!=nil;node=node.Right{
            res=append(res,node.Val)
        }
        reverse(res[resSize:])
    }
    p1:=root
    for p1!=nil{
        if p2:=p1.Left;p2!=nil{
          for p2.Right!=nil&&p2.Right!=p1{
              p2=p2.Right
          }
          if p2.Right==nil{
              p2.Right=p1
              p1=p1.Left
              continue
          }
          p2.Right=nil
          addPath(p1.Left)
        }
        p1=p1.Right
    }
    addPath(root)
    return
}
func reverse(a []int){
    for i,n:=0,len(a);i<n/2;i++{
        a[i],a[n-i-1]=a[n-i-1],a[i]
    }
}

最后

迭代和Morris没理解透,看后续训练吧。

  • 14
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值