Tag Archives: 递归

LeetCode Search in Rotated Sorted Array

33. Search in Rotated Sorted Array

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

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

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm’s runtime complexity must be in the order of O(log n).

Example 1:

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

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

这一题和LeetCode Find Minimum in Rotated Sorted Array类似,只不过不是查找最小值,而是查找某个target是否存在。也是采用二分查找的思路,首先判断nums[m]和nums[r]的关系,如果nums[m]<nums[r],说明右半部分是有序的,看看target是否在该部分范围内,如果在,则l=mid+1;否则r=mid-1。如果nums[m]>nums[r],说明右半部分无序,则左半部分有序,看看target是否在左半部分范围内,如果在,则r=mid-1;否则l=mid+1。如此循环下去。

完整代码如下:

class Solution {
public:
    int 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 mid;
            if (nums[mid] < nums[r]) {
                if (target > nums[mid] && target <= nums[r]) {
                    l = mid + 1;
                }
                else {
                    r = mid – 1;
                }
            }
            else {
                if (target >= nums[l] && target < nums[mid]) {
                    r = mid – 1;
                }
                else {
                    l = mid + 1;
                }
            }
        }
        return -1;
    }
};

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

二刷。用递归方法实现,每次判断区间的左边是否小于右边,如果是的话,说明是有序的,常规二分即可;如果不是,则取中点,递归在其左边和右边搜索。由于取了中点后,必定有一边是有序的,则要么在有序区间搜索,要么在另一边搜索,即每次是减半的,所以时间复杂度也是O(lgN)。

完整代码如下:

class Solution {
public:
	int BinarySearch(vector<int>& nums, int target, int left, int right) {
		if (left > right)return -1;
		if (left == right) {
			if (nums[left] == target)return left;
			else return -1;
		}
		if (nums[left] < nums[right]) {
			if (target >= nums[left] && target <= nums[right]) {
				int mid = left + (right - left) / 2;
				if (nums[mid] == target)return mid;
				else if (nums[mid] < target)return BinarySearch(nums, target, mid + 1, right);
				else return BinarySearch(nums, target, left, mid - 1);
			}
			else {
				return -1;
			}
		}
		else {
			int mid = left + (right - left) / 2;
			int lans = BinarySearch(nums, target, left, mid);
			int rans = BinarySearch(nums, target, mid + 1, right);
			if (lans != -1)return lans;
			if (rans != -1)return rans;
			return -1;
		}
	}
	int search(vector<int>& nums, int target) {
		return BinarySearch(nums, target, 0, nums.size() - 1);
	}
};

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

三刷。迭代方法还是主流,和常规二分搜索的代码框架是一致的。但是一刷的时候是先判断右半部分是否有序,有点不符合直觉,如果简单的先判断左半部分,会出bug,比如[3,1],要找1的时候,此时m=0,和l=0是相同的,如果简单的用nums[l]<nums[m]来判断左边是否有序的话,会有问题,此时m==l,所以左边也可以看做是有序的,但不满足nums[l]<nums[s]。所以需要额外加一个l==m的判断。完整代码如下:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        while(l <= r) {
            int m = l + (r - l) / 2;
            if(nums[m] == target) return m;
            
            if(l == m || nums[l] < nums[m]) { // l == m 左边只有一个元素,左边也是有序的
                if(target >= nums[l] && target < nums[m]) r = m - 1;
                else l = m + 1;
            } else {
                if(target > nums[m] && target <= nums[r]) l = m + 1;
                else r = m - 1;
            }
        }
        
        return -1;
    }
};

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

LeetCode Flatten Nested List Iterator

LeetCode Flatten Nested List Iterator Given a nested list of integers, implement an iterator to flatten it. Each element is either an integer, or a list — whose elements may also be integers or other lists. Example 1: Given the list [[1,1],2,[1,1]], By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1]. Example 2: Given the list [1,[4,[6]]], By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].


