Monthly Archives: February 2017

LeetCode Jump Game

55. Jump Game

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

给定一个数组,每个数字A[i]表示站在i位置最多能跳的步数,初始时站在数组左边,问能否跳到数组右边。
使用贪心策略,维护一个当前能跳到的最远位置maxpos。从左往右遍历数组,如果位置i>maxpos,说明根据之前的数字,没办法跳到位置i,直接break;否则更新maxpos为max(maxpos,A[i]+i)。最后检查maxpos>=n-1。完整代码如下:

class Solution {
public:
    bool canJump(vector<int>& nums)
    {
        int maxpos = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (i > maxpos)
                break;
            maxpos = max(maxpos, i + nums[i]);
        }
        return maxpos >= nums.size() – 1;
    }
};

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

二刷。是LeetCode Jump Game II的简化版,依然将问题转化为BFS,如果在BFS某一层时,最远距离没有更新,则表示没法再往前跳了。最后看看最远距离能否到达末尾即可。完整代码如下:

class Solution {
public:
	bool canJump(vector<int>& nums) {
		int n = nums.size(), i = 0;
		int cur_fastest = 0, next_fastest = 0;
		while (i < n) {
			while (i < n&&i <= cur_fastest) {
				next_fastest = max(next_fastest, i + nums[i]);
				++i;
			}
			if (next_fastest >= n - 1)return true;
			if (cur_fastest == next_fastest)return false;
			cur_fastest = next_fastest;
		}
		return false;
	}
};

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

三刷。在每个位置,我们可以算到该位置能跳到的最远位置maxindex,如果当前位置i在maxindex范围内,说明可以到达i,且能从i进行新的起跳,所以可更新maxindex。如果i超过maxindex,说明无法到达i,也就无法在i的基础上进行新的起跳了。最后看看maxindex能否到达末尾即可。

完整代码如下:

class Solution {
public:
	bool canJump(vector<int>& nums) {
		int maxindex = 0, n = nums.size();
		for (int i = 0; i < n; ++i) {
			if (i > maxindex)break;
			maxindex = max(maxindex, i + nums[i]);
		}
		return maxindex >= n - 1;
	}
};

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

LeetCode First Bad Version

278. First Bad Version

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.

Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.

You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

Example:

Given n = 5, and version = 4 is the first bad version. call isBadVersion(3) -> false call isBadVersion(5) -> true call isBadVersion(4) -> true Then 4 is the first bad version. 

从[1,2,…,n]有n个版本,其中有个版本是坏的,导致其后的所有版本都是坏的,现在要找到第一个坏了的版本。
简单题,二分搜索,相当于找lowerbound,正好刷了LeetCode Search for a Range,理清了二分搜索查找上下界的方法,此题直接实现lowerbound就好。代码如下:

// Forward declaration of isBadVersion API. bool isBadVersion(int version);
class Solution {
public:
    int firstBadVersion(int n)
    {
        int l = 1, r = n;
        while (l <= r) {
            int m = l + (r – l) / 2;
            if (!isBadVersion(m))
                l = m + 1;
            else
                r = m – 1;
        }
        return l;
    }
};

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

二刷:

class Solution {
public:
	int firstBadVersion(int n) {
		int left = 1, right = n;
		while (left <= right) {
			int mid = left + (right - left) / 2;
			bool bad = isBadVersion(mid);
			if (bad)right = mid - 1;
			else left = mid + 1;
		}
		return right + 1;
	}
};

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

LeetCode Search for a Range

LeetCode Search for a Range Given an array of integers sorted in ascending order, find the starting and ending position of a given target value. Your algorithm’s runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, Given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4].


