Tag Archives: 递归

LeetCode Kth Smallest Element in a BST

230. Kth Smallest Element in a BST

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Example 1:

Input: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
Output: 1

Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
Output: 3

Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

Constraints:

  • The number of elements of the BST is between 1 to 10^4.
  • You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

给定一棵二叉搜索树,问树中第k小的元素是哪个。 因为BST的特点是左<=根<=右,所以对树中序遍历之后就得到升序序列,取出第k个元素即可。代码如下:

class Solution {
private:
    void inOrder(TreeNode* root, int& k, int& target)
    {
        if (k < 0)
            return;
        if (root->left)
            inOrder(root->left, k, target);
        –k;
        if (k == 0)
            target = root->val;
        if (root->right)
            inOrder(root->right, k, target);
    }
public:
    int kthSmallest(TreeNode* root, int k)
    {
        int target;
        inOrder(root, k, target);
        return target;
    }
};

本代码提交AC,用时26MS,时间复杂度是O(k)。
Follow up指出如果可以修改节点,怎样才能使得复杂度降低到O(height of tree),可以给每个节点增加一个属性rank:左子树节点个数,该属性的含义就是小于该节点的节点个数,也即当前节点在中序遍历中的位置(rank)。查找的过程就类似二分了,从根节点开始,如果k小于当前节点的rank,说明第k小的数在左子树;如果k大于当前节点的rank,说明第k小的数在右子树;如果k==rank,则当前节点就是第k小的数。这样的查找过程就是不断的在当前节点选择走左子树还是右子树,走过的节点最多是树的高度。
rank属性可以在建BST时完成。

LeetCode Most Frequent Subtree Sum

LeetCode Most Frequent Subtree Sum Given the root of a tree, you are asked to find the most frequent subtree sum. The subtree sum of a node is defined as the sum of all the node values formed by the subtree rooted at that node (including the node itself). So what is the most frequent subtree sum value? If there is a tie, return all the values with the highest frequency in any order. Examples 1 Input:

  5
 /  \
2   -3
return [2, -3, 4], since all the values happen only once, return all of them in any order. Examples 2 Input:
  5
 /  \
2   -5
return [2], since 2 happens twice, however -5 only occur once. Note: You may assume the sum of values in any subtree is in the range of 32-bit signed integer.
给定一棵二叉树,问频率最大的子树之和是哪个数。子树之和是指根节点及其子节点的和。 简单题,使用后序遍历把所有子树之和统计一遍,用Hash记录和与频率的关系,然后找出最大频率的和。 代码如下: [cpp] class Solution { private: int postOrder(TreeNode* root, unordered_map<int, int>& hash) { int sum = 0; if (root->left)sum += postOrder(root->left, hash); if (root->right)sum += postOrder(root->right, hash); sum += root->val; ++hash[sum]; return sum; } public: vector<int> findFrequentTreeSum(TreeNode* root) { if (!root)return vector<int>(); unordered_map<int, int> hash; postOrder(root, hash); int maxCnt = 0; for (auto it = hash.begin(); it != hash.end(); ++it)maxCnt = max(maxCnt, (*it).second); vector<int> ans; for (auto it = hash.begin(); it != hash.end(); ++it) { if ((*it).second == maxCnt)ans.push_back((*it).first); } return ans; } }; [/cpp] 本代码提交AC,用时19MS。]]>

LeetCode Beautiful Arrangement

LeetCode Beautiful Arrangement Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is constructed by these N numbers successfully if one of the following is true for the ith position (1 ≤ i ≤ N) in this array:

  1. The number at the ith position is divisible by i.
  2. i is divisible by the number at the ith position.
Now given N, how many beautiful arrangements can you construct? Example 1:
Input: 2
Output: 2
Explanation:
The first beautiful arrangement is [1, 2]:
Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1).
Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2).
The second beautiful arrangement is [2, 1]:
Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1).
Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1.
Note:
  1. N is a positive integer and will not exceed 15.

本题要求1~N共有多少个漂亮的排列。一个漂亮的排列A[]需要满足以下两个条件中的一个:
  1. A[i]能整除i
  2. i能整除A[i]
