• 二叉树的四种遍历方式以及中序后序、前序中序、前序后序、层序创建二叉树【专为力扣刷题而打造】


    博主介绍:

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


    前言

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


    前序遍历

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    中序遍历

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    后序遍历

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    层序遍历

    层序遍历肯定是一行一行的遍历,其思想就是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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    层序创建二叉树

    到了重点时刻了,以参考层序遍历为相反思维,首先对一维数组进行拆分成每一层节点,做出二维数组,之后根据遍历每一个孩子当作当前的根节点添加的它的左右孩子。此写法较为丑陋且性能较低,望耐心扣底。这里-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 { // 由于每次都是左右孩子所以一次需要跨两步j+=2
    			if sliNums[i][j] != -1 {
    				queue[count].Left = &TreeNode{Val: sliNums[i][j]}
    				queue = append(queue, queue[i-1].Left)
    			} else {
    				queue[count].Left = nil
    				queue = append(queue, queue[i-1].Left)
    			}
    			if sliNums[i][j+1] != -1 {
    				queue[count].Right = &TreeNode{Val: sliNums[i][j+1]}
    				queue = append(queue, queue[i-1].Right)
    			} else {
    				queue[count].Right = nil
    				queue = append(queue, queue[i-1].Right)
    			}
    			count++
    		}
    	}
    	return root
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    使用案例

    这里以力扣的简单题为例: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))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    测试结果

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

    前序中序创建二叉树

    这里参考力扣题解,思维比较简单,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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    中序后序创建二叉树

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

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    前序后序创建二叉树

    这里结果可能不唯一

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    参考链接:

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


  • 相关阅读:
    视频拉流推流技术梳理
    Lumiprobe 蛋白质标记研究方案
    2. 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml
    做软件测试是在浪费时间吗,有未来吗?
    设备互联(一)
    视觉语言模型能多好地看到图像细节? [单位:蒙纳士大学]
    智慧城市的前景:数字孪生技术在智慧城市中的应用前景
    Designing a Seamless Omnichannel Data Collection Strategy: A Step-by-Step Guide
    vue 概述
    Flutter:类功能索引(全)
  • 原文地址:https://blog.csdn.net/weixin_45765795/article/details/125521494