本题自定义了一个【嵌套的整数】,【嵌套的整数】可以是一个真正的整数,也可以是一个包含整数的数组。现在要实现针对【嵌套的整数】的一个迭代器,迭代器包含next()和hasNext()函数。 解题思路是先解析这个【嵌套的整数】数组,解析的方法就是递归的解析,把解析出来的真正的整数存到一个vector数组中。接下来的迭代器就好实现了,用pos表示当前访问的数组下标,next()函数先访问pos指向元素,然后++pos;hasNext()就判断一下pos是否走到末尾了。 完整代码如下: [cpp] class NestedIterator { private: vector<int> vi; int pos; public: void parseNestedInteger(NestedInteger& ni) { if (ni.isInteger())vi.push_back(ni.getInteger()); else { vector<NestedInteger> vni = ni.getList(); for (int i = 0; i < vni.size(); ++i) { parseNestedInteger(vni[i]); } } } NestedIterator(vector<NestedInteger> &nestedList) { for (int i = 0; i < nestedList.size(); ++i) { parseNestedInteger(nestedList[i]); } pos = 0; } int next() { return vi[pos++]; } bool hasNext() { return pos < vi.size(); } }; [/cpp] 本代码提交AC,用时29MS。]]>

LeetCode Gray Code

89. Gray Code

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

Example 1:

Input: 2
Output: [0,1,3,2]
Explanation:
00 - 0
01 - 1
11 - 3
10 - 2

For a given n, a gray code sequence may not be uniquely defined.
For example, [0,2,3,1] is also a valid gray code sequence.

00 - 0
10 - 2
11 - 3
01 - 1

Example 2:

Input: 0
Output: [0]
Explanation: We define the gray code sequence to begin with 0.
             A gray code sequence of n has size = 2n, which for n = 0 the size is 20 = 1.
             Therefore, for n = 0 the gray code sequence is [0].

格雷码是这样一种编码:第一个数为0,后面每个数和前一个数在二进制位上只相差一位。现在给定二进制编码为n位,要求输出一串格雷码。
在不了解格雷码的情况下,可以单纯根据上面格雷码的定义来列出这一串格雷码,我们需要尝试依次翻转n位中的每一位,如果翻转之后得到的编码之前没有出现过,则翻转成功,接着进入下一次尝试,直到产生所有格雷码。
假设一个二进制数为x,则翻转其第i位之后的二进制数为(x&(~(1 << i))) | (~x)&(1 << i),还是有点复杂的。
这种思路可以用递归的方法实现,完整代码如下:

class Solution {
public:
    void work(vector<int>& ans, unordered_set<int>& codes, int gray, int n)
    {
        for (int i = 0; i < n; ++i) {
            int new_gray = (gray & (~(1 << i))) | (~gray) & (1 << i); // flip i-th bit
            if (codes.find(new_gray) == codes.end()) {
                codes.insert(new_gray);
                ans.push_back(new_gray);
                work(ans, codes, new_gray, n);
                break;
            }
        }
    }
    vector<int> grayCode(int n)
    {
        vector<int> ans = { 0 };
        if (n == 0)
            return ans;
        unordered_set<int> codes = { 0 };
        work(ans, codes, 0, n);
        return ans;
    }
};


本代码提交AC,用时6MS。
查看格雷码的维基百科,有好多种方法都可以生成格雷码序列,其中一种很有意思,可以根据二进制长度为n-1的格雷码序列生成二进制长度为n的格雷码序列,如下图所示,这种方法叫做镜射排列。

仔细观察会发现,n长的格雷码序列的前一半序列和n-1长的格雷码序列,在十进制数上是完全一样的,二进制数上也只是多了一个前导零;而后半序列是n-1长的格雷码序列镜面对称之后,加上一位前导1得到的。所以很容易可以写出镜射排列的代码:

class Solution {
public:
    vector<int> grayCode(int n)
    {
        vector<int> ans = { 0 };
        if (n == 0)
            return ans;
        ans.push_back(1);
        for (int i = 2; i <= n; ++i) {
            int sz = ans.size();
            while (sz–) {
                ans.push_back(ans[sz] | (1 << (i – 1)));
            }
        }
        return ans;
    }
};

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