本题要在一个有序数组中求一个数出现的下标区间,如果该数不在数组中,返回[-1,-1],要求用O(lgn)时间复杂度。 很明显用两次二分搜索找到该数的上下区间即可,可以认为是二分搜索的综合题。下界就是第一个不小于target的位置,所以当找到中间值和target相等时,我们要继续往左找。上界就是最后一个不大于target的位置,所以当找到中间值和target相等时,我们要继续往右找。理清了这个思路之后就很好写代码了,如下: [cpp] class Solution { private: int lowerbound(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)l = m + 1; else if (nums[m] >= target)r = m – 1; } return l; } int upperbound(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)l = m + 1; else if (nums[m] > target)r = m – 1; } return r; } public: vector<int> searchRange(vector<int>& nums, int target) { int lower = lowerbound(nums, target), upper = upperbound(nums, target); if (lower <= upper)return vector<int>({ lower, upper }); else return vector<int>({ -1,-1 }); } }; [/cpp] 本代码提交AC,用时9MS。 注意一点,为啥lowerbound是返回l,而upperbound是返回r呢,对于lowerbound,我们想象一下当中值等于target时,我们不停的往左走,即r=m-1,直到l<=r不再满足,即此时r已经小于l了,也就是说r往左走过头了,使得r对应的值已经小于target了,但此时l对应的值还是等于target,所以根据lowerbound的定义,我们返回l。对于uppperbound也类似,当中值等于target时,不停往右走,即l=m+1,直到不满足l<=r,此时l对应的值已经大于target,而r对应的值还是等于target的,根据upperbound的定义,我们返回r。 其实,STL中已经实现了lower_boundupper_bound其lower_bound和上文的lowerbound含义是一致的,upper_bound稍有不同,STL中的upper_bound是返回第一个大于target的位置,比上文的upperbound要大一个位置。所以如果借助STL的算法,需要对upper_bound返回的位置减1。代码如下: [cpp] class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { if (nums.size() == 0)return vector<int>({ -1,-1 }); vector<int>::iterator lower = lower_bound(nums.begin(), nums.end(), target), upper = upper_bound(nums.begin(), nums.end(), target) – 1; if (lower <= upper)return vector<int>({ lower – nums.begin(), upper – nums.begin() }); else return vector<int>({ -1,-1 }); } }; [/cpp] 本代码提交AC,用时16MS。貌似STL中的实现也是O(lgn)的,看来性能还是有所差别。]]>

LeetCode 4Sum II

LeetCode 4Sum II Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero. To make problem a bit easier, all A, B, C, D have same length of N where 0 ≤ N ≤ 500. All integers are in the range of -228 to 228 – 1 and the result is guaranteed to be at most 231 – 1. Example:

Input:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
Output:
2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

本题和LeetCode 4Sum类似,只不过给定的是四个数组,问分别从四个数组中取一个数,加起来和为0的方案总数。 暴力方法是$$O(n^4)$$的,但是我们可以利用Hash方法将复杂度降为$$O(n^2)$$。分别对A[i]+B[j]和C[i]+D[j]出现的次数建立Hash,然后遍历其中一个Hash表,在另一个Hash表中查相反数出现的次数,然后乘起来。代码如下: [cpp] class Solution { public: int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) { int ans = 0, n = A.size(); unordered_map<int, int> hash1, hash2; for(int i = 0; i < n; ++i){ for(int j = 0; j < n; ++j){ ++hash1[A[i] + B[j]]; ++hash2[C[i] + D[j]]; } } for(unordered_map<int, int>::iterator it = hash1.begin(); it != hash1.end(); ++it){ ans += it->second * hash2[-it->first]; } return ans; } }; [/cpp] 本代码提交AC,用时909MS。 参考:http://www.cnblogs.com/grandyang/p/6073317.html]]>

LeetCode 4Sum

18. 4Sum

Given an array nums of n integers and an integer target, are there elements abc, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

The solution set must not contain duplicate quadruplets.

Example:

Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.

A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

本题问一个数组中哪四个数加起来的和等于target,相当于LeetCode 3Sum的升级版,但是方法和3Sum是一样的,先枚举前两个数,对于后两个数,用首尾指针来求和。 一开始我的代码完全按照LeetCode 3Sum来写,先求最大最小值,然后用桶来hash,最后三层循环,代码如下:

class Solution {
public:
    vector<vector<int> > fourSum(vector<int>& nums, int target)
    {
        int min_v = INT_MAX, max_v = INT_MIN;
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] < min_v)
                min_v = nums[i];
            if (nums[i] > max_v)
                max_v = nums[i];
        }
        vector<int> hash(max_v – min_v + 1, 0);
        for (int i = 0; i < nums.size(); ++i) {
            ++hash[nums[i] – min_v];
        }
        vector<vector<int> > ans;
        for (int i = 0; i < hash.size(); ++i) {
            if (hash[i] == 0)
                continue;
            –hash[i];
            for (int j = i; j < hash.size(); ++j) {
                if (hash[j] == 0)
                    continue;
                –hash[j];
                int u = j, v = hash.size() – 1;
                while (u <= v) {
                    while (u <= v && hash[u] == 0)
                        ++u;
                    if (u > v)
                        break;
                    –hash[u];
                    while (u <= v && hash[v] == 0)
                        –v;
                    if (u > v) {
                        ++hash[u];
                        break;
                    }
                    –hash[v];
                    int sum = i + j + u + v + 4 * min_v;
                    ++hash[u];
                    ++hash[v];
                    if (sum == target) {
                        ans.push_back({ i + min_v, j + min_v, u + min_v, v + min_v });
                        ++u;
                        –v;
                    }
                    else if (sum < target)
                        ++u;
                    else
                        –v;
                }
                ++hash[j];
            }
            ++hash[i];
        }
        return ans;
    }
};

