Monthly Archives: January 2017

LeetCode Find All Duplicates in an Array

LeetCode Find All Duplicates in an Array Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements that appear twice in this array. Could you do it without extra space and in O(n) runtime? Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[2,3]

长度为n的数组中存储了1~n的若干个数,有的出现了两次,有的只出现了一次,所以还会有一些没有出现。现在要找出所有出现两次的数字。因为不能使用额外的空间,时间复杂度也只能O(n),所以方法和LeetCode Find All Numbers Disappeared in an Array几乎一样。 第一种方法是取相反数,如果发现nums[idx]已经是负数了,说明nums[i]在之前出现过了。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; if (nums[idx] > 0) { nums[idx] = -nums[idx]; } else { ans.push_back(abs(nums[i])); } } return ans; } }; [/cpp] 本代码提交AC,用时188MS。 第二种方法是把每个数放到它正确的位置,第二遍扫描的时候,如果发现数字和下标对不上,则存储的数字就是出现两次的数字,对应的下标+1就是没出现过的数字(LeetCode Find All Numbers Disappeared in an Array解法2)。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[nums[i] – 1] != nums[i]) { swap(nums[nums[i] – 1], nums[i]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] != i + 1) { ans.push_back(nums[i]); } } return ans; } }; [/cpp] 本代码提交AC,用时129MS。]]>

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 Sort Characters By Frequency

LeetCode Sort Characters By Frequency Given a string, sort it in decreasing order based on the frequency of characters. Example 1:

Input:
"tree"
Output:
"eert"
Explanation:
'e' appears twice while 'r' and 't' both appear once.
So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
Example 2:
Input:
"cccaaa"
Output:
"cccaaa"
Explanation:
Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer.
Note that "cacaca" is incorrect, as the same characters must be together.
Example 3:
Input:
"Aabb"
Output:
"bbAa"
Explanation:
"bbaA" is also a valid answer, but "Aabb" is incorrect.
Note that 'A' and 'a' are treated as two different characters.

把一个字符串中的字符按出现频率重新排序并输出。 简单题,因为ascii的字符有128个,所以构造一个128长的hash表,先统计一下每个字符出现的次数,然后对频率排个序,最后按频率从高到低重新构造字符串。 完整代码如下: [cpp] typedef struct Character{ char c; int cnt; bool operator<(const Character& ch) const { return this->cnt < ch.cnt; } }; class Solution { public: string frequencySort(string s) { vector<Character> hash(128); for (int i = 0; i < s.size(); ++i) { hash[s[i]].c = s[i]; ++hash[s[i]].cnt; } sort(hash.begin(), hash.end()); string ans = ""; for (int i = 127; i >=0; –i) { if (hash[i].cnt == 0)break; ans += string(hash[i].cnt, hash[i].c); } return ans; } }; [/cpp] 本代码提交AC,用时23MS。]]>

LeetCode Construct the Rectangle

LeetCode Construct the Rectangle For a web developer, it is very important to know how to design a web page’s size. So, given a specific rectangular web page’s area, your job by now is to design a rectangular web page, whose length L and width W satisfy the following requirements:

1. The area of the rectangular web page you designed must equal to the given target area.
2. The width W should not be larger than the length L, which means L >= W.
3. The difference between length L and width W should be as small as possible.
You need to output the length L and the width W of the web page you designed in sequence. Example:
Input: 4
Output: [2, 2]
Explanation: The target area is 4, and all the possible ways to construct it are [1,4], [2,2], [4,1].
But according to requirement 2, [1,4] is illegal; according to requirement 3,  [4,1] is not optimal compared to [2,2]. So the length L is 2, and the width W is 2.
Note:
  1. The given area won’t exceed 10,000,000 and is a positive integer
  2. The web page’s width and length you designed must be positive integers.

