1. /* 二叉树遍历框架 */
  2. void traverse(TreeNode root) {
  3. // 前序遍历
  4. traverse(root.left)
  5. // 中序遍历
  6. traverse(root.right)
  7. // 后序遍历
  8. }

快排
快速排序的逻辑是,若要对 nums[lo..hi] 进行排序,我们先找一个分界点 p,通过交换元素使得 nums[lo..p-1] 都小于等于 nums[p],且 nums[p+1..hi] 都大于 nums[p],然后递归地去 nums[lo..p-1] 和 nums[p+1..hi] 中寻找新的分界点,最后整个数组就被排序了。

void sort(int[] nums, int lo, int hi) {
    /****** 前序遍历位置 ******/
    // 通过交换元素构建分界点 p
    int p = partition(nums, lo, hi);
    /************************/

    sort(nums, lo, p - 1);
    sort(nums, p + 1, hi);
}
再说说归并排序的逻辑,若要对 nums[lo..hi] 进行排序,我们先对 nums[lo..mid] 排序,再对 nums[mid+1..hi] 排序,最后把这两个有序的子数组合并,整个数组就排好序了

void sort(int[] nums, int lo, int hi) {
    int mid = (lo + hi) / 2;
    sort(nums, lo, mid);
    sort(nums, mid + 1, hi);

    /****** 后序遍历位置 ******/
    // 合并两个排好序的子数组
    merge(nums, lo, mid, hi);
    /************************/
}

写二叉树的算法题,都是基于递归框架的,我们先要搞清楚 root 节点它自己要做什么,然后根据题目要求选择使用前序,中序,后续的递归框架。

100. 相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 :

输入:      1         1
          / \       / \
         2   3     2   3
        [1,2,3],   [1,2,3]
输出: true

思路
二叉树的问题,通常通过递归的思想来解决。如果两棵树都是空树,返回True,如果两棵树都不是空,那么分别判断两棵树的根节点的数值,左结点和右结点是否相同。
代码

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

101. 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

1
   / \
  2   2
 / \ / \
3  4 4  3

思路
这个题思路和100题有点类似,依然是递归的思想,首先判断头结点是否为空。然后将根节点的左右两个节点假设成两个独立的树,如果左右两个树都为空,返回True。然后看左子树的左结点和右子树的右结点、左子树的右结点和右子树的左结点是否相同,都相同返回True.
代码

class Solution(object):
    def isSymmetric(self, root):
        if root is None:
            return True
        return self.isSymmetricTree(root.left,root.right)
    def isSymmetricTree(self,left,right):
        if left is None and right is None:
            return True
        if left is None or right is None or left.val != right.val:
            return False
        return self.isSymmetricTree(left.left,right.right) and self.isSymmetricTree(left.right,right.left)

104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
示例:
给定二叉树 [3,9,20,null,null,15,7]

        3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。
思路
递归的方法,比较左边路径和右边路径哪边最长,选择最长的一边路径,加上root结点本身的长度。
代码

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

107. 二叉树的层次遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:给定二叉树 [3,9,20,null,null,15,7],

        3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层次遍历为:

[
  [15,7],
  [9,20],
  [3]
]

思路
一层层地输出result:[3],[9,20],[15,7],然后翻转result [::-1],返回[15,7],[9,20],[3]。如果不一层层输出,直接输出[3,9,20,15,7],然后翻转变成了[7,15,20,9,3]。
代码

class Solution(object):
    def levelOrderBottom(self, root):
        if root is None:
            return []

        result,current = [],[root]

        while current:
            next_level,vals = [], []
            for node in current:
                vals.append(node.val)
                if node.left:
                    next_level.append(node.left)
                if node.right:
                    next_level.append(node.right)
            current = next_level
            result.append(vals)
        return result[::-1]

108. 将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:

给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
      0
     / \
   -3   9
   /   /
 -10  5

思路
这也是个高频题,用递归的方法找到root结点,以及左子树和右子树。
代码

class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        def to_bst(nums, start, end):
            if start > end:
                return None
            mid = (start+end)//2
            node = TreeNode(nums[mid])
            node.left = to_bst(nums, start, mid-1)
            node.right = to_bst(nums, mid+1,end)
            return node
        return to_bst(nums, 0, len(nums) - 1)

110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 :
给定二叉树 [3,9,20,null,null,15,7]

        3
   / \
  9  20
    /  \
   15   7

返回 true
思路
利用104题中判断二叉树最大深度的函数,左子树和右子树的深度差小于等于1即为平衡二叉树。
代码

class Solution(object):
    def isBalanced(self, root):
        if root == None:
            return True
        elif abs(self.height(root.left)-self.height(root.right))>1:
            return False
        else:
            return self.isBalanced(root.left) and self.isBalanced(root.right)

    def height(self,root):
        if root == None:
            return 0
        else:
            return max(self.height(root.left),self.height(root.right))+ 1

