Tag Archives: 递归

LeetCode Search a 2D Matrix II

240. Search a 2D Matrix II 240. Search a 2D Matrix II

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted in ascending from left to right.
  • Integers in each column are sorted in ascending from top to bottom.

Example:

Consider the following matrix:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.

Given target = 20, return false. 240. Search a 2D Matrix II


LeetCode Search a 2D Matrix基础上改动了一点点,但是难度增加不少。这一次,每行和每列都是递增的,但是这一行的行首元素和上一行的行末元素没有大小关系,这种矩阵有点像从左上角递增到右下角的一个曲面,想象一下。 一种比较笨的办法就是对每一行或者每一列二分搜索,复杂度为O(mlgn)或O(nlgm)。 参考这位大神的介绍,有很多厉害的算法,具体看原博客,这里简单给出其前两个算法的C++实现。 算法1相当于对角二分搜索,是线性复杂度的。搜索从矩阵右上角开始,遇到比当前元素大时,递增行;遇到比当前元素小时,递减列;直到找到相等元素,或者到达矩阵左下角。最坏情况是从右上角走到了左下角,复杂度为O(m+n)。 这种思路的代码如下:

class Solution {
public:
    bool searchMatrix(vector<vector<int> >& matrix, int target)
    {
        if (matrix.size() == 0)
            return false;
        int m = matrix.size(), n = matrix[0].size();
        if (n == 0)
            return false;
        int i = 0, j = n – 1;
        while (i < m && j >= 0) {
            while (i < m && matrix[i][j] < target)
                ++i;
            if (i >= m)
                return false;
            while (j >= 0 && matrix[i][j] > target)
                –j;
            if (j < 0)
                return false;
            if (matrix[i][j] == target)
                return true;
        }
        return false;
    }
};

本代码提交AC,用时129MS。
算法2利用了分治法的思路。每次取矩阵中心元素,把矩阵分成4个小份。如果target大于中心元素,则左上角部分可以全部舍弃掉,因为矩阵的行列都是递增的,左上角元素肯定全部小于中心元素,不必再找。此时可以递归的在剩余的3个小矩阵里查找。比如下图中心元素为9,如果要找26,则可以递归的在其右上角、左下角和右下角三个小矩阵中查找。

时间复杂度公式为T(n)=3T(n/2)+c,c为target和中心元素比较的时间。使用主方法可以算到T(n)=O(n1.58)。
分治法思路代码如下:

class Solution {
public:
    bool quadPart(vector<vector<int> >& matrix, int left, int top, int right, int bottom, int target)
    {
        if (left > right || top > bottom)
            return false;
        if (target < matrix[left][top] || target > matrix[right][bottom])
            return false;
        int midrow = left + (right - left) / 2, midcol = top + (bottom - top) / 2;
        if (target == matrix[midrow][midcol])
            return true;

        //if (target > matrix[midrow][midcol]) { // 抛弃左上角
        //	return quadPart(matrix, left, midcol + 1, midrow - 1, bottom, target) || // 右上角
        //		quadPart(matrix, midrow + 1, top, right, midcol - 1, target) || // 左下角
        //		quadPart(matrix, midrow, midcol, right, bottom, target); // 右下角
        //}
        //else {
        //	return quadPart(matrix, left, midcol + 1, midrow - 1, bottom, target) || // 右上角
        //		quadPart(matrix, midrow + 1, top, right, midcol - 1, target) || // 左下角
        //		quadPart(matrix, left, top, midrow, midcol, target); //左上角
        //}

        if (target > matrix[midrow][midcol]) { // 抛弃左上角
            return quadPart(matrix, left, midcol + 1, midrow, bottom, target) || // 右上角
                quadPart(matrix, midrow + 1, top, right, midcol, target) || // 左下角
                quadPart(matrix, midrow + 1, midcol + 1, right, bottom, target); // 右下角
        }
        else {
            return quadPart(matrix, left, midcol, midrow - 1, bottom, target) || // 右上角
                quadPart(matrix, midrow, top, right, midcol - 1, target) || // 左下角
                quadPart(matrix, left, top, midrow - 1, midcol - 1, target); //左上角
        }
    }
    bool searchMatrix(vector<vector<int> >& matrix, int target)
    {
        if (matrix.size() == 0)
            return false;
        int m = matrix.size(), n = matrix[0].size();
        if (n == 0)
            return false;
        return quadPart(matrix, 0, 0, m - 1, n - 1, target);
    }
};