给定一个面积area,要求出一个长L和宽W,使得L*W=area,并且L>=W,并且L和W的差越小越好。 简单题,先对area开根号,然后L不断++,直到L*W=area为止。 唯一需要注意的是,必须L=ceil(sqrt(area)),因为如果area=2,不加ceil的话,L=1,W=2了。 完整代码如下: [cpp] class Solution { public: vector<int> constructRectangle(int area) { int L = ceil(sqrt(area)); while (area%L != 0) { ++L; } return vector<int>({ L,area / L }); } }; [/cpp] 本代码提交AC,用时89MS。不开心,为啥这么慢呢。。。]]>

LeetCode Arithmetic Slices

LeetCode Arithmetic Slices A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same. For example, these are arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic.
1, 1, 2, 5, 7
  A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N. A slice (P, Q) of array A is called arithmetic if the sequence: A[P], A[p + 1], …, A[Q – 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q. The function should return the number of arithmetic slices in the array A.   Example:
A = [1, 2, 3, 4]
return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

本题要求一个数组中等差数列的个数,并不要求是最长的等差数列。比如给的例子中数组A=[1,2,3,4],可以抽出3个等差数列,分别是[1,2,3]、[2,3,4]、[1,2,3,4]。 首先还是找特征,我们可以在数组中先找出最长的等差数列,然后在等差数列的基础上抽取(Slice)出比较小的等差数列,所以先研究一下长度为n的等差数列可以抽出多少个小的等差数列。
  • n=3,抽出1个
  • n=4,抽出3=1+2个,即1个长度为4的和2个长度为3的
  • n=5,抽出6=1+2+3个,即1个长度为5的、2个长度为4的和3个长度为3的
  • n=6,抽出10=1+2+3+4个,即…
由此可以得出规律,长度为n的等差数列,可以抽出1+2+…+(n-2)=(n-1)(n-2)/2个小的等差数列。 于是问题就转换为找出数组中所有最长连续等差数列,然后代入上述公式计算。 完整代码如下: [cpp] class Solution { public: inline int slice(int cnt) { if (cnt < 3)return 0; if (cnt == 3)return 1; return (cnt – 1)*(cnt – 2) / 2; // 1+2+…+(cnt-2) } int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0, cnt = 2, diff = A[1] – A[0]; for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == diff) { ++cnt; } else { ans += slice(cnt); cnt = 2; diff = A[i] – A[i – 1]; } } return ans + slice(cnt); } }; [/cpp] 本代码提交AC,用时3MS。 网上还有一种DP解法,我们看看上面的规律,当n从4增加到5时,抽取出来的小等差数列增加了3个,是在n=4时,最后+2的基础上+1=3;当n从5增加到6时,抽取出来的小等差数列增加了4个,是在n=5时,最后+3的基础上+1=4。 所以我们设置一个dp数组,dp[i]表示:如果第i个元素和前面构成等差数列了,则能贡献多少个小等差数列,根据前面的分析,dp[i]=dp[i-1]+1,这里的dp相当于比如n=6时的10=1+2+3+4(等号右边的数组);如果第i个元素不和前面构成等差数列,则dp[i]=0不贡献小等差数列。 这种解法的代码如下: [cpp] class Solution { public: int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0; vector<int> dp(A.size(), 0); for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == A[i – 1] – A[i – 2]) { dp[i] = dp[i – 1] + 1; } ans += dp[i]; } return ans; } }; [/cpp] 本代码提交AC,用时3MS。 DP一般都可以优化空间,上面的解法也可以不用一个dp数组,而只用一个变量。如下: [cpp] class Solution { public: int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0, cur = 0; for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == A[i – 1] – A[i – 2]) { ++cur; } else { cur = 0; } ans += cur; } return ans; } }; [/cpp] 本代码提交AC,用时也是3MS。]]>

LeetCode Find the Duplicate Number

287. Find the Duplicate Number 287. Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Example 1:

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

Example 2:

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

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once. 287. Find the Duplicate Number

一个长度为n+1的数组,包含1~n的数字,证明至少有一个数字重复,并找出这个重复数字。 证明好说,鸽巢原理。要求在线性时间复杂度和常数空间复杂度把这个重复的数字找出来就有难度了,而且不能修改原数组,难以用上LeetCode Find All Numbers Disappeared in an Array的技巧。 这题确实hard,看了看提示,以及网上的题解,现总结如下。 第一种解法是二分搜索。根据鸽巢原理,如果有n+1个[1,n]的数,则必有一个数重复了,把区间分成[1,m]和[m+1,n],则重复的数字要么出现在[1,m],要么出现在[m+1,n],所以只需统计一下小于等于和大于m的数字的个数,数字多的那个区间肯定是存在重复数字的区间。然后在那个区间递归的找下去,直到找到重复数字。
完整代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums)
    {
        int low = 1, high = nums.size() – 1;
        while (low < high) {
            int mid = (low + high) / 2, low_cnt = 0;
            for (int i = 0; i < nums.size(); ++i) {
                if (nums[i] <= mid)
                    ++low_cnt;
            }
            if (low_cnt > mid) {
                high = mid;
            }
            else {
                low = mid + 1;
            }
        }
        return low;
    }
};

本代码提交AC,用时9MS。
还有一种解法和LeetCode Linked List Cycle II很类似,如果存在重复数字,则使用下标递归访问的时候,会出现循环访问。首先通过快慢指针找到循环相遇点,然后慢指针回到原点,快慢指针按相同速度前进,直到遇到函数值相同时停止,这个相同的函数值即为重复数字。一些证明可以参考这个博客
完整代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums)
    {
        int slow = 0, fast = 0;
        while (true) {
            slow = nums[slow];
            fast = nums[nums[fast]];
            if (slow == fast)
                break;
        }
        slow = 0;
        while (true) {
            slow = nums[slow];
            fast = nums[fast];
            if (slow == fast)
                return slow;
        }
    }
};

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