上面的规则中下标从1开始。 本题使用DFS解决,想象数组长度为N,每个位置上都可能填入1~N这N个数。咱们从第一个位置,尝试填入1~N,然后把visited置位;第二个位置,尝试填入除第一个已经填入的其他所有数字。每个位置尝试填入时,都需要至少满足以上两个条件中的一个。当所有位置都填满时,找到一个可行解。回退时,需要把visited相关位复位。 完整代码如下: [cpp] class Solution { void dfs(int step, const int& N, vector<int>& visited, int& ans) { if (step > N) { ++ans; return; } for (int i = 1; i <= N; ++i) { if (visited[i] == 0 && (i%step == 0 || step%i == 0)) { visited[i] = 1; dfs(step + 1, N, visited, ans); visited[i] = 0; } } } public: int countArrangement(int N) { vector<int> visited(N + 1, 0); int ans = 0; dfs(1, N, visited, ans); return ans; } }; [/cpp] 本代码提交AC,用时149MS。]]>

LeetCode Diameter of Binary Tree

543. Diameter of Binary Tree

Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

Example:
Given a binary tree

          1
         / \
        2   3
       / \     
      4   5    

Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

Note: The length of path between two nodes is represented by the number of edges between them.


本题要求一棵树的直径,树的直径的定义是树中任意两点间的最长距离。
任意两点间的最长路径有如下三种选择:

  1. 经过根节点的最长路径
  2. 在左子树中不经过根节点的最长路径
  3. 在右子树中不经过根节点的最长路径

其中第1种情况比较好处理,必须经过根节点,又必须是最长路径,则路径长度肯定是左右子树的最大高度之和+1(根节点)。第2、3种情况就更好处理了,就是以左右孩子为根递归计算最长路径。
代码如下:

class Solution {
private:
    int getHeight(TreeNode* root)
    {
        if (!root)
            return 0;
        int lh = getHeight(root->left);
        int rh = getHeight(root->right);
        return 1 + max(lh, rh);
    }

public:
    int diameterOfBinaryTree(TreeNode* root)
    {
        if (!root)
            return 0;
        //int ans = 1 + getHeight(root->left) + getHeight(root->right); // 节点数
        int ans = getHeight(root->left) + getHeight(root->right); // 路径数
        ans = max(ans, diameterOfBinaryTree(root->left));
        ans = max(ans, diameterOfBinaryTree(root->right));
        return ans;
    }
};

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

参考:http://www.geeksforgeeks.org/diameter-of-a-binary-tree/,这篇博客的路径是指节点数,而本题的路径是指边数,所以第12行不能再加1了。

以上代码递归的过程是从根节点往孩子节点递归,这样计算高层节点的高度时,其实已经计算过低层节点的高度了,后面递归到低层节点时,会重复计算。所以代码还可以优化,比如可以不断递归到叶子节点再返回,这样高层节点的高度就可以直接用低层节点的高度加1计算得到,可以省不少时间。

二刷。在求解路径的同时可计算高度,而且先递归到叶子,然后不断从往上走,代码如下:

class Solution {
public:
	int DiameterAndHeight(TreeNode* root, int &height) {
		if (root == NULL || (root->left == NULL && root->right == NULL)) {
			height = 0;
			return 0;
		}
		int left_height = 0, right_height = 0;
		int left_diameter = DiameterAndHeight(root->left, left_height);
		int right_diameter = DiameterAndHeight(root->right, right_height);
		height = max(left_height, right_height) + 1;
		int left_edge = root->left == NULL ? 0 : 1, right_edge = root->right == NULL ? 0 : 1;
		return max(max(left_diameter, right_diameter), left_height + right_height + left_edge + right_edge);
	}
	int diameterOfBinaryTree(TreeNode* root) {
		int height = 0;
		return DiameterAndHeight(root, height);
	}
};

本代码提交AC,用时8MS,快了不少。

LeetCode Minimum Absolute Difference in BST

LeetCode Minimum Absolute Difference in BST Given a binary search tree with non-negative values, find the minimum absolute difference between values of any two nodes. Example:

Input:
   1
    \
     3
    /
   2