111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
示例:
给定二叉树 [3,9,20,null,null,15,7],

        3
   / \
  9  20
    /  \
   15   7

返回它的最小深度 2.
思路
如果根节点不存在,返回0;然后找根节点的左右子节点,找到深度最小的值,然后加1。
有一个特殊情况,如果根节点只有一个结点,那么深度最小的值不是1,具体见下例。因为是到最近的叶子节点的深度。

例:           3
                \
                 4
               /   \
              5     6      这种情况下,最小深度不是1,而是3。

代码

class Solution(object):
    def minDepth(self, root):

        if root is None:
            return 0
        if root.left and root.right:
            return min(self.minDepth(root.left),self.minDepth(root.right))+1
        else:
            return max(self.minDepth(root.left),self.minDepth(root.right))+1

112. 路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
示例:
给定如下二叉树,以及目标和 sum = 22

                            5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2
思路
从上到下加起来,最后一个节点的值就等于sum-前面的和,且这个节点是叶子节点。
代码

class Solution(object):
    def hasPathSum(self, root, sum):

        if root is None:
            return False
        if root.left is None and root.right is None and root.val==sum:
            return True
        else:
            return self.hasPathSum(root.left,sum-root.val) or self.hasPathSum(root.right,sum-root.val)

226. 翻转二叉树

翻转一棵二叉树。

我们发现只要把二叉树上的每一个节点的左右子节点进行交换,最后的结果就是完全翻转之后的二叉树
这道题目比较简单,关键思路在于我们发现翻转整棵树就是交换每个节点的左右子节点,于是我们把交换左右子节点的代码放在了前序遍历的位置。
二叉树题目的一个难点就是,如何把题目的要求细化成每个节点需要做的事情

// 将整棵树的节点翻转
TreeNode invertTree(TreeNode root) {
    // base case
    if (root == null) {
        return null;
    }

    /**** 前序遍历位置 ****/
    // root 节点需要交换它的左右子节点
    TreeNode tmp = root.left;
    root.left = root.right;
    root.right = tmp;

    // 让左右子节点继续翻转它们的子节点
    invertTree(root.left);
    invertTree(root.right);

    return root;
}

示例:
输入:

            4
   /   \
  2     7
 / \   / \
1   3 6   9

输出:

            4
   /   \
  7     2
 / \   / \
9   6 3   1

思路
交换左右节点
代码

class Solution(object):
    def invertTree(self, root):
        if root is not None:
            root.left,root.right = self.invertTree(root.right),self.invertTree(root.left)
        return root

235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
树 - 图1
示例 1: 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8。输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2: 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4。 输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
思路
二叉搜索树(Binary Search Tree,BST),它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树
从上到下,如果这两个值都大于该节点,那么就往该节点的右节点继续查找;若都小于该节点,那么就往该节点的左节点继续查找,如果一个值等于该节点,那么该节点即为最近公共祖先。
代码

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        pointer = root
        while pointer:
            if q.val < pointer.val and p.val < pointer.val:
                pointer = pointer.left
            elif q.val > pointer.val and p.val > pointer.val:
                pointer = pointer.right
            else:
                return pointer

257. 二叉树的所有路径

给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:

输入:
   1
 /   \
2     3
 \
  5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

代码

class Solution(object):
    def binaryTreePaths(self, root):
        """
        :type root: TreeNode
        :rtype: List[str]
        """
        result = list()
        if root == None:
            return result
        if root.left == None and root.right == None:
            result.append(str(root.val))
            return result
        left = self.binaryTreePaths(root.left)
        for i in range(len(left)):
            result.append(str(root.val) + '->' + left[i])
        right = self.binaryTreePaths(root.right)
        for i in range(len(right)):
            result.append(str(root.val) + '->' + right[i])
        return result

404. 左叶子之和
计算给定二叉树的所有左叶子之和。
示例:

        3
   / \
  9  20
    /  \
   15   7
在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

代码

class Solution(object):
    def sumOfLeftLeaves(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        result = 0
        if not root:
            return 0      
        if root.left and not root.left.left and not root.left.right:
            result += root.left.val
        return result+self.sumOfLeftLeaves(root.left)+self.sumOfLeftLeaves(root.right)

437. 路径总和 III

给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1
返回 3。和等于 8 的路径有:
1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11

思路
借助一个函数:查找从该点出发得到目标值的路径的个数。
代码

class Solution(object):
    def pathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: int
        """
        if not root:
            return 0
        return self.pathSumFrom(root, sum) + self.pathSum(root.left, sum) + self.pathSum(root.right, sum)
    def pathSumFrom(self, node, sum):
        if not node:
            return 0
        return (1 if node.val == sum else 0) + self.pathSumFrom(node.left, sum - node.val) + self.pathSumFrom(node.right, sum - node.val)