首页 > 编程语言 >算法练习-第十四天【二叉树】

算法练习-第十四天【二叉树】

时间:2022-10-10 23:23:12浏览次数:68  
标签:遍历 TreeNode cur int 第十四天 算法 二叉树 rlt stack

二叉树的遍历

144. 二叉树的前序遍历

参考:代码随想录-二叉树的递归遍历
参考:代码随想录-二叉树的迭代遍历
参考:代码随想录-二叉树的迭代遍历统一写法

思路-递归法

二叉树的前序遍历是中左右的顺序,最常用的遍历方式是递归法。
总结递归法分三个步骤:

  1. 确定递归函数的参数与返回值:本题递归参数是当前节点,不存在返回值。
  2. 确定递归的终止条件:当访问的节点为空时,停止。
  3. 确定单层递归逻辑:中(处理节点的逻辑)、左、右的遍历顺序。
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func preorderTraversal(root *TreeNode) []int {
	rlt := []int{}
	var traversal func(*TreeNode)
	traversal = func(node *TreeNode) {
		if node == nil {
			return
		}

		//前序遍历 中 左 右
		rlt = append(rlt, node.Val)
		traversal(node.Left)
		traversal(node.Right)
	}
	traversal(root)

	return rlt
}

思路-迭代法

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中。递归能实现的使用栈一样可以实现。
前序遍历的顺序是:中左右, 因为栈是先进后出的操作,要想实现中左右的顺序遍历,应该先处理中间节点,然后依次右节点入栈、左节点入栈。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func preorderTraversal(root *TreeNode) []int {
	if root == nil {
		return []int{}
	}
	rlt := []int{}
	// 中 左 右
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		cur := stack.Remove(stack.Back()).(*TreeNode)
		rlt = append(rlt, cur.Val)
		if cur.Right != nil {
			stack.PushBack(cur.Right)
		}
		if cur.Left != nil {
			stack.PushBack(cur.Left)
		}

	}

	return rlt
}

思路-迭代法的统一写法

前中后序遍历使用迭代法时写出的代码格式不一致,主要是因为中序遍历无法像前后序遍历一样在迭代的过程中就处理中间的节点。
我们可以将要访问的节点放入到栈中,把要处理的节点也放入栈中并添加一个标记。这个标记可以是在处理的节点之后在栈中放入一个NULL,当栈顶为NULL时,就将其弹出,并获取下一个待处理的元素。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func preorderTraversal(root *TreeNode) []int {
	// 前序遍历顺序 中 左 右
	if root == nil {
		return []int{}
	}
	rlt := []int{}

	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		e := stack.Back()
		if e.Value != nil {
			cur := e.Value.(*TreeNode)
			stack.Remove(e) // 移除栈顶元素, 访问过 但没有处理
			if cur.Right != nil {
				stack.PushBack(cur.Right)
			}
			if cur.Left != nil {
				stack.PushBack(cur.Left)
			}

			stack.PushBack(cur)
			stack.PushBack(nil)
		} else {
			stack.Remove(e)                               // 弹出空节点 下一个就是待处理的节点
			cur := stack.Remove(stack.Back()).(*TreeNode) // 弹出处理的节点
			rlt = append(rlt, cur.Val)
		}
	}

	return rlt
}

总结

二叉树的前序遍历使用递归是最常见的,牢记递归的三个步骤。
迭代遍历中使用标记法来标记待处理的节点,这样可以统一前中后序遍历的一致性,避免了因为中序遍历时遍历顺序与处理节点的顺序不一致的问题。

145. 二叉树的后序遍历

思路-递归法

后序遍历的顺序是左右中, 按照此顺序来处理递归的单层逻辑。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func postorderTraversal(root *TreeNode) []int {
	rlt := []int{}
	var traversal func(*TreeNode)
	traversal = func(node *TreeNode) {
		if node == nil {
			return
		}

		// 后序遍历 左 右 中
		traversal(node.Left)
		traversal(node.Right)
		rlt = append(rlt, node.Val)
	}

	traversal(root)

	return rlt
}

思路-迭代法

前序遍历的顺序是中左右,后序遍历的顺序是左右中,只需要在前序遍历的基础上调整下左右节点的顺序就变成了中右左,那么将结果进行反转,就是后序遍历的顺序左右中了。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func postorderTraversal(root *TreeNode) []int {
	if root == nil {
		return []int{}
	}
	// 后序遍历: 左 右 中
	rlt := []int{}
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		// 从栈中 取出的顺序为 中 右 左
		cur := stack.Remove(stack.Back()).(*TreeNode)
		rlt = append(rlt, cur.Val)
		if cur.Left != nil {
			stack.PushBack(cur.Left)
		}
		if cur.Right != nil {
			stack.PushBack(cur.Right)
		}

	}
	//反转结果
	reverse(rlt)

	return rlt
}
func reverse(nums []int) {
	left, right := 0, len(nums)-1
	for left < right {
		nums[left], nums[right] = nums[right], nums[left]
		left++
		right--
	}
}