Output:
1
Explanation:
The minimum absolute difference is 1, which is the difference between 2 and 1 (or between 2 and 3).
Note: There are at least two nodes in this BST.
求一棵二叉搜索树中任意两个节点数值之差的绝对值的最小值。 因为是BST,所以满足左子树<=根节点<=右子树。以根节点root为例,则最小的差可能有两个,一个是root减去左子树中的最大数,另一个是右子树中的最小数减去root,所以整体的最小值就是这两个最小值的最小值。 左子树中的最大数就是左子树中最右下端的叶子节点,右子树中的最小数就是右子树中最左下端的叶子节点。 然后递归的以左右孩子为根更新最小值。代码如下: [cpp] class Solution { public: int getMinimumDifference(TreeNode* root) { int l = INT_MIN, r = INT_MAX; int left_min = INT_MAX, right_min = INT_MAX; if (root->left) { TreeNode* left = root->left; while (left->right)left = left->right; left_min = min(root->val – left->val, getMinimumDifference(root->left)); } if (root->right) { TreeNode* right = root->right; while (right->left)right = right->left; right_min = min(right->val – root->val, getMinimumDifference(root->right)); } return min(left_min, right_min); } }; [/cpp] 本代码提交AC,用时19MS。 还有一种思路,因为是BST,所以其中序遍历的结果是升序的,得到有序数组之后,相邻两个数的差的最小值就是要求的结果。 实际上,我们可以不用完整求出其升序序列之后再计算差,只需要在每次中序遍历时,记住该节点的上一个节点,这样该节点的值减去上一节点的值就相当于有序数组中相邻两个数作差。 代码如下: [cpp] class Solution { private: long long m_nLast, m_nAns; void inorderTraverse(TreeNode* root) { if (!root)return; inorderTraverse(root->left); // 左 m_nAns = min(m_nAns, root->val – m_nLast); m_nLast = root->val; // 根 inorderTraverse(root->right); // 右 } public: Solution() { m_nLast = INT_MIN; m_nAns = INT_MAX; } int getMinimumDifference(TreeNode* root) { inorderTraverse(root); return m_nAns; } }; [/cpp] 本代码提交AC,用时16MS。注意因为可能出现x-INT_MIN导致int溢出,所以两个成员变量定义为long long。 参考:http://bookshadow.com/weblog/2017/02/26/leetcode-minimum-absolute-difference-in-bst/]]>

LeetCode Summary Ranges

228. Summary Ranges2

Given a sorted integer array without duplicates, return the summary of its ranges.

Example 1:

Input:  [0,1,2,4,5,7]
Output: ["0->2","4->5","7"]
Explanation: 0,1,2 form a continuous range; 4,5 form a continuous range.

Example 2:

Input:  [0,2,3,4,6,8,9] Output: ["0","2->4","6","8->9"] Explanation: 2,3,4 form a continuous range; 8,9 form a continuous range.28. Summary Ranges

给定一个数组,把数组汇总成若干个连续的区间,以字符串的形式给出。
方法是判断两个相邻的数之差是否为1,不是1则前面的构成一个区间,转换为字符串输出。判断前一个区间只有一个数还是有多个数的方法是区间的起止位置i,j是否相差1,如果是,则只有一个数,否则有多个数。
注意最后一个区间需要在while循环外部判断。代码如下:

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        int n = nums.size(), i = 0, j = 1;
        if (n == 0)
            return ans;
        while (j < n) {
            if (nums[j] – nums[j – 1] == 1)
                ++j;
            else {
                if (j – i == 1)
                    ans.push_back(to_string(nums[i]));
                else
                    ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
                i = j++;
            }
        }
        if (j – i == 1)
            ans.push_back(to_string(nums[i]));
        else
            ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
        return ans;
    }
};

本代码提交AC,用时0MS。
还有一种解法利用了二分查找的思路,很有意思,参考讨论区
假如给定的数组是[1,2,3,4,5,…,10000,20000],如果还是用上述方法,时间复杂度是O(n),至少需要遍历一遍数组。但是数组的前10000个数是严格有序且连续递增的,如果能把这一部分识别出来,直接转换为”1->10000″,则速度会大大提高。
二分查找的思路是,对于给定区间[a,…,b],假设其在数组中的下标起点分别为[i,…,j],则如果b-a==j-i,说明[a,b]之间是类似上面的[1,2,…,10000]的情况的,因为数组中没有重复元素,所以区间有j-i个元素,且元素最大值和最小值的差b-a也是j-i,说明他们是一个连续的有序区间,我们直接把这个区间转换为”a->b”。
否则如果j-i!=b-a,则取中点mid=(i+j)/2,递归在[i,mid]和[mid+1,j]进行。
有一种情况需要注意的是,分割出来的区间可能需要合并,比如[1,2,3,4,5,6,8],初始i[1,..,8]不满足b-a==j-i,所以递归在[1,2,3,4]和[5,6,8]进行。左边转换为了”1->4″,右边还需要递归,假设转换为了”5->6″和”8″,那么”1->4″和”5->6″是需要合并的。所以我们在插入”5->6″时,看看之前得到的区间”1->4″的end是否和当前区间”5->6″的start只差1,如果是,则需要合并,更新之前区间的end为现在要插入区间的end,变成了”1->6″。
完整代码如下:

