题目:
面试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 dequeclass TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef arr2tree(arr, index):# 满二叉树数组格式构造二叉树# 构造arr[index]的二叉树# 满二叉树数组格式: 依照满二叉树的结构,无论是否为空都会将数组填上if index >= len(arr) or arr[index] == None:return Noneroot = TreeNode(val = arr[index])left = arr2tree(arr, 2 * index + 1)right = arr2tree(arr, 2 * index + 2)root.left = leftroot.right = rightreturn rootdef 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 -= 1result.append(list(path))sign = not signreturn resultif __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 = valself.left = leftself.right = rightdef arr2tree(arr, index):# 满二叉树数组格式构造二叉树# 构造arr[index]的二叉树# 满二叉树数组格式: 是指首先按层序遍历顺序,且二叉树的非空节点的左右孩子(尽管为空)都会打印出来,空节点的左右孩子则不打印if index >= len(arr) or arr[index] == None:return Noneroot = TreeNode(val = arr[index])left = arr2tree(arr, 2 * index + 1)right = arr2tree(arr, 2 * index + 2)root.left = leftroot.right = rightreturn rootdef 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 resultif __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)官网 - 全球极客挚爱的技术成长平台