LeetCode Binary Watch

LeetCode Binary Watch A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59). Each LED represents a zero or one, with the least significant bit on the right. For example, the above binary watch reads “3:25”. Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the watch could represent. Example:

Input: n = 1
Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]
Note:
  • The order of output does not matter.
  • The hour must not contain a leading zero, for example “01:00” is not valid, it should be “1:00”.
  • The minute must be consist of two digits and may contain a leading zero, for example “10:2” is not valid, it should be “10:02”.

程序员有一个二进制的手表,如图所示,上面4个灯表示小时,下面6个灯表示分钟。现在告诉你手表上亮着n盏灯,问共有多少种时间的可能,要求输出所有可能的时间。 我觉得这应该算中等题,稍微有一点麻烦。 首先我们要把亮着的n盏灯分到小时和分钟上,比如小时亮x盏灯,分钟亮y盏灯,x+y=n。对于小时,长度为4bit,要把亮着的x盏灯填入这4bit中,共有$$C_4^x$$种填法,可以用递归的思路实现。对于分钟,长度为6bit,要把亮着的y盏灯填入这6bit中,共有$$C_6^y$$种填法,也可以用递归的思路实现。 所以我们可以统一小时和分钟的递归算法,具体看代码comb函数。假设初始的灯的状态为cur,我们就是要在cur的[s,t) bit之间填入ones个1bit,对于小时,长度为4bit,所以传入cur=0, s=0, t=4;对于分钟,长度为6bit,所以传入cur=0, s=0, t=6。注意每次递归调用的时候,s都要更新,否则生成的组合结果会有重复。 生成了小时和分钟的组合之后,我们再把小时和分钟字符串组合起来,这个就简单了,不再赘述。完整代码如下: [cpp] class Solution { public: // cur为传入的二进制串,comb要把ones个1填入[s,t)中 void comb(vector<int>& res, int cur, int s, int t, int ones) { if (ones == 0)res.push_back(cur); else { for (int i = s; i < t; ++i) { if ((cur&(1 << i)) == 0) { comb(res, cur | (1 << i), i + 1, t, ones – 1); // 递归把ones-1个1填入[i+1,t)中 } } } } vector<string> readBinaryWatch(int num) { vector<string> ans; for (int hour = 0; hour <= 3; ++hour) { if (hour > num)break; vector<int> hours; comb(hours, 0, 0, 4, hour); vector<int> minutes; comb(minutes, 0, 0, 6, num – hour); for (int i = 0; i < hours.size(); ++i) { if (hours[i] > 11)continue; string tmp_h = to_string(hours[i]); for (int j = 0; j < minutes.size(); ++j) { if (minutes[j] > 59)continue; string tmp_m = to_string(minutes[j]); if (tmp_m.size() < 2)tmp_m = "0" + tmp_m; ans.push_back(tmp_h + ":" + tmp_m); } } } return ans; } }; [/cpp] 本代码提交AC,用时0MS。]]>

LeetCode Sum of Left Leaves

LeetCode Sum of Left Leaves Find the sum of all left leaves in a given binary tree. Example:

    3
   / \
  9  20
    /  \
   15   7
There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.

本题要求二叉树中所有左叶子节点之和。简单题,直接递归,递归的时候记录或判断当前节点是父亲节点的左孩子还是右孩子。完整代码如下: [cpp] class Solution { public: int work(TreeNode *cur, TreeNode *par) { if (cur->left == NULL&&cur->right == NULL&&par->left == cur)return cur->val; int ans = 0; if (cur->left)ans += work(cur->left, cur); if (cur->right)ans += work(cur->right, cur); return ans; } int sumOfLeftLeaves(TreeNode* root) { if (root == NULL)return 0; int sum = 0; if (root->left)sum += work(root->left, root); if (root->right)sum += work(root->right, root); return sum; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Invert Binary Tree

226. Invert Binary Tree 226. Invert Binary Tree LeetCode Invert Binary Tree Invert a binary tree.

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

to

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

Trivia: This problem was inspired by this original tweet by Max Howell:

Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.


本题要把二叉树翻转,看题目中的例子,也就是交换树的左右子树,然后递归的交换就好了。简单题,完整代码如下:

class Solution {
public:
    void invert(TreeNode* root)
    {
        if (root == NULL)
            return;
        swap(root->left, root->right);
        invert(root->left);
        invert(root->right);
    }
    TreeNode* invertTree(TreeNode* root)
    {
        invert(root);
        return root;
    }
};

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

LeetCode Binary Tree Paths

257. Binary Tree Paths257. Binary Tree Paths

Given a binary tree, return all root-to-leaf paths.

Note: A leaf is a node with no children.

Example:

Input:    1  /   \ 2     3  \   5 Output: ["1->2->5", "1->3"] Explanation: All root-to-leaf paths are: 1->2->5, 1->3257. Binary Tree Paths

本题要求输出二叉树从根到叶子的所有路径,DFS题都做烂了,完整代码如下,不解释。

class Solution {
public:
    void dfs(vector<string>& ans, string cand, TreeNode* root)
    {
        cand += "->" + to_string(root->val);
        if (root->left == NULL && root->right == NULL) {
            ans.push_back(cand.substr(2));
            return;
        }
        if (root->left != NULL)
            dfs(ans, cand, root->left);
        if (root->right != NULL)
            dfs(ans, cand, root->right);
    }
    vector<string> binaryTreePaths(TreeNode* root)
    {
        vector<string> ans;
        if (root == NULL)
            return ans;
        dfs(ans, "", root);
        return ans;
    }
};

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

LeetCode Balanced Binary Tree

110. Balanced Binary Tree

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

Example 1:

Given the following tree [3,9,20,null,null,15,7]:

    3
   / \
  9  20
    /  \
   15   7

Return true.

Example 2:

Given the following tree [1,2,2,3,3,null,null,4,4]:

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

Return false.


判断一棵二叉树是否是平衡二叉树。平衡二叉树是指任意子树的左右孩子高度差不超过1。 我最初的想法是DFS找到整棵树的最小和最大高度,然后判断这两个高度差是否超过1,但提交了好几次都只能通过部分样例,好忧伤。 后来看参考网上一个人的题解,还是很厉害的。网上题解都大同小异,思想就是算每棵子树的左右孩子高度差,但是有人是自顶向下的,有人是自底向上的,这里面复杂度当然有差别,如果自顶向下的算,那么很多子树的高度重复计算了,所以采用自底向上的方法比较好,求父亲的高度时,可以用到孩子的高度。 完整代码如下:

class Solution {
public:
    int depth(TreeNode* root)
    {
        if (root == NULL)
            return 0;
        int left = depth(root->left); // 先不断递归到叶子
        int right = depth(root->right);
        if (left < 0 || right < 0)
            return -1;
        if (abs(left – right) > 1)
            return -1;
        return max(left, right) + 1; // 父亲高度利用了孩子的高度
    }
    bool isBalanced(TreeNode* root) { return depth(root) != -1; }
};

本代码提交AC,用时13MS。
二刷。自己写的代码,子函数同时算高度和判断是否平衡,如下:

class Solution {
private:
    bool isBalanced(TreeNode* root, int& height)
    {
        if (root == NULL) {
            height = 0;
            return true;
        }
        int lheight = 0, rheight = 0;
        bool lbalanced = isBalanced(root->left, lheight);
        bool rbalanced = isBalanced(root->right, rheight);
        height = max(lheight, rheight) + 1;
        bool balanced = abs(lheight – rheight) <= 1;
        return lbalanced && rbalanced && balanced;
    }
public:
    bool isBalanced(TreeNode* root)
    {
        int height = 0;
        return isBalanced(root, height);
    }
};

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

LeetCode Convert Sorted List to Binary Search Tree

109. Convert Sorted List to Binary Search Tree

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Example:

Given the sorted linked list: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST:

      0
     / \
   -3   9
   /   /
 -10  5

这一题和之前的LeetCode Convert Sorted Array to Binary Search Tree思路是一样的,只是把数组换成了链表,这样的话,就不能通过下标直接访问到中点元素了。 此时只能通过遍历找到链表的中点,然后以中点为根节点建树,最后分割左右链表,递归建树。完整代码如下:

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head)
    {
        if (head == NULL)
            return NULL;
        if (head->next == NULL)
            return new TreeNode(head->val);
        int n = 0, cnt = 0;
        ListNode* tmp = head;
        while (tmp != NULL) {
            n++;
            tmp = tmp->next;
        }
        tmp = head;
        while (cnt < n / 2 – 1) {
            tmp = tmp->next;
            cnt++;
        } // 此时tmp是中间节点的上一个节点
        TreeNode* root = new TreeNode(tmp->next->val);
        ListNode* head2 = tmp->next->next;
        tmp->next = NULL; // 断开
        root->left = sortedListToBST(head);
        root->right = sortedListToBST(head2);
        return root;
    }
};

本代码提交AC,用时22MS。击败了87%的人:-)
后来想想这题会不会有更优的解法,网上看了一下,发现自己蠢到哭了,链表找中点居然用了这么笨的方法,之前的快慢指针都忘了。换上快慢指针找链表中点的解法如下:

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head)
    {
        if (head == NULL)
            return NULL;
        if (head->next == NULL)
            return new TreeNode(head->val);
        ListNode *fast = head, *slow = head, *prior = head;
        while (fast->next != NULL && fast->next->next != NULL) {
            prior = slow;
            slow = slow->next;
            fast = fast->next->next;
        } // 此时slow为中间节点,prior为中间节点的上一个节点
        TreeNode* root = new TreeNode(slow->val);
        ListNode* head2 = slow->next;
        prior->next = NULL; //断开
        if (head != slow)
            root->left = sortedListToBST(head);
        root->right = sortedListToBST(head2);
        return root;
    }
};

