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

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

时间:2022-10-12 23:56:28浏览次数:81  
标签:node Right TreeNode nil 第十七 算法 二叉树 return Left

二叉树

110. 平衡二叉树

参考:代码随想录

思路

二叉树的深度:从根节点出发到该节点的最长简单路径边的条数。
二叉树的高度:从该节点出发到叶子节点的最长简单路径的条数。
题目要求判断是否是高度平衡的二叉树, 那么就是二叉树高度的问题,既然是求高度问题使用后序遍历。

递归

  1. 递归函数的参数和返回值:var getHeight func(*TreeNode) int
  2. 递归的终止条件:if node == nil ; return
  3. 单层递归的逻辑:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1,求出左右子树的差值,如果差值大于1则返回-1,表示当前已经不是平衡二叉树了。
    leftH := getHeight(node.Left) // 左
    	if leftH == -1 {
    		return -1
    	}
    	rightH := getHeight(node.Right) // 右
    	if rightH == -1 {
    		return -1
    	}
    
    	if abs(leftH-rightH) > 1 { // 中
    		return -1
    	}
    

整体递归代码如下:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isBalanced(root *TreeNode) bool {
	var getHeight func(*TreeNode) int
	getHeight = func(node *TreeNode) int {
		if node == nil {
			return 0
		}
		leftH := getHeight(node.Left) // 左
		if leftH == -1 {
			return -1
		}
		rightH := getHeight(node.Right) // 右
		if rightH == -1 {
			return -1
		}

		if abs(leftH-rightH) > 1 { // 中
			return -1
		}

		return 1 + max(leftH, rightH)
	}

	return getHeight(root) != -1
}
func abs(a int) int {
	if a < 0 {
		return -a
	}

	return a
}

func max(a, b int) int {
	if a > b {
		return a
	}

	return b
}

迭代法

因为求二叉树的高度,因此无法使用层序遍历来完成,可以使用栈来模拟后序遍历求得节点的高度。然后再使用栈模拟后序遍历遍历左右子树判断是否是平衡二叉树。

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isBalanced(root *TreeNode) bool {
	if root == nil {
		return true
	}

	// 通过栈模拟的后序遍历 来求该节点的高度
	var getDepth func(*TreeNode) int
	getDepth = func(node *TreeNode) int {
		if node == nil {
			return 0
		}
		depth := 0
		rlt := 0
		stack := list.New()
		stack.PushBack(node)
		for stack.Len() > 0 {
			e := stack.Back()
			if e.Value != nil {
				cur := e.Value.(*TreeNode)
				stack.Remove(e)
				depth++
				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)
				stack.Remove(stack.Back())
				depth--
			}
			if rlt < depth {
				rlt = depth
			}
		}

		return rlt
	}

	st := list.New()
	st.PushBack(root)
	for st.Len() > 0 {
		node := st.Remove(st.Back()).(*TreeNode)
		if abs(getDepth(node.Left)-getDepth(node.Right)) > 1 { //处理中间节点
			return false
		}

		if node.Right != nil {
			st.PushBack(node.Right)
		}

		if node.Left != nil {
			st.PushBack(node.Left)
		}
	}

	return true

}
func abs(a int) int {
	if a < 0 {
		return -a
	}

	return a
}

总结

通过本题了解二叉树的深度和高度的区别,使用前序遍历求深度,使用后序遍历求高度。

257. 二叉树的所有路径

参考:代码随想录

思考

求二叉树的所有路径,就是记录从根节点到叶子节点到所有路径。需要使用前序遍历这样可以让父节点指向孩子节点。
在遍历的过程中,需要进行回溯操作,回退一个路径进入下一个路径之中。

递归

  1. 确定递归函数参数和返回值: var traversal func(*TreeNode, []string)
  2. 递归的结束条件:遇到了叶子节点if node.Left == nil && node.Right == nil {...}
  3. 单层递归的处理逻辑:
    • 因为是前序遍历,要先处理节点逻辑,遇到叶子节点将路径加入到结果集中。
    • 如果左或右子节点不为空,进入下一个递归
    • 递归完之后还需要进行回溯操作,因为path不能一直添加,还需要回退一个路径之后进入下一个路径。

递归代码如下:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func binaryTreePaths(root *TreeNode) []string {
	rlt := []string{}
	var traversal func(*TreeNode, []string)
	traversal = func(node *TreeNode, paths []string) {
		curVal := strconv.Itoa(node.Val)
        paths = append(paths, curVal)
		//左右子树为空
		if node.Left == nil && node.Right == nil {
			rlt = append(rlt, strings.Join(paths, "->"))
			return
		}

		if node.Left != nil {
 			traversal(node.Left, paths)
			paths = paths[:len(paths)-1] // 回溯
		}
		if node.Right != nil {
			traversal(node.Right, paths)
			paths = paths[:len(paths)-1] // 回溯
		}
	}

	traversal(root, []string{})

	return rlt
}

迭代法:
也可以使用栈来模拟前序遍历

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func binaryTreePaths(root *TreeNode) []string {
	type pair struct {
		node *TreeNode
		path string
	}
	rlt := []string{}
	stack := list.New()
	stack.PushBack(pair{root, strconv.Itoa(root.Val)})
	for stack.Len() > 0 {
		p := stack.Remove(stack.Back()).(pair)
		if p.node.Left == nil && p.node.Right == nil {
			rlt = append(rlt, p.path)
		}
		if p.node.Right != nil {
			stack.PushBack(pair{p.node.Right, p.path + "->" + strconv.Itoa(p.node.Right.Val)})
		}
		if p.node.Left != nil {
			stack.PushBack(pair{p.node.Left, p.path + "->" + strconv.Itoa(p.node.Left.Val)})
		}
	}

	return rlt
}

