题目:
面试tips:
1. 询问是否可以使用双端队列 (看后面思路就可知为什么要问这个)
思路:
时复和空复都为O(n)
思路一:利用双端队列。总体思想是利用二叉树层序遍历(二叉树的层序遍历就是用队列dq,且从左往右每一层存入队列中),但这里的双端队列使用在path中,即存储路径path时,遇到奇数列,从dq中读出来的节点进行尾插入path;遇到偶数列,从dq中读出来的节点进行头插入。
例如:层序遍历对上述二叉树(因为是层序遍历,因此都是从左往右读取的)
第一层读取: 1 。 因为是奇数层,则存入path尾插,则[1]
第二层读取:2 3 。因为是偶数层,则存入path头插,则[3 2](注意先读取先插)
第三层读取:4 5 6 7 。因为是奇数层,则存入path尾插,则[4 5 6 7](注意先读取先插)
第二层读取:8 9 10 11 12 13 14 15 。因为是偶数层,则存入path头插,则[15 14 13 12 11 10 9 8]
其实本质上思路一是伪Z字形遍历,因为其在第一次pop节点时还是层序的,只是加入路径path时对奇偶列的加入一个是尾插一个是头插。是leetcode上提供的思路。
而思路二当在pop节点时就已经时Z字形遍历了。是剑指offer提供的思路。
思路二:利用两个栈。分别称为当前栈,下一栈。分析:若当前栈存储的是奇数行的节点时,则处理时将其左右孩子按顺序存入下一栈中(这样下一次就可以输出右左孩子);若当前栈存储的是偶数行的节点时,则处理时将其右左孩子按顺序存入下一栈(这样下一次就可以输出左右孩子)。
具体分析:
当前栈:第一行。-> 弹出节点1,将其左右孩子存入下一栈23。当前栈为空了则将下一栈作为当前栈,当前栈作为下一栈。result[1]
当前栈:此时节点为23,是第二行。->弹出节点3,将其右左孩子存入下一栈76,弹出节点2,将其右左孩子存入下一栈54,则下一栈为7654。当前栈为空了则将下一栈作为当前栈,当前栈作为下一栈。result[32]
当前栈:result[4567]
````直至两个栈都为空。
代码实现:
思路一:
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def arr2tree(arr, index):
# 满二叉树数组格式构造二叉树
# 构造arr[index]的二叉树
# 满二叉树数组格式: 依照满二叉树的结构,无论是否为空都会将数组填上
if index >= len(arr) or arr[index] == None:
return None
root = TreeNode(val = arr[index])
left = arr2tree(arr, 2 * index + 1)
right = arr2tree(arr, 2 * index + 2)
root.left = left
root.right = right
return root
def zigzagLevelOrder(root):
# 使用双端队列。
if not root:
return []
dq = deque([root]) # 这个作用只是层序遍历的迭代法
result = []
sign = True # 表明当前是奇数行
while dq:
size = len(dq)
path = deque() # 这里使用双端队列
while size:
# 之所以说其是伪Z字形遍历 就是其取出来时还是层序遍历的从左往右,只是对结果集根据奇数列or偶数列头插或尾插
node = dq.popleft()
if sign:
path.append(node.val)
else:
path.appendleft(node.val)
# 下面都是层序遍历的套路 左右孩子往dq中存
if node.left:
dq.append(node.left)
if node.right:
dq.append(node.right)
size -= 1
result.append(list(path))
sign = not sign
return result
if __name__ == '__main__':
arr = [3, 9, 20, None, None, 15, 7]
root = arr2tree(arr, 0)
print(zigzagLevelOrder(root))
# [[3], [20, 9], [15, 7]]
思路二:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def arr2tree(arr, index):
# 满二叉树数组格式构造二叉树
# 构造arr[index]的二叉树
# 满二叉树数组格式: 是指首先按层序遍历顺序,且二叉树的非空节点的左右孩子(尽管为空)都会打印出来,空节点的左右孩子则不打印
if index >= len(arr) or arr[index] == None:
return None
root = TreeNode(val = arr[index])
left = arr2tree(arr, 2 * index + 1)
right = arr2tree(arr, 2 * index + 2)
root.left = left
root.right = right
return root
def zigzagLevelOrder(root) :
# 利用两个栈解决本题
# 将奇数层1放入第一个栈中,且放左右孩子在第二个栈中(则下一次就可逆序打印)
# 偶数层0时(第二个栈)放右左孩子
if not root:
return []
# 定义一个二维数组 分别是两个栈
stack = [[],[]]
result = []
path = []
# 初始化奇数层、偶数层
current, next_lay = 1, 0 # 先处理第一层 故当前层是奇数层
stack[current].append(root)
while stack[current] or stack[next_lay]:
# 只要奇数层or偶数层还有节点 说明未遍历完毕
node = stack[current].pop()
path.append(node.val)
if current:
# 如果是奇数层则先放左孩子再放右孩子(因为下一层要逆序)
if node.left:
stack[next_lay].append(node.left)
if node.right:
stack[next_lay].append(node.right)
else:
# 若是偶数层则先放右孩子
if node.right:
stack[next_lay].append(node.right)
if node.left:
stack[next_lay].append(node.left)
if not stack[current]:
# 如果当前层空了则更换当前层
result.append(path[:])
path = []
current = 1 - current # 当前层从奇数层更换成偶数层,偶数层更换为奇数层
next_lay = 1 - next_lay # 下一层从偶数层更换成奇数层,奇数层更换为偶数层
return result
if __name__ == '__main__':
arr = [3, 9, 20, None, None, 15, 7]
root = arr2tree(arr, 0)
print(zigzagLevelOrder(root))
# [[3], [20, 9], [15, 7]]
参考资料:
1. 《剑指offer》
2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台