二刷。对二分搜索的解读。假设数组没有重复数字,比如[1,2,3,4,5,6,7],则对于任意一个数x,<=x的数的个数和x是相等的,比如<=1的数有1个,<=2的数有2个etc…如果出现重复数字,假设比中点小,则<=m的数的个数要大于m。所以代码中每次比较是low_cnt和m比较。

l、r、while(l<=r)都是标准的二分搜索框架。最后返回r+1,想象一直low_cnt>m,知道不满足while循环,则最后一个满足的就是r+1。

class Solution {
public:
	int findDuplicate(vector<int>& nums) {
		int n = nums.size();
		int l = 1, r = n - 1;
		while (l <= r) {
			int m = l + (r - l) / 2;
			int low_cnt = 0;
			for (int i = 0; i < n; ++i) {
				if (nums[i] <= m)++low_cnt;
			}
			if (low_cnt > m)r = m - 1;
			else l = m + 1;
		}
		return r + 1;
	}
};

LeetCode Find All Numbers Disappeared in an Array

LeetCode Find All Numbers Disappeared in an Array Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]

一个长度为n的数组,保存了1~n中的若干个数,有的出现了两次,有的出现了一次,有的数没有出现,现在要找出那些没有出现的数。要求线性时间复杂度和常数空间复杂度。 这一题有点意思,要求不使用额外的空间,那么唯一的办法就是借助原数组进行操作了。我们可以把数组中的数字都放到它们正确的位置上,然后扫描一遍数组,如果某个位置存储的数字和下标对不上,则这个位置存储的是出现两次的数字,同时说明下标对应那个数字没有出现在原数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[i] != nums[nums[i] – 1]) { swap(nums[i], nums[nums[i] – 1]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] – 1 != i) { ans.push_back(i + 1); } } return ans; } }; [/cpp] 本代码提交AC,用时159MS。 还有一种解法是,对于每一个数字nums[i],如果它对应的nums[nums[i]-1]是正数,则把nums[nums[i]-1]赋值为自身的相反数;否则不变。最后重新扫描一遍数组,如果是负数,则说明对应数字存在,否则下标对应数字不存在。这个不难理解,比如题目中的例子,nums[0]=4,则把nums[nums[0]-1]赋值为-nums[nums[0]-1]=-7,这样第二遍扫描nums数组时,发现下标为3的数字是负数,则说明数字4存在于原始数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; nums[idx] = nums[idx]>0 ? (-nums[idx]) : nums[idx]; } for (int i = 0; i < nums.size(); ++i) { if (nums[i] > 0)ans.push_back(i + 1); } return ans; } }; [/cpp] 本代码提交AC,用时142MS。]]>

