首页 > 其他分享 >代码随想录day13 || 树定义以及遍历

代码随想录day13 || 树定义以及遍历

时间:2024-07-29 13:39:08浏览次数:9  
标签:node 遍历 res 随想录 二叉树 day13 root 节点

二叉树定义和种类

二叉树是一种树形数据结构,其中每个节点最多有两个子节点,通常称为“左子节点”和“右子节点”。二叉树在计算机科学中有广泛的应用,比如表达式解析、排序算法、搜索算法等。

二叉树的定义

一个二叉树由一组节点组成,其中每个节点至多有两个子节点,分别称为左子节点和右子节点。二叉树可以是空的(没有节点),也可以是一个根节点加上两个分别是左子树和右子树的二叉树。

二叉树的种类

二叉树有多种不同的类型,每种类型都有其特定的性质和用途。常见的二叉树种类包括:

  1. 满二叉树(Full Binary Tree)

    • 每个节点要么是叶子节点,要么有两个子节点。
    • 满二叉树的所有叶子节点都在同一层次上。
  2. 完全二叉树(Complete Binary Tree)

    • 所有层都是完全填满的,除了最后一层。
    • 最后一层的节点尽可能靠左排列。
  3. 完美二叉树(Perfect Binary Tree)

    • 是一种特殊的满二叉树。
    • 所有内部节点都有两个子节点,所有叶子节点都在同一层次上。
  4. 平衡二叉树(Balanced Binary Tree)

    • 任意节点的左子树和右子树的高度差不超过1。
    • 例如:AVL树、红黑树。
  5. 二叉搜索树(Binary Search Tree, BST)

    • 对于每个节点,其左子树中所有节点的值都小于该节点的值,其右子树中所有节点的值都大于该节点的值。
    • 允许高效的查找、插入和删除操作。

二叉树的表示

二叉树可以通过多种方式表示,常见的表示方法包括:

  1. 链式存储

    • 每个节点包含一个数据元素和两个指针,分别指向左子节点和右子节点。
    type TreeNode struct {
        Val   int
        Left  *TreeNode
        Right *TreeNode
    }
    
  2. 数组表示

    • 完全二叉树可以用数组表示,根节点存储在索引1的位置(或0),对于索引为 (i) 的节点,其左子节点存储在索引 (2i)(或 (2i+1)),右子节点存储在索引 (2i+1)(或 (2i+2))。
    // 数组表示的二叉树
    tree := []int{1, 2, 3, 4, 5, 6, 7}
    

二叉树的遍历

