【LeetCode】二叉树的中序遍历(非递归形式:栈模拟递归,标记模拟递归,莫里斯遍历)...

二叉树的中序遍历

题目链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/

面试的时候问这道题基本都是考察非递归的写法,但还是贴一下递归写法:

方法1:递归

var result []int
func f(root *TreeNode)  {
	if root==nil{
		return
	}
	f(root.Left)
	result=append(result,root.Val)
	f(root.Right)
}
func inorderTraversal(root *TreeNode) []int {
	result=[]int{}
	f(root)
	return result
}

时间复杂度:O(N)

空间复杂度:O(N)

方法2:采用栈模拟递归

递归其实也有一个栈,要求非递归写法的话我们可以模拟这个栈

func inorderTraversal(root *TreeNode) []int {
	var stack []*TreeNode
	var result []int
	
	// 中序遍历,非递归形式

	for root != nil ||  len(stack) > 0 {

		// 当前节点不空,则一直将当前节点的左子树入栈
		if root!=nil{
			stack=append(stack,root)
			root=root.Left
		}else {
			// 当前节点空,则取栈顶元素并打印
			node:= stack[len(stack)-1]
			stack = stack[:len(stack)-1]
			result = append(result, node.Val)

			// 然后当前元素指向栈顶元素的右子树
			root = node.Right

		}
	}
	return result
}

时间复杂度:O(N)

空间复杂度:O(N)

方法3:采用标记法模拟递归

有点类似golang的gc的三色标记法

对每个节点,都需要入栈标记两次才能访问其元素值,第一次入栈是不能访问其值的,因为第一次入栈是第一次访问该节点,需要先访问该节点的左子树,本身节点,右子树分别入栈,第二次访问时,才访问其元素值

func inorderTraversal(root *TreeNode) []int {
	var stack []*TreeNode
	var result []int
	if root == nil {
		return result
	}

	// 中序遍历,非递归形式 标记法模拟递归
	flagMap := make(map[*TreeNode]int)
	stack = append(stack, root)
	flagMap[root] = 1

	for len(stack) > 0 {
		node := stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		flag := flagMap[node]

		// 第一次入栈元素 按照 右 自身 左 的顺序入栈,因为栈的性质,要求中序,最先打印的最后入
		if flag == 1 {
			
			// 右孩子,标记为第一次入栈
			if node.Right != nil {
				stack = append(stack, node.Right)
				flagMap[node.Right] = 1
			}

			// 自身 标记为第二次入栈
			stack = append(stack, node)
			flagMap[node]++

			// 左孩子 标记为第一次入栈
			if node.Left != nil {
				stack = append(stack, node.Left)
				flagMap[node.Left] = 1
			}

		} else if flag == 2 {
			// 已经是第二次入栈的元素了,直接打印
			result = append(result, node.Val)
		}
	}
	return result
}

方法4:莫里斯遍历

递归,迭代和模拟发方式都使用了额外的辅助空间,而莫里斯遍历的优点就是没有使用任何辅助空间,缺点就是将二叉树变成了链表结构

根据中序遍历的特点,将树转化为一个链表

在中序遍历中,根节点的前一个字符肯定是其左子树中最右边的那个节点

  • 将黄色部分挂到5的右子树上
  • 将2和5挂到4的右子树上

这样整棵树基本上就变成了一个链表结构,遍历即可,结构即是中序遍历结果

时间复杂度:O(N)

空间复杂度:O(1)

func inorderTraversal(root *TreeNode) []int {
	var result []int
	if root == nil {
		return result
	}

	for root!=nil{
		// 如果左节点不为空,则将当前节点连带右子树全部挂到 左节点的最右子树下面
		if root.Left!=nil{

			// pre.right 就是左节点的最右子树
			pre:=root.Left
			for pre.Right!=nil{
				pre=pre.Right
			}

			// 挂上去
			pre.Right=root

			// 将root指向原来root的left
			node:=root
			root=root.Left
			node.Left=nil
		}else {
			// 左子树为空,打印节点,并向右遍历
			result=append(result,root.Val)
			root=root.Right
		}
	}
	return result
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值