Binary Tree

二叉树 80~90%和递归相关的的面试问题都在考binary tree

定义:一个树的每个节点不超过2个child nodes

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None 
        #self.parent = None

创建二叉树

root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(10)
... 

可见这也可以写成一个recursive的定义

其实,在SQL中的query plan tree也是一个 tree

Tree中寻找一个节点

如果3个节点,不做任何限制,顺序可以有3!个。但如果对排列做一个规定,左子树一定要在右子树之前,那么就是3!/2!=3种可能。

分别是 前序、中序、后序 这个“前中后“是针对current node (or current root of the subtree)的。

Pre-Order

把自己放在最前面打印,然后左,然后右

把结果存进list

Time Complexity O(c*n)=O(n) c=3

Space Complexity O(height) O(logn)=<hight<=O(n)

In-Order

先左节点,然后处理自己,最后右子树 所以调整上面code的位置,4、5换一下

Post-Order

面试Trick: Base Case: 通常,空节点是base case (但也有些时候叶子是base case)

常见Binary Tree类型

所有的问题都可以简化成一个问题, ask for boolean: 是否BST,是否BBT or ask for value: max, integer of sth. 因为:

  • 每层node具备的性质,传递的值和下一层的性质 往往一致,很容易定义recursive rule

  • Base case (generally): null pointer under the leaf node

每层遵循同样的逻辑,比如 getheight的谁高取谁然后+1

Balanced Binary Tree

The depth difference between the left and the right subtrees of every node differ by 1 or less. 不满足的节点不一定是root。反例:大雁似的左右两撇。

所以要特别注意,如果一开始没有BBT,那么不能assume h=logn,因为此时应该是O(n),糖葫芦状。

Complete Binary Tree

最后一层以上的都满,最后一层没有泡泡

  • complete binary tree一定是balanced binary tree

  • 好处是有很好的locality,可以很简单的用array等物理连续的存储 做level order traversal时有紧凑的表示 Left Child Node Index = Parent Node Index * 2 +1 Right Child Node Index = Parent Node Index * 2 +2

Binary Search Tree

每一个node的左子树的所有node值比node小,右子树的所有值比node大

  • 好处1是in-order print时是ascending order

  • 好处2是方便查找,binary search似的,知道search的时候该往左还是往右

  • 也可以存duplicated value

Bottom Up (从下往上返值 灵魂三问)

  1. what do we expect from the left child/ right child?

  2. what do you want to do in the current layer?

  3. what do you want to return to your parent (same as 1)

以下这几个都是Time O(n) Space O(h)

求二叉树的高度

求二叉树的Size

求二叉树的value

求二叉树的left child总数

Max difference in 左子树和右子树

其实更好的做法是把两个global封装到一个class里,用函数调用reference指向object的方式去维护这两个global值。这是一个工程上更好的做法

Find minimum Depth

From the root down to the nearest leaf node

Base Case不是None的例子

Longest Ascending Path Binary Tree

Lowest Common Ancestor 最小公共祖先

假设两个数pq一定存在。那么只有2种情况,

  • 在某一个root的左右两边找到了p和q

  • 自己是root,另一个值在自己下,返回自己

一系列经典

是否Balanced Binary Tree

但因为调用get_height太多次,这不是最优解。

时间复杂度的分析

recursion tree画出来后每一个node的时间复杂度之和。单独分析

  • Best Case:退化成单链表的tree,第一次算left和right时就return False, O(n) 下面的isBalanced就没有执行,只有root上的那一对getHeight

  • Worst Case: 刚好是个Balanced Tree,必须每个node都要算一遍isBalanced O(nlogn)

空间复杂度的分析

  • Worst Case:退化成单链表的tree, O(n)

  • Best Case:刚好是个Balanced Tree O(logn)

更好的,改一下get_height 让找到不平衡时提前终止, Time Complexity O(n)

Symmetric Binary Tree

四叉树,在上面code的基础上加一个or就行。

时间复杂度:

Worst Case: 假如input的左右两个tree都是balanced,那么每层分别1, 4, 16, 64, 256...个tree node, 总共有h=log2nh=log_2n 层,最后一层的时间复杂度是 O(4log2n)=O(n2)O(4^{log_2n})=O(n^2)

Best Case: 假如input不是balanced,极端情况是两个linked list,此时的recursion tree是一叉树,时间复杂度是O(n)

Tweaked Identical Binary Trees

Invert a Binary Tree

Binary Tree Upside Down

Base Case不是None的例子

Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left node that shares the same parent node) or empty, flip it upside down and turn it into a tree where the original right nodes turned into left leaf nodes. Return the new root.

根变成了左孩子的右孩子(line8),根的左孩子变成了根,根的右孩子变成了左孩子的左孩子(line 7)

这和linked list的题目,reverse a singly linked list有点像。

1—> 2 —> 3 —> 4 —> None

cur. next. next =cur (line7&8)

cur.next = None (line9&10)

那么对于这个tree来说,root.right=root.left root.left=root

Time: O(n)

Space: O(h)

Reverse Binary Tree Upside Down

Examples

1

/ \

2 5

/ \

3 4

is reversed to

3

/ \

2 4

/ \

1 5

Binary Tree Diameter

Queue/Stack + Tree

用stack 区别只是 在访问每个节点第几次的时候打印它

如果是先序,第一次就打印;如果是中序,第二次打印;如果是后序,第三次打印

stack里存Node和第几次访问该Node

按照Level打印二叉树(广度搜索)

维护一个队列,q=[],root进,看是否有左右孩子,如果有,deque,左右孩子enque,依此类推。同时还要注意每一层的空行 方法有很多,比如可以建立一个临时队列,q1是当前队列,q2是下一层的队列。q1打印完后,如果q2有值,把值给q1,q2接收下一行。如果q2是空,结束。

Time: O(n)

Space: O(max(len(q)))

这个思路也可以用来解minDepth的题

或者,使用一个queue,做一个counter记录需要打印几次。

面试题目 Suppose a tree is stored on the disk, and each node may have a large number of fan-out. We only have limited size of memory. How can we access the tree in level order?

用preorder traversal模拟level order 假设root是2,在level=1的时候打印。

再followup 如果来回从左到右-从右到左,zigzag打印?

——必须用2个queue,再加一个层数的变量

Get Keys In Binary Tree Layer By Layer Zig-Zag Order

Top Down (从上往下传值)

  1. 参数传递信息,用全局变量记录结果

  2. 通常不需要返值

  3. 思维更简单,但代码不如bottom up简洁

  4. 如果需要输出完整path,top down更实用 但是如果依赖subtree的结果,用bottom up 如果我们认为bottom up是post-order,那么top down就可以认为是preorder 即先做完自己这层的事情再做children的

求二叉树的高度

如果在上面的代码加一个__init__, 把self.result=0放进init去,那么,每次调用时,上一次的result值并没有重置,每次需要加一个 s=Solution,创建一个新的object。

求二叉树的最大值

Path Sum to Target

Solution 1: Top Down

变种

Time: O(n)

Space: O(hight)

Solution 2: bottom up

Last updated