经典算法之树

本章节主要介绍基于树一些常见的操作和算法。

二叉树

class TreeNode(object):
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

二叉树中序遍历

# 递归版本
class Solution(object):
    def inorderTraversal(self, root):
        if root is None:
            return []
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
# 非递归版本
class Solution:
    def inorderTraversal(self, root):
        # 注意:根节点为空,直接返回空列表
        if not root:
            return []
        stack = []
        res = []
        while root or stack:
            # 一直向左子树走,每一次将当前节点保存到栈中
            if root:
                stack.append(root)
                root = root.left
            # 当前节点为空,证明走到了最左边,从栈中弹出节点加入结果数组
            # 开始对右子树重复上述过程。
            else:
                cur = stack.pop()
                res.append(cur.val)
                root = cur.right
        return res

    def preorderTraversal(self, root):
        if not root:
            return []
        stack = [root]
        res = []
        while stack:
            # 根节点出栈
            node = stack.pop()
            # 将根节点值加入结果
            res.append(node.val)
            # 右子树入栈
            if node.right:
                stack.append(node.right)
            # 左子树入栈
            if node.left:
                stack.append(node.left)
        return res

    def postorderTraversal(self, root):
        # 注意:根节点为空,直接返回空列表
        if not root:
            return []
        stack = []
        res = []
        while root or stack:
            while root:
                # 当前节点入栈
                stack.append(root)
                # 如果当前节点有左子树,继续向左子树找
                if root.left:
                    root = root.left
                # 如果当前节点无左子树,在右子树继续找
                else:
                    root = root.right
            # 跳出循环的条件是 root 为空,那当前栈顶元素为叶子节点。
            # 弹出栈顶元素,并加入结果数组
            cur = stack.pop()
            res.append(cur.val)
            if stack and stack[-1].left == cur:
                root = stack[-1].right
            # 否则证明当前栈顶元素无左右子树,那当前的栈顶元素弹出。
            else:
                root = None
        return res


二叉树的插入

def insert_tree(root, data):
    tmp_node = root
    if root is None:
        return root
    while tmp_node:
        if tmp_node.val > data:
            if tmp_node.left is None:
                tmp_node.left = TreeNode(data, None, None)
                break
            else:
                tmp_node = tmp_node.left
        else:
            if tmp_node.right is None:
                tmp_node.right = TreeNode(data, None, None)
                break
            else:
                tmp_node = tmp_node.right
    return root

获取二叉树最大深度

class Solution(object):
    def maxDepth(self, root):
        if root is None:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

二叉树最小深度

class Solution(object):
    def minDepth(self, root):
        from collections import deque
        if root is None:
            return 0
        queue = deque()
        queue.append(root)
        depth = 0
        last = root
        while queue:
            top = queue.popleft()
            if top.left is not None:
                queue.append(top.left)
                n_last = top.left
            if top.right is not None:
                queue.append(top.right)
                n_last = top.right
            if top.left is None and top.right is None:
                return depth + 1
            if top == last and queue:
                last = n_last
                depth += 1
        return depth + 1

二叉树的翻转

interview_2.png

def mirror_tree(root):
    if root is None:
        return None
    root.left = mirror_tree(root.left)
    root.right = mirror_tree(root.right)
    tmp = root.left
    root.left = root.right
    root.right = tmp
    return root

二叉树是否相同

class Solution(object):
    def isSameTree(self, p, q):
        if p is None and q is None:
            return True
        elif p is None or q is None:
            return False
        elif p.val != q.val:
            return False
        return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

判断二叉树是否轴对称

image.png

class Solution(object):
    def check_same_tree(self, R1, R2):
        if R1 is None and R2 is None:
            return True
        elif R1 is None or R2 is None:
            return False
        elif R1.val != R2.val:
            return False
        return self.check_same_tree(R1.left, R2.right) and self.check_same_tree(R1.right, R2.left)

    def isSymmetric(self, root):
        return self.check_same_tree(root, root)

这个问题十分巧妙,本来是一个树的判断,如果你的思维紧固到一个树上,那么就比较难做,但是当成两个树判断,这个题的难度一下子就的降低了不少。

二叉树的广度优先遍历

def breadth_first_traversal(root):
    res = list()
    queue = deque()
    queue.append(root)
    while queue:
        top = queue.popleft() # 深度popleft->pop
        res.append(top.val)
        if top.left is not None:
            queue.append(top.left)
        if top.right is not None:
            queue.append(top.right)
    return res

按层遍历二叉树

def level_traversal(root):
    t_set = list()
    res = list()
    q = deque()
    q.append(root)
    last = root
    level = 0
    while q:
        top = q.popleft()
        t_set.append(top.val)
        if top.left is not None:
            q.append(top.left)
            n_last = top.left
        if top.right is not None:
            q.append(top.right)
            n_last = top.right
        while top == last and q:
            last = n_last
            res.append(t_set[:])
            del(t_set[:])
            level += 1
    res.append(t_set[:])
    return res, level

