文章目录
leetcode - 二叉树的遍历
1. 前序遍历
前序遍历:实际上理解为根节点在什么时候访问,
前序遍历访问顺序: 跟节点 -> 左子节点 -> 右子节点
预期结果:A,B,D,G,H,C,E,I,F
递归方法:
思路
跟节点 -> 左子节点 -> 右子节点
- 输出当前节点的
- 递归调用左子节点
- 递归调用右子节点
时间复杂度:O(n)
空间复杂度:O(n)
/// 前序遍历 - 递归
/// - Parameter node: node
func preorderTree(node: TreeNode<Any>?){
if node == nil { return }
print(node?.value ?? "")
preorderTree(node: node?.left)
preorderTree(node: node?.right)
}
栈方式:
思路1
跟 左 右
- 创建栈
- 将根节点赋值给node
- 如果node不为空
- 输出node
- 如果右子节点不为空 入栈
- node = node.left
- 左子节点 == nil 时 判断栈的大小==0 则遍历结束
- 如果栈中有值 则弹出栈顶元素复制给node 继续循环
- 重复执行 3~5步骤
时间复杂度:O(n)
空间复杂度:O(n)
func preorderTree_Stack(root: TreeNode<Any>?){
if root == nil { return }
let stack = Stack<TreeNode<Any>>() // 栈
var node = root
while true {
if node != nil {
print(node?.value ?? "")
if let rNode = node?.right {
stack.push(rNode)
}
node = node?.left
}else if stack.size == 0{
return // 栈为空时
}else{
node = stack.pop() // 弹出栈顶元素
}
}
}
思路2
- 直接将root入栈
- 每次取出 栈顶元素 进行访问
- 判断是否有 左右子节点并入栈
- 重复执行 2~3 直至栈为空时 结束遍历 时间复杂度:O(n)
空间复杂度:O(n)
func preorderTree_Stack01(root: TreeNode<Any>?){
if root == nil { return }
let stack = Stack<TreeNode<Any>>() // 栈
stack.push(root!)
while stack.isEmpty() == false {
let node = stack.pop()
print(node?.value ?? "")
if let rNode = node?.right{
stack.push(rNode)
}
if let lNode = node?.left{
stack.push(lNode)
}
}
}
2. 中序遍历
前序遍历:实际上理解为根节点在什么时候访问,
中序遍历访问顺序: 左子节点 -> 跟节点 -> 右子节点
预期结果:G,D,H,B,A,E,I,C,F
递归方法:
思路
- 递归调用左子节点
- 输出当前节点的
- 递归调用右子节点
时间复杂度:O(n)
空间复杂度:O(n)
func inorderTree(node: TreeNode<Any>?){
if node == nil { return }
inorderTree(node: node?.left)
print(node?.value ?? "")
inorderTree(node: node?.right)
}
栈方式:
思路
- 将root赋值给node节点
- 循环执行以下操作: (退出条件 栈中的元素为空)
- 如果node不为空
- 将node入栈
- node 向左走, node = node.left
- 如果node为空
- 将栈中的元素弹出 node
- 输出 node的元素
- 将node向右走, (如果有值会 继续执行向左右的操作)
- 如果node不为空
时间复杂度:O(n)
空间复杂度:O(n)
func inorderTree_Stack(root: TreeNode<Any>?){
if root == nil { return }
let stack = Stack<TreeNode<Any>>()
var node = root
while node != nil {
if let n = node{
stack.push(n)
node = node?.left
}else if stack.size == 0{
return
}else{
node = stack.pop()
print(node?.value ?? "")
node = node?.right
}
}
}
3. 后序遍历
后序遍历:实际上理解为根节点在什么时候访问,
前序遍历访问顺序: 左子节点 -> 右子节点 -> 跟节点
预期结果:G,H,D,B,I,E,F,C,A
递归方法:
思路
- 递归调用左子节点
- 递归调用右子节点
- 输出当前节点的
时间复杂度:O(n)
空间复杂度:O(n)
func postorderTree(node: TreeNode<Any>?){
if node == nil { return }
postorderTree(node: node?.left)
postorderTree(node: node?.right)
print(node?.value ?? "")
}
栈方式:
思路
遍历顺序 左右根 入栈顺序 根右左 则 出栈顺序 左右根
- 将root 入栈
- 创建一个 上一次弹出的元素prev 作为记录
- 循环执行以下操作
- 查看栈顶元素 peek 查看 仅仅是查看并不是出栈
- 如果是叶子节点 或者 上一次访问的是栈顶元素的的子节点
- 弹出栈顶元素 并记录为上一次弹出的元素 prev
- 进行访问输出
- 如果不是叶子节点
- 将右子节点入栈
- 将左子节点入栈 继续循环
时间复杂度:O(n)
空间复杂度:O(n)
func postorderTree_Stack(root: TreeNode<Any>?){
if root == nil { return }
let stack = Stack<TreeNode<Any>>()
var prev:TreeNode<Any>?
stack.push(root!)
while stack.size != 0 {
let top = stack.peek() // 查看栈顶元素是什么 没有删除
if top?.isLeaf() == true || ( prev != nil && prev?.parent == top){
// 是否是叶子节点 || 上一次访问的是top的子节点
prev = stack.pop() // 出栈
print(prev?.value ?? "")
}else{
if let rNode = top?.right {
stack.push(rNode)
}
if let lNode = top?.left {
stack.push(lNode)
}
}
}
}
4. 层序遍历
层序遍历访问顺序:顾名思义 一层一层的去遍历
预期结果:A,B,C,D,E,F,G,H,I
队列方式:
思路
利用 队列 先进先出
的性质
- 创建队列 并将根节点加入队列中
- 循环执行以下条件:(如果队列中的元素为空时 退出循环)
- 将队列中的队顶元素node出队
- 输出队列的值
- 判断node的左子节点是否为空 不为空加入队列
- 判断node的右子节点是否为空 不为空加入队列
时间复杂度:O(n)
空间复杂度:O(n)
func levelTree_Queue(root: TreeNode<Any>?){
if root == nil { return }
var node = root
let queue = Queue<TreeNode<Any>>()
queue.enQeueu(node!)
while queue.isEmpty() == false {
node = queue.deQeueu()
print(node?.value ?? "")
if node?.left != nil {
queue.enQeueu(node?.left)
}
if node?.right != nil {
queue.enQeueu(node?.right)
}
}
}
5. 扩展
1. 二叉树的高度
递归方式:
思路:
- root 为空 直接返回 0
- 递归寻找左右子树中 最大的高度 +1 就可以了
func levelCount(root: TreeNode<Any>?) -> Int{
if root == nil { return 0 }
return max(levelCount(root: root?.left), levelCount(root: root?.right)) + 1
}
迭代方式
思路:
利用层序遍历
- 创建队列 并将根节点加入队列中
- 默认高度为0
- levelCount 当前遍历的层数 有多少个节点
- 循环执行以下条件:(如果队列中的元素为空时 退出循环)
- levelCount 减1
- 将队列中的队顶元素node出队
- 输出队列的值
- 判断node的左子节点是否为空 不为空加入队列
- 判断node的右子节点是否为空 不为空加入队列
- 当levelCount == 0 时 证明当前层的节点全部遍历完成
- 这时队列中的数量则为下一层节点的数量 levelCount = queue.size
- 二叉树的高度 height + 1
func levelCount_queue(n: TreeNode<Any>?) -> Int{
if n == nil { return 0 }
var node = n
let queue = Queue<TreeNode<Any>>()
queue.enQeueu(node!)
var height = 0
var levelCount = 1
while queue.isEmpty() == false {
node = queue.deQeueu()
levelCount -= 1
print(node?.value ?? "")
if node?.left != nil {
queue.enQeueu(node?.left)
}
if node?.right != nil {
queue.enQeueu(node?.right)
}
if levelCount == 0 {
levelCount = queue.size
height+=1
}
}
return height
}