本代码提交AC,用时168MS。
注意代码中注释掉的部分是错误代码,因为其在右下角或者左上角搜索时可能会进入死循环。比如要在上图搜索30,当搜索到右下角深色部分的小矩阵时,中心元素是17,本该搜索更小的矩阵30,但是根据注释代码中第一个if搜索右下角的代码,新的矩阵(右下角)的左上角坐标还是17,因为其坐标是(midrow, midcol),无论递归多少次,右下角的矩阵没有变小,所以进入了死循环。
正确的方法是把涉及到中点的两条边分配到不同的小矩阵里,必须使得每个小矩阵的左上角或右下角坐标有变化。

LeetCode Populating Next Right Pointers in Each Node II

117. Populating Next Right Pointers in Each Node II

Given a binary tree

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Follow up:

  • You may only use constant extra space.
  • Recursive approach is fine, you may assume implicit stack space does not count as extra space for this problem.

Example 1:

Input: root = [1,2,3,4,5,null,7]
Output: [1,#,2,3,#,4,5,7,#]
Explanation: Given the above binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level.

Constraints:

  • The number of nodes in the given tree is less than 6000.
  • -100 <= node.val <= 100

本题在LeetCode Populating Next Right Pointers in Each Node的基础上,扩展了二叉树的形式,并不要求二叉树一定是满二叉树(题中的完全二叉树),而是一个一般的二叉树。 这个时候就会遇到一些问题,比如下面的二叉树。

         1
       /  \
      2    3
     / \    \
    4   5    7
   /          \
  8            9

如果还按原来的先左孩子后右孩子的DFS,则第一次深度遍历完之后是这样的:

         1
       /  \
      2 -> 3
     / \    \
    4 ->5    7
   /          \
  8            9

此时要找8的next时就比较麻烦,因为在上一层到5时断了,没有5->7,所以找不到9号节点。所以需要修改为先对右孩子遍历,再对左孩子遍历,这样父亲一层的next就都完备了。 另外代码中还统一了child是左孩子和右孩子的代码,都是在父亲一层不断找next,直到找到合法next或者NULL。不过针对右孩子时,需要注意,为了不让其next指向左兄弟,增加了tmp != parent的约束。 完整代码如下:

class Solution {
public:
    void dfs(TreeLinkNode* child, TreeLinkNode* parent)
    {
        if (child == NULL)
            return;
        if (parent) {
            TreeLinkNode* tmp = parent;
            while (tmp) {
                if (tmp != parent && tmp->left && child != tmp->left) {
                    child->next = tmp->left;
                    break;
                }
                if (tmp->right && child != tmp->right) {
                    child->next = tmp->right;
                    break;
                }
                tmp = tmp->next;
            }
        }
        dfs(child->right, child);
        dfs(child->left, child);
    }
    void connect(TreeLinkNode* root) { dfs(root, NULL); }
};

本代码提交AC,用时32MS。

二刷。简单易懂但是比较啰嗦的代码:

class Solution {
public:
	void point2right(Node* par, Node* cur) {
		if (cur == NULL)return;

		if (par != NULL) {
			if (cur == par->left) {
				if (par->right != NULL) {
					cur->next = par->right;
				}
				else {
					Node* tmp = par->next;
					while (tmp != NULL) {
						if (tmp->left != NULL) {
							cur->next = tmp->left;
							break;
						}
						else if (tmp->right != NULL) {
							cur->next = tmp->right;
							break;
						}
						tmp = tmp->next;
					}
				}
			}
			else {
				// 和上面一段是一样的
				Node* tmp = par->next;
				while (tmp != NULL) {
					if (tmp->left != NULL) {
						cur->next = tmp->left;
						break;
					}
					else if (tmp->right != NULL) {
						cur->next = tmp->right;
						break;
					}
					tmp = tmp->next;
				}
			}
		}
		point2right(cur, cur->right);
		point2right(cur, cur->left);
	}
	Node* connect(Node* root) {
		if (root == NULL || (root->left == NULL && root->right == NULL))return root;
		point2right(NULL, root);
		return root;
	}
};

这一题的关键是要先递归右子树,再递归左子树。本代码提交AC,用时16MS。

LeetCode Validate Binary Search Tree

98. Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node’s key.
  • The right subtree of a node contains only nodes with keys greater than the node’s key.
  • Both the left and right subtrees must also be binary search trees.

Example 1:

    2
   / \
  1   3

Input: [2,1,3]
Output: true

Example 2:

    5
   / \
  1   4
     / \
    3   6

Input: [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

验证一棵二叉树是否是二叉搜索树。二叉搜索树树需要满足左孩子小于等于根节点,根节点小于等于右孩子,本题中是严格的小于关系,所以二叉搜索树的中序遍历是严格递增的。一种解法是对二叉树中序遍历,然后检查遍历结果是否是严格递增的,这种解法很简单,不赘述了。
另外一种解法是直接用递归判断,代码如下。我们在每一次递归时,都判断当前节点是否大于最小值,且小于最大值,同时把当前节点的值递归带入。代码中没有求min和max的过程,但是每次调用的时候能正确检查,里面的奥秘需要自己体会,不太好讲。
举个例子吧,下图中节点6违反了BST的规则,虽然13所在子树是BST,但是6小于根节点10,所以不是BST。本代码是怎样判断出来的呢,首先根节点传入右孩子的约束是[10,max],15传入左孩子的约束是[10,15],13传入左孩子的约束是[10,13],所以6并不属于[10,13],不属于BST。

    10
   / \
  5   15
     /  \
    13   20
   /  \
  6   14
class Solution {
public:
    bool helper(TreeNode* root, long long minVal, long long maxVal)
    {
        if (root == NULL)
            return true;
        if (root->val <= minVal || root->val >= maxVal)
            return false;
        return helper(root->left, minVal, root->val) && helper(root->right, root->val, maxVal);
    }
    bool isValidBST(TreeNode* root)
    {
        long long minVal = LLONG_MIN, maxVal = LLONG_MAX;
        return helper(root, minVal, maxVal);
    }
};

本代码提交AC,用时13MS。

二刷。原理相同,更好理解的代码:

class Solution {
public:
	bool isBST(TreeNode* root, int &max_val, int &min_val) {
		if (root == NULL) {
			max_val = INT_MIN;
			min_val = INT_MAX;
			return true;
		}
		// 以root为根的子树的最大和最小值
		max_val = root->val;
		min_val = root->val;
		if (root->left == NULL && root->right == NULL) {
			return true;
		}
		if (root->left) {
			int left_max = INT_MIN, left_min = INT_MAX;
			bool left_tree = isBST(root->left, left_max, left_min);
			if (!left_tree || left_max >= root->val)return false;
			min_val = min(min_val, left_min);
		}
		
		if (root->right) {
			int right_max = INT_MIN, right_min = INT_MAX;
			bool right_tree = isBST(root->right, right_max, right_min);
			if (!right_tree || right_min <= root->val)return false;
			max_val = max(max_val, right_max);
		}
		return true;
	}

	bool isValidBST(TreeNode* root) {
		int max_val = INT_MIN, min_val = INT_MAX;
		return isBST(root, max_val, min_val);
	}
};

本代码提交AC,用时12MS。

LeetCode Find Mode in Binary Search Tree

LeetCode Find Mode in Binary Search Tree Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than or equal to the node’s key.
  • The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
  • Both the left and right subtrees must also be binary search trees.
For example: Given BST [1,null,2,2],
   1
    \
     2
    /
   2
return [2]. Note: If a tree has more than one mode, you can return them in any order. Follow up: Could you do that without using any extra space? (Assume that the implicit stack space incurred due to recursion does not count).
找到二叉搜索树中所有的众数,如果有多个众数,都要给出。并且Follow up限制空间复杂度为O(1)。 很有意思的一个题,如果把二叉搜索树当成一棵普通的树,并且没有O(1)空间复杂度的限制,则在遍历时,可以使用hash统计每个数出现的频率,然后取最高频率的数。代码如下: [cpp] class Solution { public: void work(unordered_map<int, int>& hash, TreeNode* root, int& modCnt) { if (root == NULL)return; ++hash[root->val]; modCnt = max(modCnt, hash[root->val]); work(hash, root->left, modCnt); work(hash, root->right, modCnt); } vector<int> findMode(TreeNode* root) { unordered_map<int, int> hash; int modCnt = INT_MIN; work(hash, root, modCnt); vector<int> ans; unordered_map<int, int>::iterator it = hash.begin(); while (it != hash.end()) { if (it->second == modCnt)ans.push_back(it->first); ++it; } return ans; } }; [/cpp] 本代码提交AC,用时25MS。 如果使用二叉搜索树的特点并且考虑O(1)的空间复杂度,就有点复杂了。因为二叉搜索树的左孩子小于等于根节点,根节点小于等于右孩子,所以如果对二叉搜索树中序遍历的话,得到的序列就是一个非递减序列,也就是说是一个有序序列,那就好办多了。比如给定序列[1,2,2,3,4,4,5,6],要找出数组中的众数,并且限制O(1)空间复杂度,应该就不难了。 因为要O(1)空间复杂度,就不能用hash统计频率了,因为众数可能有多个,我们没法在一次遍历时就既找到最大的频率,又找到众数,所以可以采取两次遍历的策略。第一遍先找到最大的频率,上面的例子中是2(2和4都出现了两次);第二遍再把频率等于2的数值找出来,就是众数了。 对应到二叉搜索树中,就是对树中序遍历两次,第一次得到最大的频率以及众数的个数,第二遍再把所有众数都找出来。代码如下: [cpp] class Solution { private: vector<int> mods; int curVal = 0, curCnt = 0, modCnt = 0, maxCnt = 0; void handleValue(int v) { if (v != curVal) { curVal = v; curCnt = 0; } ++curCnt; if (curCnt > maxCnt) { // 第二次遍历时不会走此分支 maxCnt = curCnt; modCnt = 1; } else if (curCnt == maxCnt) { if (!mods.empty())mods[modCnt] = curVal; ++modCnt; } } void inorder(TreeNode* root) { if (root == NULL)return; inorder(root->left); handleValue(root->val); inorder(root->right); } public: vector<int> findMode(TreeNode* root) { inorder(root); mods.resize(modCnt); curCnt = 0; modCnt = 0; inorder(root); return mods; } }; [/cpp] 本代码提交AC,用时19MS。 参考:https://discuss.leetcode.com/topic/77335/proper-o-1-space]]>

LeetCode Serialize and Deserialize BST

LeetCode Serialize and Deserialize BST Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary search tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary search tree can be serialized to a string and this string can be deserialized to the original tree structure. The encoded string should be as compact as possible. Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.


本题要序列化和反序列化二叉搜索树。因为二叉搜索树属于二叉树,所以可以直接用LeetCode Serialize and Deserialize Binary Tree对普通二叉树的序列化和反序列化方法。但是二叉搜索树又有其特殊性,即左孩子小于等于根节点,根节点小于等于右孩子。所以如果知道树中有哪些数,通过插入的方法就可以构造好二叉搜索树。 首先还是根据层次遍历或者树的先序遍历得到树的序列化表示,那么序列化的第一个数就是根节点了。反序列化时,依次插入后续的节点构成一棵二叉搜索树就好了。但是数值插入的顺序不能乱,比如下图,虽然确定了根节点是1,如果先插入了4号节点,再插入3号节点,则反序列化得到的树就不是原来的树了。为了保证序列化到的数值顺序正确,可以使用层次遍历或者树的先序遍历。 完整代码如下: [cpp] class Codec { public: // Encodes a tree to a single string. string serialize(TreeNode* root) { if (root == NULL)return ""; string ans = to_string(root->val); if (root->left) ans += ","+serialize(root->left); if (root->right) ans += ","+serialize(root->right); return ans; } void insert(TreeNode* root, TreeNode* node) { if (node->val < root->val) { if (root->left == NULL)root->left = node; else insert(root->left, node); } else { if (root->right == NULL)root->right = node; else insert(root->right, node); } } // Decodes your encoded data to tree. TreeNode* deserialize(string data) { if (data == "")return NULL; TreeNode* root = NULL; for (int start = 0, end = 0; end <= data.size(); ++end) { if (data[end] == ‘,’ || end == data.size()) { TreeNode* node = new TreeNode(atoi(data.substr(start, end – start).c_str())); if (root == NULL)root = node; else insert(root, node); start = end + 1; } } return root; } }; [/cpp] 本代码提交AC,用时26MS。]]>

LeetCode Symmetric Tree

101. Symmetric Tree

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree [1,2,2,3,4,4,3] is symmetric:

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

But the following [1,2,2,null,3,null,3] is not:

    1
   / \
  2   2
   \   \
   3    3

Follow up: Solve it both recursively and iteratively.


本题要判断一棵二叉树是否是镜面对称的,镜面对称就像题中第一个例子,在树中间画一条线,则树的左右两边是镜面对称的。
无论是递归还是迭代的解法,镜面对称的树都需要满足以下三个条件:

  1. 左右节点的值相等
  2. 左节点的左孩子等于右节点的右孩子
  3. 左节点的右孩子等于右节点的左孩子

根据以上的三条规则,可以很容易写出递归版本的代码:

class Solution {
public:
    bool work(TreeNode* left, TreeNode* right)
    {
        if (left == NULL && right == NULL)
            return true;
        if (left == NULL || right == NULL)
            return false;
        bool cond1 = left->val == right->val;
        bool cond2 = work(left->left, right->right);
        bool cond3 = work(left->right, right->left);
        return cond1 && cond2 && cond3;
    }
    bool isSymmetric(TreeNode* root)
    {
        if (root == NULL)
            return true;
        return work(root->left, root->right);
    }
};

本代码提交AC,用时6MS。
迭代的版本中,因为每层都需要判断左右是否镜面对称,所以可以用类似层次遍历的方法,也相当于广度优先遍历。因为是左右两棵子树,所以需要用到两个队列,在节点入队的时候需要注意,因为以上的第2,3个条件,所以如果对左节点的孩子入队顺序是先左后右,那么对右节点的孩子入队顺序就应该是先右后左,这样出队时的顺序才能和2,3条件一致。完整代码如下:

class Solution {
public:
    bool isSymmetric(TreeNode* root)
    {
        if (root == NULL)
            return true;
        queue<TreeNode *> left, right;
        left.push(root->left);
        right.push(root->right);
        while (!left.empty() && !right.empty()) {
            TreeNode *l = left.front(), *r = right.front();
            left.pop();
            right.pop();
            if (l == NULL && r == NULL)
                continue;
            if (l == NULL || r == NULL)
                return false;
            if (l->val != r->val)
                return false;
            left.push(l->left);
            left.push(l->right);
            right.push(r->right);
            right.push(r->left);
        }
        return true;
    }
};

本代码提交AC,用时3MS。

二刷。迭代版本,用一个双端队列也可以解决,就是麻烦一点:

class Solution {
public:
	bool isSymmetric(TreeNode* root) {
		if (root == NULL)return true;
		deque<TreeNode*> q; // deque支持下标访问,而queue不支持
		q.push_back(root);
		while (!q.empty()) {
			int n = q.size();
			int i = 0, j = n - 1;
			bool curans = true;
			while (i < j) {
				if (q[i] != NULL && q[j] != NULL && q[i]->val == q[j]->val) {
					++i;
					--j;
				}
				else if (q[i] == NULL && q[j] == NULL) {
					++i;
					--j;
				}
				else {
					curans = false;
					break;
				}
			}
			if (!curans)return false;
			while (n--) {
				TreeNode* cur = q.front();
				if (cur != NULL) {
					q.push_back(cur->left);
					q.push_back(cur->right);
				}
				q.pop_front();
			}
		}
		return true;
	}
};

本代码提交AC,用时8MS。

LeetCode Unique Binary Search Trees II

95. Unique Binary Search Trees II

Given an integer n, generate all structurally unique BST’s (binary search trees) that store values 1 … n.

Example:

Input: 3
Output:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]
Explanation:
The above output corresponds to the 5 unique BST's shown below:

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

本题在LeetCode Unique Binary Search Trees的基础上,要求给出具体有哪些不同的二叉搜索树。方法是一样的DP,先枚举根节点r,然后把[1,n]分成两个部分,[1,…,r-1]递归的生成左子树,[r+1,…,n]递归的生成右子树。然后把左右子树接到r的左右两边。完整代码如下:

class Solution {
public:
    vector<TreeNode*> generate(int start, int end)
    {
        if (start > end)
            return vector<TreeNode*>({ NULL });
        if (start == end)
            return vector<TreeNode*>({ new TreeNode(start) });
        vector<TreeNode*> ans;
        for (int root = start; root <= end; ++root) {
            //TreeNode* rt = new TreeNode(root); //注意根节点不能放在这里,不能共用
            vector<TreeNode*> left = generate(start, root – 1);
            vector<TreeNode*> right = generate(root + 1, end);
            for (int l = 0; l < left.size(); ++l) {
                for (int r = 0; r < right.size(); ++r) {
                    TreeNode* rt = new TreeNode(root); //每棵树的根节点是独立的,要不然下一轮修改会影响上一轮结果
                    rt->left = left[l];
                    rt->right = right[r];
                    ans.push_back(rt);
                }
            }
        }
        return ans;
    }
    vector<TreeNode*> generateTrees(int n)
    {
        if (n < 1)
            return vector<TreeNode*>();
        return generate(1, n);
    }
};

本代码提交AC,用时26MS。

LeetCode Unique Binary Search Trees

Given n, how many structurally unique BST’s (binary search trees) that store values 1 … n?

Example:

Input: 3
Output: 5
Explanation:
Given n = 3, there are a total of 5 unique BST's:

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

给定n,问保存1~n的二叉搜索树共有多少种形式。注意要充分利用二叉搜索树的特点,左孩子小于等于根节点,根节点小于等于右孩子。如果trees[0,…,n-1]保存了0,…,n-1个节点的二叉搜索树的个数,现在要求trees[n]。n个节点,除了根节点,剩余n-1个节点,根据选取的根节点的不同,把n-1个节点分在了不同的左右子树中,以该节点为根的有效的二叉搜索树个数等于左右孩子有效的二叉搜索树个数乘积。
以3个点为例,如下图所示,如果选取1作为根节点,则左右孩子的节点数分别为0,2,所以可以有的二叉搜索树形式为trees[0]*trees[2];如果选取2作为根节点,则左右孩子的节点数分别为1,1,所以可以有的二叉搜索树形式为trees[1]*trees[1];同理3为trees[2]*trees[0]。

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

所以trees[n]=trees[0]*trees[n-1]+trees[1]*trees[n-2]+…+trees[n-1]*trees[1]。这其实就是卡特兰数

完整代码如下:

class Solution {
public:
    int numTrees(int n)
    {
        if (n <= 1)
            return 1;
        vector<int> trees(n + 1);
        trees[0] = 1;
        trees[1] = 1;
        for (int i = 2; i <= n; ++i) {
            for (int j = 0; j < i; ++j) {
                trees[i] += trees[j] * trees[i – j – 1];
            }
        }
        return trees[n];
    }
};

本代码提交AC,用时0MS。

LeetCode Lowest Common Ancestor of a Binary Search Tree

235. Lowest Common Ancestor of a Binary Search Tree

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

Given binary search tree:  root = [6,2,8,0,4,7,9,null,null,3,5]

Example 1:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Note:

  • All of the nodes’ values will be unique.
  • p and q are different and both values will exist in the BST.

给定一棵二叉搜索树和两个节点,问这两个节点的最近公共祖先(LCA)。LCA问题很久以前在hihoCoder上做过好多,用到一些比较高级的算法。 这个题的一个特点是二叉树是二叉搜索树,二叉搜索树的特点是左孩子小于等于根节点,根节点小于等于右孩子。不失一般性,令p<=q,所以如果p<=root<=q,则p,q分立root两边,则root就是p,q的LCA。如果p,q都小于root,则递归在root->left找。如果p,q都大于root,则递归在root->right找。 递归的算法很容易就实现了,代码如下:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    {
        if (p->val > q->val)
            swap(p, q);
        if (p->val <= root->val && q->val >= root->val)
            return root;
        if (p->val < root->val && q->val < root->val)
            return lowestCommonAncestor(root->left, p, q);
        if (p->val > root->val && q->val > root->val)
            return lowestCommonAncestor(root->right, p, q);
    }
};

本代码提交AC,用时32MS。

二刷。遍历二叉树,把p和q的祖先找出来,然后寻找公共前缀:

class Solution {
	void BSTSearch(TreeNode* root, TreeNode* p, vector<TreeNode*>& ancestors) {
		while (root->val != p->val) {
			ancestors.push_back(root);
			if (p->val > root->val)root = root->right;
			else root = root->left;
		}
		ancestors.push_back(root);
	}
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		vector<TreeNode*> pa, qa;
		BSTSearch(root, p, pa);
		BSTSearch(root, q, qa);
		int i = 0;
		for (; i < pa.size() && i < qa.size(); ++i) {
			if (pa[i] != qa[i])break;
		}
		return pa[i - 1];
	}
};

本代码提交AC,用时40MS。感觉还是第一种递归解法漂亮。

LeetCode Search in Rotated Sorted Array II

81. Search in Rotated Sorted Array II

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,0,1,2,2,5,6] might become [2,5,6,0,0,1,2]).

You are given a target value to search. If found in the array return true, otherwise return false.

Example 1:

Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true

Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false

Follow up:

  • This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates.
  • Would this affect the run-time complexity? How and why?

这一题在LeetCode Search in Rotated Sorted Array的基础上,增加了数组有重复元素的约束,其实相当于LeetCode Search in Rotated Sorted ArrayLeetCode Find Minimum in Rotated Sorted Array II的结合,搜索框架采用前者(即先找到有序的半个部分,再决定丢弃哪半个部分),边界判断采用后者(即遇到中间和边缘相等时,移动边缘)。代码上只是在前者代码的基础上增加了一个else分支。完整代码如下:

class Solution {
public:
    bool search(vector<int>& nums, int target)
    {
        int l = 0, r = nums.size() – 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target)
                return true;
            if (nums[mid] < nums[r]) {
                if (target > nums[mid] && target <= nums[r]) {
                    l = mid + 1;
                }
                else {
                    r = mid – 1;
                }
            }
            else if (nums[mid] > nums[r]) {
                if (target >= nums[l] && target < nums[mid]) {
                    r = mid – 1;
                }
                else {
                    l = mid + 1;
                }
            }
            else {
                –r;
            }
        }
        return false;
    }
};

本代码提交AC,用时12MS。因为多了一个else分支,算法最坏情况下,会达到O(n)的复杂度。

二刷。思路相同,使用递归实现,代码如下:

class Solution {
public:
	bool SearchRange(vector<int>& nums, int l, int r, int target) {
		if (l > r)return false;
		if (l == r)return nums[l] == target;

		int mid = l + (r - l) / 2;
		if (nums[mid] == target)return true;

		if (nums[l] < nums[mid]){
			if (target >= nums[l] && target < nums[mid])return SearchRange(nums, l, mid - 1, target);
			else return SearchRange(nums, mid + 1, r, target);
		}
		else if (nums[l] > nums[mid]) {
			if (target > nums[mid] && target <= nums[r])return SearchRange(nums, mid + 1, r, target);
			else return SearchRange(nums, l, mid - 1, target);
		}
		else {
			++l;
			return SearchRange(nums, l, r, target);
		}
	}
	bool search(vector<int>& nums, int target) {
		return SearchRange(nums, 0, nums.size() - 1, target);
	}
};

本代码提交AC,用时4MS。