IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 二叉树的四种遍历方式以及中序后序、前序中序、前序后序、层序创建二叉树【专为力扣刷题而打造】 -> 正文阅读

[数据结构与算法]二叉树的四种遍历方式以及中序后序、前序中序、前序后序、层序创建二叉树【专为力扣刷题而打造】

作者:recommend-item-box type_blog clearfix

博主介绍:

我是了 凡 微信公众号【了凡银河系】期待你的关注。未来大家一起加油啊~


前言

这里三种遍历方式不用过多介绍,相信学过数据结构的人都可以轻松使用递归方式进行遍历,非递归方式思想也是一致的。
根据前序中序、中序后序、前序后序均参考力扣题解所写,只有层序遍历是为了再力扣解题不方便所以才选择在本地解题,但是本地解题不能进行测试,使用其他三种创建方式又过于麻烦,所以想使用层序创建二叉树,思维比较简单供大家参考,有问题可以及时讨论。


前序遍历

由于是前序遍历所以根节点一定在前,遍历顺序为(根、左、右)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 前序遍历
func preorderTraversal(root *TreeNode) []int {
	nums := make([]int, 0)
	var dfs func(node *TreeNode)
	dfs = func(node *TreeNode) {
		if node != nil {
			nums = append(nums, node.Val) // 根
			dfs(node.Left) // 左
			dfs(node.Right) // 右
		}
	}
	dfs(root)
	return nums
}

中序遍历

由于是中序遍历所以根节点一定在中间,遍历顺序为(左、根、右)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 中序遍历
func inorderTraversal(root *TreeNode) []int {
	nums := make([]int, 0)
	var dfs func(node *TreeNode)
	dfs = func(node *TreeNode) {
		if node != nil {
			dfs(node.Left)  // 左
			nums = append(nums, node.Val) // 根
			dfs(node.Right) // 右
		}
	}
	dfs(root)
	return nums
}

后序遍历

由于是后序遍历所以根节点一定在后面,遍历顺序为(左、右、根)

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 后续遍历
func postorderTraversal(root *TreeNode) []int {
	nums := make([]int, 0)
	var dfs func(node *TreeNode)
	dfs = func(node *TreeNode) {
		if node != nil {
			dfs(node.Left)
			dfs(node.Right)
			nums = append(nums, node.Val)
		}
	}
	dfs(root)
	return nums
}

层序遍历

层序遍历肯定是一行一行的遍历,其思想就是BFS(像一滴水滴进水潭里的波纹一样一层一层的),这里使用队列不断的暂存下一个子孩子当作下一次的根节点进行遍历它的子孩子。

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 层序遍历
func levelOrder(root *TreeNode) [][]int {
	ret := [][]int{}
	if root == nil {
		return ret
	}
	q := []*TreeNode{root}
	for i := 0; len(q) > 0; i++ {
		ret = append(ret, []int{})
		p := []*TreeNode{}
		for j := 0; j < len(q); j++ {
			node := q[j]
			ret[i] = append(ret[i], node.Val)
			if node.Left != nil {
				p = append(p, node.Left)
			}
			if node.Right != nil {
				p = append(p, node.Right)
			}
		}
		q = p
	}
	return ret
}

层序创建二叉树

到了重点时刻了,以参考层序遍历为相反思维,首先对一维数组进行拆分成每一层节点,做出二维数组,之后根据遍历每一个孩子当作当前的根节点添加的它的左右孩子。此写法较为丑陋且性能较低,望耐心扣底。这里-1代表空值

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 层序创建二叉树
func sequenceCreation(nums []int) *TreeNode {
	if len(nums) == 0 {
		return nil
	}
	if len(nums) == 1 {
		return &TreeNode{nums[0], nil, nil}
	}
	// 先对数组进行分层分割
	sliNums := make([][]int, 0)
	count := 1
	for i := 0; i < len(nums); {
		cont := make([]int, 0)
		j := i
		for ; j < i+count; j++ {
			cont = append(cont, nums[j])
		}
		i = j
		sliNums = append(sliNums, cont)
		count *= 2
	}
	root := new(TreeNode)
	root.Val = sliNums[0][0]
	queue := []*TreeNode{root}
	count = 0
	for i := 1; i < len(sliNums); i++ {
		for j := 0; j < len(sliNums[i]); j += 2 {
			if sliNums[i][j] != -1 {
				queue[count].Left = &TreeNode{Val: sliNums[i][j]}
				queue = append(queue, queue[count].Left)
			} else {
				if queue[count] != nil {
					queue[count].Left = nil
					queue = append(queue, queue[count].Left)
				}
			}
			if sliNums[i][j+1] != -1 {
				queue[count].Right = &TreeNode{Val: sliNums[i][j+1]}
				queue = append(queue, queue[count].Right)
			} else {
				if queue[count] != nil {
					queue[count].Right = nil
					queue = append(queue, queue[count].Right)
				}
			}
			count++
		}
	}
	return root
}