但是本代码提交TLE,看了一下,最后一个样例中,最小值有-236727523之小,最大值有91277418之大,导致hash数组太大了,从而无效的空for循环太多,最终TLE。
后来改用排序代替hash,需要注意为了防止重复结果,对四个数都要有去重的操作。代码如下:

class Solution {
public:
    vector<vector<int> > fourSum(vector<int>& nums, int target)
    {
        sort(nums.begin(), nums.end());
        vector<vector<int> > ans;
        int n = nums.size();
        for (int i = 0; i < n – 3; ++i) {
            if (i > 0 && nums[i] == nums[i - 1])
                continue; // 防止nums[i]重复
            for (int j = i + 1; j < n – 2; ++j) {
                if (j > i + 1 && nums[j] == nums[j - 1])
                    continue; // 防止nums[j]重复
                int u = j + 1, v = n – 1;
                while (u < v) {
                    int sum = nums[i] + nums[j] + nums[u] + nums[v];
                    if (sum == target) {
                        ans.push_back({ nums[i], nums[j], nums[u], nums[v] });
                        int k = u + 1;
                        while (k < v && nums[k] == nums[u])
                            ++k; // 防止nums[u]重复
                        u = k;
                        k = v – 1;
                        while (k > u && nums[k] == nums[v])
                            –k; // 防止nums[v]重复
                        v = k;
                    }
                    else if (sum < target)
                        ++u;
                    else
                        –v;
                }
            }
        }
        return ans;
    }
};

本代码提交AC,用时39MS。
还有一种思路是,先对数组中两数之和Hash,然后枚举两个两数之和,转换为类似Two Sum的方法,时间复杂度为$O(n^2)$。

LeetCode Teemo Attacking

LeetCode Teemo Attacking In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned condition. Now, given the Teemo’s attacking ascending time series towards Ashe and the poisoning time duration per Teemo’s attacking, you need to output the total time that Ashe is in poisoned condition. You may assume that Teemo attacks at the very beginning of a specific time point, and makes Ashe be in poisoned condition immediately. Example 1:

Input: [1,4], 2
Output: 4
Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned immediately.
This poisoned status will last 2 seconds until the end of time point 2.
And at time point 4, Teemo attacks Ashe again, and causes Ashe to be in poisoned status for another 2 seconds.
So you finally need to output 4.
Example 2:
Input: [1,2], 2
Output: 3
Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned.
This poisoned status will last 2 seconds until the end of time point 2.
However, at the beginning of time point 2, Teemo attacks Ashe again who is already in poisoned status.
Since the poisoned status won't add up together, though the second poisoning attack will still work at time point 2, it will stop at the end of time point 3.
So you finally need to output 3.
Note:
  1. You may assume the length of given time series array won’t exceed 10000.
  2. You may assume the numbers in the Teemo’s attacking time series and his poisoning time duration per attacking are non-negative integers, which won’t exceed 10,000,000.

这个题目好长,但是很简单。因为数组是递增的,我们把每段的结束时间减去开始时间就是这个数对应的持续时间,结束时间好算,就是timeSeries[i]+duration,但是开始时间要从上一段攻击的结束时间和当前开始时间求最大值,因为有可能上一段攻击还没结束,这一段攻击就开始了。 另外例子中每秒是有持续的时间的,比如第2秒有开始时间和结束时间,不用管它,按正常的第1秒开始攻击,持续2秒,就到第3秒了(题目中是第1秒开始到第1秒结束是1秒,然后第2秒开始到第2秒结束又是1秒,所以持续2秒的话,是在第2秒结束的时候结束的)。 完整代码如下: [cpp] class Solution { public: int findPoisonedDuration(vector<int>& timeSeries, int duration) { int ans = 0, start = 0, end = 0; for (int i = 0; i < timeSeries.size(); ++i) { start = max(start, timeSeries[i]); end = timeSeries[i] + duration; ans += end – start; start = end; } return ans; } }; [/cpp] 本代码提交AC,用时62MS。]]>

LeetCode Guess Number Higher or Lower

LeetCode Guess Number Higher or Lower We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to guess which number I picked. Every time you guess wrong, I’ll tell you whether the number is higher or lower. You call a pre-defined API guess(int num) which returns 3 possible results (-1, 1, or 0):

-1 : My number is lower
 1 : My number is higher
 0 : Congrats! You got it!
Example:
n = 10, I pick 6.
Return 6.