思路-迭代法的统一写法

与前序遍历基本一致

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func postorderTraversal(root *TreeNode) []int {
	if root == nil {
		return []int{}
	}
	rlt := []int{}
	// 后序遍历: 左 右 中
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		e := stack.Back()
		if e.Value != nil {
			cur := e.Value.(*TreeNode)
			stack.Remove(e)     // 移除栈顶元素
			stack.PushBack(cur) // 重新将节点加入到栈中
			stack.PushBack(nil) // 添加一个空的标识符

			if cur.Right != nil {
				stack.PushBack(cur.Right)
			}
			if cur.Left != nil {
				stack.PushBack(cur.Left)
			}
		} else {
			stack.Remove(e)
			cur := stack.Remove(stack.Back()).(*TreeNode)
			rlt = append(rlt, cur.Val)
		}
	}

	return rlt
}

94. 二叉树的中序遍历

思路-递归法

中序遍历的顺序是:左中右,改变单层递归的顺序即可。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) []int {
	rlt := make([]int, 0)
	var traversal func(node *TreeNode)
	traversal = func(node *TreeNode) {
		if node == nil {
			return
		}
		traversal(node.Left)
		rlt = append(rlt, node.Val)
		traversal(node.Right)
	}
	traversal(root)

	return rlt
}

思路-迭代法的统一写法


/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) []int {
	if root == nil {
		return []int{}
	}
	rlt := []int{}
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		e := stack.Back() //取出栈顶元素
		if e.Value != nil {
			cur := e.Value.(*TreeNode)
			stack.Remove(e)
			if cur.Right != nil {
				stack.PushBack(cur.Right)
			}
			stack.PushBack(cur)
			stack.PushBack(nil)
			if cur.Left != nil {
				stack.PushBack(cur.Left)
			}
		} else {
			stack.Remove(e)
			cur := stack.Remove(stack.Back()).(*TreeNode)
			rlt = append(rlt, cur.Val)
		}
	}

	return rlt
}

标签:遍历,TreeNode,cur,int,第十四天,算法,二叉树,rlt,stack
From: https://www.cnblogs.com/neilliu/p/16777747.html

相关文章

  • 回溯算法 组合问题(一)
    题目来自leetcode77题给定两个整数n和k,返回范围[1,n]中所有可能的k个数的组合。你可以按任何顺序返回答案。示例1:输入:n=4,k=2输出:[[2,4],[3,4],......
  • 牛客网高频算法题系列-BM19-寻找峰值
    牛客网高频算法题系列-BM19-寻找峰值题目描述给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。峰......
  • 学习常用模型及算法1.模拟退火算法
    title:学习常用模型及算法1.模拟退火算法excerpt:学习数学建模常用模型及算法tags:[数学建模,matlab]categories:[学习,数学建模]index_img:https://picture-......
  • 学习常用模型及算法4.元胞自动机
    title:学习常用模型及算法4.元胞自动机excerpt:学习数学建模常用模型及算法tags:[数学建模,matlab]categories:[学习,数学建模]index_img:https://picture-st......
  • LeetCode算法笔记 350. 两个数组的交集 II
    importjunit.framework.TestCase;importjava.util.Arrays;importjava.util.HashMap;publicclassLeetCode03extendsTestCase{/***350.两个数组......
  • Class3 GMM以及EM算法
    title:Class3GMM以及EM算法excerpt:想让张宇老师教我机器学习!!!tags:[语音识别,ASR]categories:[学习,语音识别]index_img:https://picture-store-repository.......
  • 代码随想录day20 | 654. 最大二叉树 617. 合并二叉树 700. 二叉搜索树中的搜索 98. 验
    654.最大二叉树题目|文章方法:模拟思路按照题目要求顺序使用递归函数traversal(nums,begin,end)对数组nums二叉树进行模拟。这道题的思路方法与105.从前序遍历和中序......
  • Leecode104 二叉树的最大深度
    //DFS解法前序遍历/***Definitionforabinarytreenode.*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*Tr......
  • AcWing算法提高课 容斥原理
    容斥原理的复杂度是2^n,一般n不会很大形如:  由于容斥原理一共有2^n中选法,可以用二进制枚举,1表示选择某个条件。然后将偶数个1的状态加起来,奇数个1的状态减去例题:ht......
  • qt容器与常用算法
    容器这些容器的使用方式和stl学的基本结构,使用方式是一样只要是数据就要使用容器,程序中的数据放在容器中方便增删改查。Qt库提供了一组通用的基于模板的容器类(contain......