使用案例

这里以力扣的简单题为例:104. 二叉树的最大深度
在这里插入图片描述

代码实现

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

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

func maxDepth(root *TreeNode) int {
	if root == nil {
		return 0
	}
	return max(maxDepth(root.Left), maxDepth(root.Right)) + 1
}

func main() {
	nums := []int{3, 9, 20, -1, -1, 15, 7}
	root := sequenceCreation(nums) // 这个函数就是上述的层序遍历,在这里就不重复粘贴了
	fmt.Println(maxDepth(root))
}

测试结果

在这里插入图片描述
结果正确

前序中序创建二叉树

这里参考力扣题解,思维比较简单,preorder切片的开始都是根节点,然后和inorder切片进行比较就可以找到左右孩子,不断向下重复比对就可以进行创建完成。

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 前序找根节点,中序找左右分界线
func pIBuildTree(preorder []int, inorder []int) *TreeNode {
	if len(preorder) == 0 {
		return nil
	}
	root := &TreeNode{preorder[0], nil, nil}
	i := 0
	for ; i < len(inorder); i++ {
		if inorder[i] == preorder[0] {
			break
		}
	}
	root.Left = pIBuildTree(preorder[1:len(inorder[:i])+1], inorder[:i])
	root.Right = pIBuildTree(preorder[len(inorder[:i])+1:], inorder[i+1:])
	return root
}

中序后序创建二叉树

和前序中序基本一致,只是这次是中序和后序比对

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 从中序后续创建二叉树
func iPBuildTree(inorder []int, postorder []int) *TreeNode {
	idxMap := map[int]int{}
	for i, v := range inorder {
		idxMap[v] = i
	}
	var build func(int, int) *TreeNode
	build = func(inorderLeft, inorderRight int) *TreeNode {
		// 无剩余节点
		if inorderLeft > inorderRight {
			return nil
		}

		// 后序遍历的末尾元素即为当前子树的根节点
		val := postorder[len(postorder)-1]
		postorder = postorder[:len(postorder)-1]
		root := &TreeNode{Val: val}

		// 根据 val 在中序遍历的位置,将中序遍历划分成左右两颗子树
		// 由于我们每次都从后序遍历的末尾取元素,所以要先遍历右子树再遍历左子树
		inorderRootIndex := idxMap[val]
		root.Right = build(inorderRootIndex+1, inorderRight)
		root.Left = build(inorderLeft, inorderRootIndex-1)
		return root
	}
	return build(0, len(inorder)-1)
}

前序后序创建二叉树

这里结果可能不唯一

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

// 根据前序后续构建二叉树
func constructFromPrePost(preorder []int, postorder []int) *TreeNode {
	// 递归结束条件
	if len(preorder) == 0 {
		return nil
	}
	// 只有一个元素时,为叶子节点。这一步在889不可省略
	if len(preorder) == 1 {
		return &TreeNode{Val: preorder[0]}
	}

	// 根节点
	root := &TreeNode{Val: preorder[0]}
	// 在后序序列中寻找和前序序列根节点下一个节点相等的位置
	for pos, node := range postorder {
		if node == preorder[1] {
			// 构建左子树
			root.Left = constructFromPrePost(preorder[1:pos+2], postorder[:pos+1])
			// 构建右子树
			root.Right = constructFromPrePost(preorder[pos+2:], postorder[pos+1:len(postorder)-1])
			break
		}
	}
	return root
}

参考链接:

创作不易,点个赞吧!
如果需要后续再看点个收藏!
如果对我的文章有兴趣给个关注!
如果有问题,可以关注公众号【了凡银河系】点击联系我私聊。


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:13:18  更:2022-07-04 23:14:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:29:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码