class Solution {
private:
    struct Range {
        int start, end;
        Range(int s, int e)
            : start(s)
            , end(e){};
    };
    void add2Ans(int a, int b, vector<Range>& ranges)
    {
        if (ranges.empty() || ranges[ranges.size() – 1].end != a – 1) {
            ranges.push_back(Range(a, b));
        }
        else {
            ranges[ranges.size() – 1].end = b;
        }
    }
    void helper(vector<int>& nums, int start, int end, vector<Range>& ranges)
    {
        if (start == end || end – start == nums[end] – nums[start]) {
            add2Ans(nums[start], nums[end], ranges);
            return;
        }
        int mid = start + (end – start) / 2;
        helper(nums, start, mid, ranges); // 先左区间
        helper(nums, mid + 1, end, ranges); // 后右区间
    }

public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        if (nums.empty())
            return ans;
        vector<Range> ranges;
        helper(nums, 0, nums.size() – 1, ranges);
        for (const auto& r : ranges) {
            if (r.start == r.end)
                ans.push_back(to_string(r.start));
            else
                ans.push_back(to_string(r.start) + "->" + to_string(r.end));
        }
        return ans;
    }
};

本代码提交AC,用时0MS。
这个代码在数据有很多连续区间时,接近O(lgn)的复杂度。但是当数据全是不连续的时,比如[1,3,5,7,9],则只有到递归到最深层start==end(即针对单个数字)时,才得到一个区间,所以退化为O(n)的算法。
如果再follow up可能有重复元素时,上述二分查找的方法就不管用了,因为属于一个区间的不一定满足b-a==j-i,比如[1,2,2,2,3],b-a=2,而j-i=4。此时只能用第一种O(n)的方法:

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        if (nums.empty())
            return ans;
        int i = 0, j = 1, n = nums.size();
        while (j < n) {
            while (j < n && (nums[j] == nums[j – 1] || nums[j] – nums[j – 1] == 1))
                ++j; // 考虑重复元素
            if (j >= n)
                break;
            if (j – 1 == i)
                ans.push_back(to_string(nums[i]));
            else
                ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
            i = j++;
        }
        if (j – 1 == i)
            ans.push_back(to_string(nums[i]));
        else
            ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
        return ans;
    }
};

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

二刷。更加鲁邦容易理解的代码:

class Solution {
public:
	vector<string> summaryRanges(vector<int>& nums) {
		vector<vector<int>> ranges;
		int i = 0, n = nums.size();
		while (i < n) {
			int j = i + 1;
			while (j < n&&nums[j] == nums[j - 1] + 1)++j;
			ranges.push_back({ nums[i],nums[j - 1] });
			i = j;
		}
		vector<string> ans;
		for (int i = 0; i < ranges.size(); ++i) {
			if (ranges[i][0] == ranges[i][1]) {
				ans.push_back(to_string(ranges[i][0]));
			}
			else {
				ans.push_back(to_string(ranges[i][0]) + "->" + to_string(ranges[i][1]));
			}
		}
		return ans;
	}
};

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

LeetCode Path Sum III

LeetCode Path Sum III You are given a binary tree in which each node contains an integer value. Find the number of paths that sum to a given value. The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes). The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000. Example:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1
Return 3. The paths that sum to 8 are:
1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11

问二叉树中有多少条路径和等于sum的路径,这里的路径不一定要起于根节点,也不一定要终于叶子结点,但是必须从从上往下(从父亲到孩子)。 我一开始的解法是,对每个节点,保留从其所有父亲节点到该节点的路径和,每个节点都统计到当前节点的路径和等于sum的路径数。 代码如下: [cpp] class Solution { private: void dfs(TreeNode* root, vector<int> all, int target, int& ans) { vector<int> newAll = { root->val }; for (int i = 0; i < all.size(); ++i)newAll.push_back(root->val + all[i]); for (int i = 0; i < newAll.size(); ++i) { if (newAll[i] == target)++ans; } if (root->left)dfs(root->left, newAll, target, ans); if (root->right)dfs(root->right, newAll, target, ans); } public: int pathSum(TreeNode* root, int sum) { if (root == NULL)return 0; int ans = 0; dfs(root, {}, sum, ans); return ans; } }; [/cpp] 本代码提交AC,用时62MS。这种解法需要保存所有的路径和,且有vector的复制,效率较低。 我这种解法是以每个节点作为终点统计一下路径和,网上有另一种解法,以每个节点作为开始节点,统计以这个点开始的所有路径和是否等于sum,然后递归的在其子树进行统计。 对于每个节点,走到其孩子节点时,对应的sum需要更新。代码如下: [cpp] class Solution { private: int dfs(TreeNode* root, int sum) { if (root == NULL)return 0; int ans = 0; if (root->val == sum)++ans; ans += dfs(root->left, sum – root->val); ans += dfs(root->right, sum – root->val); return ans; } public: int pathSum(TreeNode* root, int sum) { if (root == NULL)return 0; return dfs(root, sum) + pathSum(root->left, sum) + pathSum(root->right, sum); } }; [/cpp] 本代码提交AC,用时32MS,效率更高。]]>