二叉树子树的判断

bool isSubtree(TreeNode *T1, TreeNode *T2) {
         bool result  = false;
        if (T2 == nullptr) {
            return true;
        }
        if (T1 == nullptr) {
            return false;
        }
        if (T1->val == T2->val) {
             result = dp(T1,T2);
        }
        if (!result) {
          result =  isSubtree(T1->left,T2);
        }
        if (!result) {
            result =  isSubtree(T1->right,T2);
        }
        return result;
    }
bool dp (TreeNode *T1, TreeNode *T2) {

        if (T1 != nullptr && T2!=nullptr && T1->val == T2->val) {
            return dp(T1->left,T2->left) & dp (T1->right,T2->right);
        }
        if (T1 == nullptr && T2 == nullptr) {
            return true;
        }
        return false;
    }

二叉树是否平衡

bool IsBalanced(BinaryTreeNode* pRoot)
{
    if(pRoot== NULL)
        return true;

    int nLeftDepth = TreeDepth(pRoot->m_pLeft);
    int nRightDepth = TreeDepth(pRoot->m_pRight);
    int diff = nRightDepth-nLeftDepth;

    if (diff>1 || diff<-1)
        return false;

    return IsBalanced(pRoot->m_pLeft)&&IsBalanced(pRoot->m_pRight);
}

根据不同的顺序还原二叉树

从前序和中序还原二叉树

class Solution(object):
    def buildTree(self, preorder, inorder):
        def myBuildTree(preorder_left, preorder_right, inorder_left, inorder_right):
            if preorder_left > preorder_right:
                return None
            # 前序遍历中的第一个节点就是根节点
            preorder_root = preorder_left
            # 在中序遍历中定位根节点
            inorder_root = index[preorder[preorder_root]]
            # 先把根节点建立出来
            root = TreeNode(preorder[preorder_root])
            # 得到左子树中的节点数目
            size_left_subtree = inorder_root - inorder_left
            # 递归地构造左子树,并连接到根节点
            # 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
            root.left = myBuildTree(preorder_left + 1, preorder_left + size_left_subtree, inorder_left,
                                    inorder_root - 1)
            # 递归地构造右子树,并连接到根节点
            # 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
            root.right = myBuildTree(preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1,
                                     inorder_right)
            return root
        n = len(preorder)
        # 构造哈希映射,帮助我们快速定位根节点
        index = {element: i for i, element in enumerate(inorder)}
        return myBuildTree(0, n - 1, 0, n - 1)

从中序与后序遍历序列构造二叉树

class Solution(object):
    def buildTree(self, inorder, postorder):
        def helper(in_left, in_right):
            # 如果这里没有节点构造二叉树了,就结束
            if in_left > in_right:
                return None
            # 选择 post_idx 位置的元素作为当前子树根节点
            val = postorder.pop()
            root = TreeNode(val)
            # 根据 root 所在位置分成左右两棵子树
            index = idx_map[val]
            # 构造右子树
            root.right = helper(index + 1, in_right)
            # 构造左子树
            root.left = helper(in_left, index - 1)
            return root
        # 建立(元素,下标)键值对的哈希表
        idx_map = {val: idx for idx, val in enumerate(inorder)}
        return helper(0, len(inorder) - 1)

检查平衡二叉树

class Solution(object):
    def isBalanced(self, root):
        def get_depth(root):
            if root is None:
                return 0
            return max(get_depth(root.left), get_depth(root.right)) + 1
        if root is None:
            return True
        if abs(get_depth(root.left) - get_depth(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(
                root.right):
            return True
        return False

二叉树转单链表

image.png

要求

  1. 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  2. 展开后的单链表应该与二叉树 先序遍历 顺序相同。
class Solution(object):
    def flatten(self, root):
        def pre_order(root):
            if root is None:
                return []
            return [root] + pre_order(root.left) + pre_order(root.right)

        pre_list = pre_order(root)
        for i in range(1, len(pre_list)):
            pre, cur = pre_list[i-1], pre_list[i]
            pre.left = None
            pre.right = cur

二叉树的和

image.png

遍历二叉树的所有路径,且使用每个路径组成一个数字,最后求所有的数字的和。

class Solution(object):
    def sumNumbers(self, root):
        if not root:
            return 0
        total = 0
        import collections
        nodeQueue = collections.deque([root])
        numQueue = collections.deque([root.val])
        while nodeQueue:
            node = nodeQueue.popleft()
            num = numQueue.popleft()
            left, right = node.left, node.right
            if not left and not right:
                total += num
            else:
                if left:
                    nodeQueue.append(left)
                    numQueue.append(num * 10 + left.val)
                if right:
                    nodeQueue.append(right)
                    numQueue.append(num * 10 + right.val)
        return total
# 面试 
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×