二叉树的遍历是指按照某种顺序访问树中的每个节点。常见的遍历方法有:

  1. 前序遍历(Pre-order Traversal)
    • 访问根节点 -> 前序遍历左子树 -> 前序遍历右子树。
    func preorderTraversal(root *TreeNode) []int {
    	// 三种遍历顺序指的是根节点在“左右”两个节点中的顺序
    	// 前序遍历 根左右
    	// 中序遍历 左根右
    	// 后续遍历 左右根
    	var res []int
    	var traversal func(node *TreeNode)
    	traversal = func(node *TreeNode) {
    		if node == nil {
    			return
    		}
    		res = append(res,node.Val)
    		traversal(node.Left)
    		traversal(node.Right)
    	}
    	traversal(root)
    	return res
    }
    
    func preorderTraversal(root *TreeNode) []int {
    // 考虑使用迭代法解决 根左右,所以先遍历根,然后入栈右左,实现出栈顺序是左右
    
    	if root == nil {
    		return nil
    	}
    
    	st := list.New() // go包双向链表
    	var ans []int
    	st.PushBack(root)
    	for st.Len() > 0 {
    		node := st.Remove(st.Back()).(*TreeNode)
    
    		ans = append(ans, node.Val)
    		if node.Right != nil {
    			st.PushBack(node.Right)
    		}
    		if node.Left != nil {
    			st.PushBack(node.Left)
    		}
    	}
    	return ans
    }
    
  2. 中序遍历(In-order Traversal)
    • 中序遍历左子树 -> 访问根节点 -> 中序遍历右子树。
    func postorderTraversal(root *TreeNode) []int {
    	var res []int
    	traversal(root, &res)
    	return res
    }
    
    func traversal(root *TreeNode, res *[]int) {
    	if root == nil {
    		return
    	}
    	traversal(root.Left, res)
    	traversal(root.Right, res)
    	*res = append(*res, root.Val)
    }
    
    func inorderTraversal(root *TreeNode) []int {
    	// 中序迭代有点难理解的
    	if root == nil {
    		return nil
    	}
    
    	var res []int
    	st := list.New()
    	cur := root
    	for cur != nil || st.Len() > 0{
    		if cur != nil{
    			st.PushBack(cur)
    			cur = cur.Left
    		} else {
    			cur = st.Remove(st.Back()).(*TreeNode)
    			res = append(res, cur.Val)
    			cur = cur.Right
    		}
    	}
    	return res
    }
    
  3. 后序遍历(Post-order Traversal)
    • 后序遍历左子树 -> 后序遍历右子树 -> 访问根节点。
    func inorderTraversal(root *TreeNode) []int {
    	res := []int{}
    	traversal(root, &res)
    	return res
    }
    func traversal(root *TreeNode, res *[]int) {
    	if root == nil {
    		return
    	}
    	traversal(root.Left, res)
    	*res = append(*res, root.Val)
    	traversal(root.Right, res)
    }
    
    func postorderTraversal(root *TreeNode) []int {
    	// 思路,左右中,反转之后就是中右左, 那么和前序遍历zhong左右只是区别在于入栈顺序,然后最终将res翻转即可
    	if root == nil {
    		return nil
    	}
    
    	var res = []int{}
    	st := list.New()
    	st.PushBack(root)
    
    	for st.Len() > 0 {
    		node := st.Remove(st.Back()).(*TreeNode)
    		res = append(res, node.Val)
    		if node.Left != nil {
    			st.PushBack(node.Left)
    		}
    		if node.Right != nil {
    			st.PushBack(node.Right)
    		}
    	}
    
    	return reverse(res)
    }
    
    func reverse(l []int) []int {
    	left, right := 0, len(l) - 1
    	for left < right {
    		l[left], l[right] = l[right], l[left]
    		left++
    		right--
    	}
    
    	return l
    }
    
  4. 层次遍历(Level-order Traversal)
    • 按照层次从上到下、从左到右依次访问每个节点。
    func levelOrder(root *TreeNode) [][]int {
    	// 通过队列实现广度优先遍历
    	if root == nil {
    		return nil
    	}
    
    	var res [][]int
    	queue := list.New() // 同样使用go包双向链表实现队列,push = pushback pop = remove(front())
    	queue.PushBack(root)
    	size := 1
    	// 对于每一层的遍历,都要维护一个size变量,方便记录弹出元素个数
    	for queue.Len() > 0{
    		var (
    			count int
    			r []int
    		)
    		for i := 0; i < size; i++ { // 对于每一层的遍历次数
    			// 1, 插入节点元素值
    			node := queue.Remove(queue.Front()).(*TreeNode)
    			r = append(r, node.Val)
    
    			// 左右子节点入队, 顺序不能颠倒,队列先入先出
    			if node.Left != nil {
    				queue.PushBack(node.Left)
    				count++
    			}
    			if node.Right != nil {
    				queue.PushBack(node.Right)
    				count++
    			}
    		}
    		// 此时上一层已经遍历完成,将size赋值为下一层的节点数量
    		size = count
    		res = append(res, r)
    	}
    	return res
    }
    

其中前序遍历,中序遍历,后序遍历都是深度优先遍历,通常采用递归方法实现,层序遍历为广度优先遍历,采用队列方法实现

深度优先搜索(DFS)和广度优先搜索(BFS)

深度优先搜索(DFS)和广度优先搜索(BFS)是两种基本的图遍历算法,它们也常用于树的遍历。以下是它们的详细介绍:

深度优先搜索(DFS)

深度优先搜索是一种优先探索尽可能深的节点的遍历策略。它沿着每一个分支尽可能深地搜索,直到不能再继续为止,然后回溯并继续探索下一个分支。DFS可以使用递归或显式的栈来实现。

广度优先搜索(BFS)

广度优先搜索是一种优先探索离起始节点最近的节点的遍历策略。它按层次逐层访问节点,先访问完一层的所有节点,再继续访问下一层的节点。BFS通常使用队列来实现。

DFS:

  • 适用于需要遍历到某个特定深度的场景,例如解决迷宫问题、寻找路径等。
  • 常用于图的连通性检测、拓扑排序等。

BFS:

  • 适用于寻找最短路径的场景,例如在无权图中寻找两个节点之间的最短路径。
  • 常用于层次遍历、广度优先搜索树等。

标签:node,遍历,res,随想录,二叉树,day13,root,节点
From: https://www.cnblogs.com/zhougongjin55/p/18329899

相关文章