• 【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
    }
    
  • 相关阅读:
    Spring学习(21)--- AOP之Advice应用(上)
    Spring学习(20)--- Schema-based AOP(基于配置的AOP实现) -- 配置切入点pointcut
    Spring学习(19)--- Schema-based AOP(基于配置的AOP实现) --- 配置切面aspect
    Spring学习(18)--- AOP基本概念及特点
    Spring学习(17)--- 三种装配Bean方式比较
    Spring学习(16)--- 基于Java类的配置Bean 之 基于泛型的自动装配(spring4新增)
    Spring学习(15)--- 基于Java类的配置Bean 之 @Bean & @Scope 注解
    怎么找到与你Eclipse匹配的spring tool suite插件
    eclipse安装hibernate插件(在线Marketplace中安装)
    从配置maven环境到maven项目的新建
  • 原文地址:https://www.cnblogs.com/yinbiao/p/16131370.html
Copyright © 2020-2023  润新知