本代码提交AC,用时26MS,有点失望,按理说这种解法是要比我上面的解法快的。
后来我又发现,这两种解法构建出来的平衡二叉搜索树不一样,如下图所示,但是他们都还算紧凑,都是正确的,看来答案不唯一啊。(其实主要是偶数长度时,有两个中间节点,选哪个都是正确的。)

LeetCode Convert Sorted Array to Binary Search Tree

108. Convert Sorted Array to Binary Search Tree

Given an array where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Example:

Given the sorted array: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST:

      0
     / \
   -3   9
   /   /
 -10  5

给定一个升序排列的数组,要构建一个高度平衡的二叉搜索树。 二叉搜索树的概念是递归定义的,具体可以参看维基百科,简单来说就是左孩子小于根节点,右孩子大于根节点。 但是本题要求二叉搜索树是高度平衡的,也就是说左右孩子的高度差不能大于1。 我一开始的想法是既然是平衡二叉搜索树,直接取数组中点,然后左右依次排开,肯定能得到一棵平衡二叉搜索树,如下左图所示。 但是这一版本的代码提交上去之后只通过了6个样例。后来仔细想了想,这解法未免也太简单幼稚了吧。想到之前解树的题都用到了递归,这题应该也可以用,立马灵光一闪,第一次取中点之后,递归的在左右半个数组中取中点建树,所有还可以得到如上右图所示的更加紧凑的平衡二叉搜索树。

完整代码如下:

class Solution {
public:
    TreeNode* work(vector<int>& nums, int s, int t)
    {
        if (s == t)
            return new TreeNode(nums[s]);
        if (s > t)
            return NULL;
        int mid = (s + t + 1) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = work(nums, s, mid – 1);
        root->right = work(nums, mid + 1, t);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) { return work(nums, 0, nums.size() – 1); }
};

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