LeetCode Nim Game

292. Nim Game

You are playing the following Nim Game with your friend: There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn to remove the stones.

Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win the game given the number of stones in the heap.

Example:

Input: 4 Output: false  Explanation: If there are 4 stones in the heap, then you will never win the game;              No matter 1, 2, or 3 stones you remove, the last stone will always be               removed by your friend. 292. Nim Game 

博弈论的题。两个人轮流拿石头,一次可以拿1~3个,拿掉最后一个石头的人获胜。规定我先拿,问如果石头数为n时,我是否可以取胜。 看Hint,找规律。

  • 如果n∈[1,3],则我第一次拿就可以把所有石头拿走,所以我肯定可以获胜。
  • 如果n=4,则无论我第一次拿多少个,因为我最多可以拿3个,所以剩下的石头数转换为了n∈[1,3]的情况,也就是变成了对手肯定能获胜的情况。
  • 如果n∈[5,7],则我为了获胜,可以想方设法让我第一次拿了之后,剩余4个石头,这样相当于对手遇到n=4的情况,于是转换为我可以获胜的情况。
  • 如果n=8,则无论第一次拿多少个,剩余的石头个数都转换为了n∈[5,7]的情况,也就变成了对手必胜的情况。

观察以上的分析,发现当n是4的倍数时,我必输;否则我必胜。代码就很好写了:

class Solution {
public:
    bool canWinNim(int n) { return n % 4 != 0; }
};

本代码提交AC,用时0MS。
类似博弈论的题,一定要在纸上写几个例子出来,然后仔细找规律。

LeetCode Island Perimeter

LeetCode Island Perimeter You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water. Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells). The island doesn’t have “lakes” (water inside that isn’t connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don’t exceed 100. Determine the perimeter of the island. Example:

[[0,1,0,0],
 [1,1,1,0],
 [0,1,0,0],
 [1,1,0,0]]
Answer: 16
Explanation: The perimeter is the 16 yellow stripes in the image below:

在一个海上有一个岛,要求这个岛的周长。简单题,只要发现一个land cell,看看它的四周有没有海,比如上下左右,有一边有海,则总的周长加1,如果四周也都是land cell,则这个land cell不贡献周长。 完整代码如下: [cpp] class Solution { public: int islandPerimeter(vector<vector<int>>& grid) { int ans = 0; for (int i = 0; i < grid.size(); ++i) { for (int j = 0; j < grid[i].size(); ++j) { if (grid[i][j] == 1) { if (i == 0 || grid[i – 1][j] == 0)ans++; // top if (i + 1 == grid.size() || grid[i + 1][j] == 0)ans++; //bottom if (j == 0 || grid[i][j – 1] == 0)ans++; // left if (j + 1 == grid[i].size() || grid[i][j + 1] == 0)ans++; //right } } } return ans; } }; [/cpp] 本代码提交AC,用时212MS。居然有五千多个测试样例。。。]]>

LeetCode Max Consecutive Ones

LeetCode Max Consecutive Ones Given a binary array, find the maximum number of consecutive 1s in this array. Example 1:

Input: [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s.
    The maximum number of consecutive 1s is 3.
Note:
  • The input array will only contain 0 and 1.
  • The length of input array is a positive integer and will not exceed 10,000

求一个数组中最长连续的1的个数。简单题,直接计数,遇到0和1切换的时候记得更新一下结果就好了。完整代码如下: [cpp] class Solution { public: int findMaxConsecutiveOnes(vector<int>& nums) { int ans = 0, cnt = 0; for (int i = 0; i < nums.size(); ++i) { if (nums[i] == 1) { if (i == 0 || nums[i – 1] == 1)cnt++; else if (nums[i – 1] == 0) { ans = max(ans, cnt); cnt = 1; } } } return max(ans, cnt); } }; [/cpp] 本代码提交AC,用时49MS。]]>