LeetCode Construct Binary Tree from Preorder and Inorder Traversal

105. Construct Binary Tree from Preorder and Inorder Traversal

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

For example, given

preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]

Return the following binary tree:

    3
   / \
  9  20
    /  \
   15   7

根据树的先序遍历和中序遍历结果,重构这棵树。 其实大学的时候就有这样的作业,如果在纸上做,还是很简单的,但要实现成代码,稍微有点难理清这里的关系。 我们先来看一个纸上的解题过程,假设有下面这样一棵树:

        3
     /     \
    9       20
  /  \     /   \
 1    2  15    17
  • 先序遍历结果为:3, 9, 1, 2, 20, 15, 17
  • 中序遍历结果为:1, 9, 2, 3, 15, 20, 17

首先我们可以根据先序遍历结果找到根节点3,然后在中序遍历中看看3所处的位置,那么3左边的就是左孩子节点,右边的就是右孩子节点。我们在中序遍历中看看3左边有3个节点,说明3的左子树共有3个节点,同理右子树有3个节点。所以我们在先序遍历中也能知道3后面的3个节点是左子树,再后面3个节点是右子树,这样就把两个遍历结果分成了两部分:

  • 先序遍历结果为:3, 9, 1, 2, 20, 15, 17
  • 中序遍历结果为:1, 9, 2, 3, 15, 20, 17

在红色部分递归就是3的左子树,在蓝色部分递归就是3的右子树。 纸上解题好说,但是代码实现,我开始还把自己搞糊涂了,代码很复杂,后来参考了这位大神的博客,代码好简洁易懂,赞一个:

class Solution {
private:
    TreeNode* dfs(vector<int>& preorder, vector<int>& inorder, int preL, int preR, int inL, int inR, unordered_map<int, size_t>& hash)
    {
        if (preL > preR || inL > inR)
            return NULL;
        TreeNode* root = new TreeNode(preorder[preL]);
        size_t idx = hash[root->val];
        root->left = dfs(preorder, inorder, preL + 1, idx – inL + preL, inL, idx – 1, hash);
        root->right = dfs(preorder, inorder, idx – inL + preL + 1, preR, idx + 1, inR, hash);
        return root;
    }
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    {
        if (preorder.size() == 0)
            return NULL;
        unordered_map<int, size_t> hash;
        for (size_t i = 0; i < inorder.size(); ++i) {
            hash[inorder[i]] = i;
        }
        return dfs(preorder, inorder, 0, preorder.size() – 1, 0, inorder.size() – 1, hash);
    }
};


本代码提交AC,用时16MS。
代码中有两点需要注意的,一是为了在中序遍历中快速找到根节点,提前对中序遍历结果hash了,因为题目中说了不会有重复元素;二是在dfs递归的时候,要算好下标的起止位置,一定不要弄错了,特别是preorder在左右子树中的起止位置。

LeetCode Word Search

79. Word Search

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

Example:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.

Constraints:

  • board and word consists only of lowercase and uppercase English letters.
  • 1 <= board.length <= 200
  • 1 <= board[i].length <= 200
  • 1 <= word.length <= 10^3

给定一个字符面板和一个单词,问能否在该面板中搜索到该单词,搜索路径只能水平或垂直。 简单的深度搜索题,首先找到第一个字符的起始位置,然后深度往该字符的上下左右搜,只要有一个true就返回true,否则重置标记位,返回false。 完整代码如下:

class Solution {
private:
    bool dfs(vector<vector<char> >& board, vector<vector<char> >& mark, int row, int col, string& word, int step)
    {
        if (board[row][col] != word[step])
            return false;
        if (step == word.size() – 1)
            return true;
        mark[row][col] = 1;
        if (row – 1 >= 0 && mark[row – 1][col] == 0) {
            bool ans = dfs(board, mark, row – 1, col, word, step + 1);
            if (ans)
                return true;
        }
        if (row + 1 < board.size() && mark[row + 1][col] == 0) {
            bool ans = dfs(board, mark, row + 1, col, word, step + 1);
            if (ans)
                return true;
        }
        if (col – 1 >= 0 && mark[row][col – 1] == 0) {
            bool ans = dfs(board, mark, row, col – 1, word, step + 1);
            if (ans)
                return true;
        }
        if (col + 1 < board[0].size() && mark[row][col + 1] == 0) {
            bool ans = dfs(board, mark, row, col + 1, word, step + 1);
            if (ans)
                return true;
        }
        mark[row][col] = 0;
        return false;
    }
public:
    bool exist(vector<vector<char> >& board, string word)
    {
        if (word.size() == 0)
            return true;
        int m = board.size();
        if (m == 0)
            return false;
        int n = board[0].size();
        if (n == 0)
            return false;
        vector<vector<char> > mark(m, vector<char>(n, 0));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (board[i][j] == word[0] && dfs(board, mark, i, j, word, 0))
                    return true;
            }
        }
        return false;
    }
};

本代码提交AC,用时9MS,击败95.63%的人。

LeetCode Flatten Binary Tree to Linked List

114. Flatten Binary Tree to Linked List

Given a binary tree, flatten it to a linked list in-place.

For example, given the following tree:

    1
   / \
  2   5
 / \   \
3   4   6

The flattened tree should look like:

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

把一棵二叉树展开成一个链表,链表还是借助树的数据结构,只不过是借用了树的右孩子指针作为链表的指针。 仔细观察发现新的链表的顺序和树的先序遍历的顺序一致,如果能使用额外的空间,则先序遍历后把节点存到数组中,然后依次链接起来。 如果不使用额外空间,则只能想办法在DFS时就建立好链接关系。先序遍历的顺序是根左右,但是这题为了方便,我们先对右孩子建立链表,然后把右孩子接到父亲的左孩子的最右下角的节点上。比如上图中,我们建立好5->6的链表之后,需要把它接到4号节点的右边。

         1
        /
       2
      / \
     3   4
          \
           5
            \
             6

所以针对右孩子,我们需要根据父节点找到父节点的左子树的最右下角的节点。但是针对左孩子时,因为此时右孩子已经建立好链表并且接到了左孩子正确的位置上,所以只需要把左孩子接到父亲的右孩子位置,并把原来的左孩子置空,下图就是把上图1的左子树放到了右边,左边置为空。

   1
     \
       2
      / \
     3   4
          \
           5
            \
             6

完整代码如下:

class Solution2 {
public:
    void dfs(TreeNode* par, TreeNode* cur)
    {
        if (cur == NULL)
            return;
        if (par) {
            if (cur == par->right) {
                if (par->left) {
                    TreeNode* tmp = par->left;
                    while (tmp->right)
                        tmp = tmp->right;
                    tmp->right = cur;
                    par->right = NULL;
                }
            }
            else {
                par->right = cur;
                par->left = NULL;
            }
        }
        if (cur->right)
            dfs(cur, cur->right);
        if (cur->left)
            dfs(cur, cur->left);
    }
    void flatten(TreeNode* root) { dfs(NULL, root); }
};

本代码提交AC,用时6MS。
还有一种思路比这简单,上面的解法需要while循环查找左兄弟的最右下角的节点,如果我们在DFS中直接返回尾节点,就不需要while查找了。
假设某节点的左右子树T(root->left)和T(root->right)已经flatten成linked list了:
1
/    \
2     5
\       \
3      6 <- rightTail
\
4  <- leftTail
如何将root、T(root->left)、T(root->right) flatten成一整个linked list?显而易见:
leftTail->right = root->right;
root->right = root->left;
root->left = NULL;
这里需要用到已经flatten的linked list的尾部元素leftTail, rightTail。所以递归返回值应该是生成的整个linked list的尾部。
代码如下:

class Solution {
public:
    TreeNode* dfs(TreeNode* root)
    {
        if (!root)
            return NULL;
        TreeNode* leftTail = dfs(root->left);
        TreeNode* rightTail = dfs(root->right);
        if (root->left) {
            leftTail->right = root->right;
            root->right = root->left;
            root->left = NULL;
        }
        if (rightTail)
            return rightTail;
        if (leftTail)
            return leftTail;
        return root;
    }
    void flatten(TreeNode* root) { dfs(root); }
};

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