总结

求二叉树的所有路径问题需要注意回溯的问题, 递归与回溯操作应该放到同一个花括号内处理。

404. 左叶子之和

参考:代码随想录

思考

根据题目描述二叉树的左叶子定义为:节点A的左孩子不为空,并且节点A的左孩子的左右子节点都为空,则称节点A的左孩子为左叶子节点。
求左叶子节点则需要根据父节点来进行判断。即:if root.Left != nil && root.Left.Left == nil && root.Left.Right == nil {}

递归

使用递归求解需要使用后序遍历,因为是求左叶子之和就必须把左右孩子返回的结果进行相加。

  1. 确定递归函数的参数和返回值: func sumOfLeftLeaves(root *TreeNode) int,题目的函数就满足递归函数
  2. 递归的终止条件:当前的节点为空,或者节点的左右节点都为空。
  3. 单层递归的逻辑:遇到左叶子节点时,记录数值,然后通过递归求左子树的左叶子之和以及右子树的左叶子之和,最后进行相加

整体递归代码如下:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func sumOfLeftLeaves(root *TreeNode) int {
	if root == nil {
		return 0
	}
	if root.Left == nil && root.Right == nil {
		return 0
	}
	leftSum := sumOfLeftLeaves(root.Left)
	if root.Left != nil && root.Left.Left == nil && root.Left.Right == nil {
		leftSum = root.Left.Val
	}
	rightSum := sumOfLeftLeaves(root.Right)

	return leftSum + rightSum
}

迭代法

使用迭代法处理的逻辑也是类似的,比较容易

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func sumOfLeftLeaves(root *TreeNode) int {
	// 前序遍历
	rlt := 0
	stack := list.New()
	stack.PushBack(root)
	for stack.Len() > 0 {
		cur := stack.Remove(stack.Back()).(*TreeNode)
		if cur.Left != nil && cur.Left.Left == nil && cur.Left.Right == nil {
			rlt += cur.Left.Val
		}
		if cur.Right != nil {
			stack.PushBack(cur.Right)
		}
		if cur.Left != nil {
			stack.PushBack(cur.Left)
		}
	}

	return rlt
}

总结

通常情况下我们都是通过本节点的左右孩子判断本节点,但本题是通过父节点来进行判断的。

标签:node,Right,TreeNode,nil,第十七,算法,二叉树,return,Left
From: https://www.cnblogs.com/neilliu/p/16786297.html

相关文章

  • 视觉算法工程师招聘|杭州易思维科技
    3D视觉工坊致力于推荐最棒的工作机会,精准地为其找到最佳求职者,做连接优质企业和优质人才的桥梁。易思维简介易思维(杭州)科技有限公司坐落于浙江省杭州市,专注于工业智能视觉领......
  • 海量数据库的查询优化及分页算法方案
     随着“金盾工程”建设的逐步深入和公安信息化的高速发展,公安计算机应用系统被广泛应用在各警种、各部门。与此同时,应用系统体系的核心、系统数据的存放地――数据库也随着......
  • 视觉三维重建核心算法讲解和代码实现(sfm构建稀疏地图和mvs构建稠密地图)
    视觉三维重建=定位定姿 +稠密重建 +surfacereconstruction+纹理贴图。三维重建技术是计算机视觉的重要技术之一,基于视觉的三维重建技术通过深度数据获取、预处理、......
  • 如何跟随有三从零进阶中级CV算法工程师
    再过一段时间可能就要迈出成为真正的创业者的关键一步了,有三AI夏季划再招一些学生作为人才储备,同时也是带一些人真正系统性成为深度学习CV算法工程师。文/编辑|言有三  ......
  • 【AutoML】进化算法如何用于自动模型搜索(NAS)
    大家好,欢迎来到专栏《AutoML》,在这个专栏中我们会讲述AutoML技术在深度学习中的应用,这一期讲述进化算法用于模型搜索的基本概念和流程。作者&编辑|言有三一直以来,网络结构......
  • go语言逆向技术之---恢复函数名称算法
    go语言是最近几年发展非常火的一种语言,它具备和C/C++一样的运行速度快的优点,同时又具备开发效率高,支持包管理机制高阶语言特点。其编译出来的二进制文件格式和C/C++一样运......
  • KMP算法中对于next数组构建的理解
     时间:2022/10/12 一.next数组原理的说明KMP算法一般用于解决字符串匹配的问题,在KMP算法出现之前,字符串匹配一般通过双层for循环的暴力方法解决,时间复杂度为O(......
  • 【杂谈】GAN对人脸图像算法产生了哪些影响?
    人脸图像是整个图像领域里面研究人员最多,应用最广的一个方向。GAN作为时下最新兴的深度学习模型,在人脸图像领域里已经颇有建树,今天咱们就聊聊GAN对人脸图像算法的一些主要影......
  • 【每周CV论文推荐】换脸算法都有哪些经典的思路?
    欢迎来到《每周CV论文推荐》。在这个专栏里,还是本着有三AI一贯的原则,专注于让大家能够系统性完成学习,所以我们推荐的文章也必定是同一主题的。人脸伪造/换脸算法目前在一定......
  • 【机器学习实战学习笔记】之 2 k-近邻算法
    本学习笔记参考书目《机器学习实战》第二章。本章所有本书对应代码及数据集下载请点击(​​下载链接​​)。本文中博主自己写的代码如有需要,请点击(​​下载链接​​)。目录​​......