简单的猜数字游戏。使用二分搜索就好。唯一需要注意的是,题目中的guess函数中说My number is lower/higher是指正确答案比中值低或高,而不是我们猜的中值比正确答案低或高。唉,这算是题目描述不清吧,为此WA两次。 代码如下: [cpp] int guess(int num); class Solution { public: int guessNumber(int n) { int l = 1, r = n, mid = l + (r – l) / 2; int g = guess(mid); while (g != 0) { if (g == -1)r = mid – 1; else l = mid + 1; mid = l + (r – l) / 2; g = guess(mid); } return mid; } }; [/cpp] 本代码提交AC,用时0MS。]]>

LeetCode Binary Search Tree Iterator

Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST.

Calling next() will return the next smallest number in the BST.

Example:

BSTIterator iterator = new BSTIterator(root);
iterator.next();    // return 3
iterator.next();    // return 7
iterator.hasNext(); // return true
iterator.next();    // return 9
iterator.hasNext(); // return true
iterator.next();    // return 15
iterator.hasNext(); // return true
iterator.next();    // return 20
iterator.hasNext(); // return false

Note:

  • next() and hasNext() should run in average O(1) time and uses O(h) memory, where h is the height of the tree.
  • You may assume that next() call will always be valid, that is, there will be at least a next smallest number in the BST when next() is called.

本题要求设计一个二叉搜索树的迭代器,迭代的输出当前树中最小值,并且要求next和hasNext是O(1)时间复杂度和O(h)空间复杂度,h为树的高度。 因为二叉搜索树的中序遍历是递增有序的序列,所以如果没有O(h)空间复杂度限制,则可以提前保存中序遍历结果,然后实现对数组的迭代器。但是这样的空间复杂度就是O(sizeof(tree)),不符合题意。 立马想到树的中序遍历的非递归实现,我们可以借助一个堆栈,先只把树的根和最左边的数压栈,这样栈顶元素就是树的最下角元素,也就是最小值。每调用一次next,返回当前栈顶元素,同时把栈顶元素的右孩子及右孩子的所有最左边的节点压栈。hasNext就好办,直接返回堆栈是否为空。 这样堆栈中最多会保存树高个数的节点,所以是O(h)的空间复杂度。完整代码如下:

class BSTIterator {
private:
    stack<TreeNode*> tree;
    void putAllLeft(TreeNode* root, stack<TreeNode*>& tree)
    {
        while (root != NULL) {
            tree.push(root);
            root = root->left;
        }
    }
public:
    BSTIterator(TreeNode* root) { putAllLeft(root, tree); } /** @return whether we have a next smallest number */
    bool hasNext() { return !tree.empty(); } /** @return the next smallest number */
    int next()
    {
        TreeNode* top = tree.top();
        tree.pop();
        putAllLeft(top->right, tree);
        return top->val;
    }
};

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

LeetCode Peeking Iterator

284. Peeking Iterator284. Peeking Iterator

Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that support the peek() operation — it essentially peek() at the element that will be returned by the next call to next().

Example:

Assume that the iterator is initialized to the beginning of the list: [1,2,3].

Call next() gets you 1, the first element in the list.
Now you call peek() and it returns 2, the next element. Calling next() after that still return 2. 
You call next() the final time and it returns 3, the last element. 
Calling hasNext() after that should return false.

Follow up: How would you extend your design to be generic and work with all types, not just integer?284. Peeking Iterator


设计预取迭代器peek(),简单题,理清思路就行。
设置两个局部变量,hasPeeked表示是否预取过,peekedVal表示预取的值。调用peek()时,调用父类的next得到peekedVal,并设置hasPeeked为true;当然如果hasPeeked本身为true时,直接返回peekedVal就好。调用next()时,如果预取了,则直接返回peekedVal,并且把hasPeeked重置为false,这点很重要;如果没预取,则调用父类的next。调用hasNext()时,如果有预取,则返回true,否则返回父类的hasNext()。
谷歌有关于peek迭代器的样例代码
完整代码如下:

class PeekingIterator : public Iterator {
private:
    bool hasPeeked;
    int peekedVal;

public:
    PeekingIterator(const vector<int>& nums)
        : Iterator(nums)
    {
        // Initialize any member here.
        // **DO NOT** save a copy of nums and manipulate it directly. // You should only use the Iterator interface methods.
        hasPeeked = false;
    }
    // Returns the next element in the iteration without advancing the iterator.
    int peek()
    {
        if (hasPeeked)
            return peekedVal;
        peekedVal = Iterator::next();
        hasPeeked = true;
        return peekedVal;
    }
    //hasNext() and next() should behave the same as in the Iterator interface. // Override them if needed.

    int next()
    {
        if (hasPeeked) {
            hasPeeked = false;
            return peekedVal;
        }
        return Iterator::next();
    }
    bool hasNext() const { return hasPeeked || Iterator::hasNext(); }
};

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

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。