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

二叉树的遍历

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
}
posted @ 2022-10-10 23:11  neil_liu  阅读(30)  评论(0)    收藏  举报