LeetCode 122. Best Time to Buy and Sell Stock II

题目描述:

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

这道题与上一题相比不同点在于可以多次买入卖出股票但是不能同时持有多份股票, 所以整个的操作流程必须是"买入-卖出-买入-卖出…-买入-卖出". 考虑一个简单的情况:

1,4,2,10

显然有两种策略, 分别的利润为(4-1)+(10-2)=1110-1=9, 应选择第一种. 而另一种情况:

1,2,4,10

两种策略的利润为(2-1)+(10-4)=810-1=9, 此时应该选择第二种. 对于一般情况来说:

a1,a2,a3...,ap,...,aq,...,an

如果在a1买入ap卖出然后再aq买入an卖出的话, 利润为(an-aq)+(ap-a1), 如果在a1买入an卖出的话, 利润为an-a1, 两者之差为(an-a1)-[(an-aq)+(ap-a1)]=aq-ap, 所以如果ap>aq, 那么应该选择前者, 反之选择后者. 从编程策略上来说就应该是搜索一个从低价位开始的递增序列, 在不能再保持递增的时候就是应该卖出的时候.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() < 2) return 0;
int low = prices[0], profit = 0;
for(int i = 1; i < prices.size() - 1; i++){
if(prices[i] < low) low = prices[i];
else if(prices[i] > prices[i + 1]){
profit += (prices[i] - low);
low = prices[i + 1];
}
}
if(prices.back() > low) profit += (prices.back() - low);
return profit;
}
};

LeetCode 456. 132 Pattern

题目描述:

Given a sequence of n integers a1, a2, …, an, a 132 pattern is a subsequence ai, aj, ak such that i < j < k and ai < ak < aj. Design an algorithm that takes a list of n numbers as input and checks whether there is a 132 pattern in the list.

Note: n will be less than 15,000.

Example 1:

1
2
3
4
5
6
Input: [1, 2, 3, 4]

Output: False

Explanation: There is no 132 pattern in the sequence.

Example 2:

1
2
3
4
5
6
Input: [3, 1, 4, 2]

Output: True

Explanation: There is a 132 pattern in the sequence: [1, 4, 2].

Example 3:

1
2
3
4
5
Input: [-1, 3, 2, 0]

Output: True

Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0].

这道题一开始我用了最直接的O(n^2)时间复杂度的解法, 后来仔细想了一下后才想出O(n)复杂度的解法.

主要思路是使用栈, 从后往前遍历nums. 假如我们遍历到了元素$a_k$, 那么在继续往前遍历的过程中, $a_k$有两种状态:

  1. 等待找到一个比$a_k$大的元素$a_j$.
  2. 如果找到了$a_j$, 那么$a_k$就要等待找到一个比$a_k$小的元素$a_i$.

问题在于$a_j$与$a_k$之间会有很多个其他元素, 这些元素一定比$a_k$小或相等, 因此当出现$a_j$的时候, $a_{j+1}\dots a_k$都会进入第二种状态, 对于$a_k$之后的元素则仍然处于第一种状态. 可以用一个栈来保存处于第一种状态的元素, 出现$a_j$时, 就弹出所有小于$a_j$的元素, 并把这些元素都压入另一个栈中.

仔细想一想, 每次从第一个栈中弹出的元素都一定是有序的, 越接近栈顶越小, 如果按照出栈的顺序压入第二个栈中, 就是越接近栈顶的元素越大, 当我们遍历到另一个元素$a_i$时, 只要它比第二个栈的栈顶元素小, 我们就可以确定函数返回true. 所以实际上我们并不需要第二个栈, 只要用一个变量来保存第一个栈中最后一个弹出的元素就可以了.

最后, 关于这个算法的复杂度. 首先遍历数组是O(n), 然后每一个数组中的元素都只有一次入栈和出栈的机会, 因此总的复杂度是O(n). 因为每个元素只能入栈一次, 所以我们可以一开始就设置栈的最大大小, 手动维护栈顶, 以避免栈大小动态增长过程中的开销.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
bool find132pattern(vector<int>& nums) {
if(nums.size() < 3) return false;
vector<int> waitGreater(nums.size());
int maxWaitSmaller = INT_MIN, top = -1;
for(int i = nums.size() - 1; i >= 0; i--){
if(top < 0 || nums[i] < waitGreater[top]){
waitGreater[++top] = nums[i];
}
else if(nums[i] > waitGreater[top]){
while(top >= 0 && waitGreater[top] < nums[i]){
top--;
}
maxWaitSmaller = waitGreater[top + 1];

waitGreater[++top] = nums[i];
}

if(maxWaitSmaller != INT_MIN && nums[i] < maxWaitSmaller) return true;
}
return false;
}
};

LeetCode 459. Repeated Substring Pattern

题目描述:

Given a non-empty string check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. You may assume the given string consists of lowercase English letters only and its length will not exceed 10000.

Example 1:

1
2
3
4
5
6
Input: "abab"

Output: True

Explanation: It's the substring "ab" twice.

Example 2:

1
2
3
4
Input: "aba"

Output: False

Example 3:

1
2
3
4
5
Input: "abcabcabcabc"

Output: True

Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.)

这道题用最简单的遍历+回溯就可以AC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
bool repeatedSubstringPattern(string str) {
for(int i = 1; i <= str.length() / 2; i++){
string sub = str.substr(0, i);
if(check(str, sub)) return true;
}
return false;
}

bool check(string &str, string &sub){
if(str.length() % sub.length() != 0) return false;
for(int i = 0; i < str.length(); i += sub.length()){
if(!strEqual(str, i, sub)) return false;
}
return true;
}

bool strEqual(string &str, int start, string &sub){
for(int i = 0; i < sub.length(); i++){
if(sub[i] != str[start + i]) return false;
}
return true;
}
};

Runtime 92ms, 不是好解法, 看Discussion有许多人用KMP, 但是我不太懂KMP(加入学习列表), 而且感觉有点大材小. 所以选择了另一种办法: 主要思路就是把字符串循环左移(右移), 当正好移出了要找的子串的时候, 这个字符串应该是跟原字符串相等的. 我们可以只考虑移动字符串长度因子的长度, 因为其他长度都是不可能相等的. 这个解法的Runtime是29ms.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool repeatedSubstringPattern(string str) {
string nextStr = str;
int len = str.length();
if(len < 1) return false;
for(int i = 1; i <= len / 2; i++){
if(len % i == 0){
nextStr = leftShift(str, i);
if(nextStr == str) return true;
}
}
return false;
}

string leftShift(string &str, int l){
string ret = str.substr(l);
ret += str.substr(0, l);
return ret;
}
};

LeetCode 455. Assign Cookies

题目描述:

Assume you are an awesome parent and want to give your children some cookies. But, you should give each child at most one cookie. Each child i has a greed factor gi, which is the minimum size of a cookie that the child will be content with; and each cookie j has a size sj. If sj >= gi, we can assign the cookie j to the child i, and the child i will be content. Your goal is to maximize the number of your content children and output the maximum number.

Note: You may assume the greed factor is always positive.  You cannot assign more than one cookie to one child.

Example 1:

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

Output: 1

Explanation: You have 3 children and 2 cookies. The greed factors of 3 children are 1, 2, 3.
And even though you have 2 cookies, since their size is both 1, you could only make the child whose greed factor is 1 content.
You need to output 1.

Example 2:

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

Output: 2

Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2.
You have 3 cookies and their sizes are big enough to gratify all of the children,
You need to output 2.

排序之后使用贪心来满足尽量多的孩子.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int gi = 0, si = 0, ans = 0;
while(si < s.size() && gi < g.size()){
if(g[gi] <= s[si]){
ans++;
si++;
gi++;
}
else{
si++;
}
}
return ans;
}
};

LeetCode 453. Minimum Moves to Equal Array Elements

题目描述:

Given a non-empty integer array of size n, find the minimum number of moves required to make all array elements equal, where a move is incrementing n - 1 elements by 1.

Example:

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

Output:
3

Explanation:
Only three moves are needed (remember each move increments two elements):

[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

有很多种move的策略, 可以任意选择一种. 我的办法是:

  1. 找到当前的最小值与最大值
  2. 增加除了该最大值以外的其他元素, 直到最小值与最大值相等
  3. 如果没有全部相等则回到第一步, 否则结束

这样的话总的move次数应该为每个元素与最小元素之差的和.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int minMoves(vector<int>& nums) {
int ans = 0, nMin = INT_MAX;
for(auto i : nums){
nMin = min(nMin, i);
}
for(auto i : nums){
ans += (i - nMin);
}
return ans;
}
};

LeetCode 452. Minimum Number of Arrows to Burst Balloons

题目描述:

There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided input is the start and end coordinates of the horizontal diameter. Since it’s horizontal, y-coordinates don’t matter and hence the x-coordinates of start and end of the diameter suffice. Start is always smaller than end. There will be at most 104 balloons.

An arrow can be shot up exactly vertically from different points along the x-axis. A balloon with xstart and xend bursts by an arrow shot at x if xstart ≤ x ≤ xend. There is no limit to the number of arrows that can be shot. An arrow once shot keeps travelling up infinitely. The problem is to find the minimum number of arrows that must be shot to burst all balloons.

Example:

1
2
3
4
5
6
7
8
Input:
[[10,16], [2,8], [1,6], [7,12]]

Output:
2

Explanation:
One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons).

基本思路是每次尽可能多的刺破气球, 所以一开始从最左边的气球的右边缘发射一支箭, 否则最左边的气球就无法刺破了, 这样也能保证刺破尽可能多的气球.

但是问题在于如何判断哪个气球在"最左边", 不能以左边缘来进行判断, 因为有这种情况: (1,10),(2,5), 第一支箭应该从x=5发出而不是x=10, 所以排序时应该使用每个气球的右边缘来进行排序.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int findMinArrowShots(vector<pair<int, int>>& points) {
if(points.empty()) return 0;
sort(points.begin(), points.end(), [](pair<int, int> &a, pair<int, int> &b){
return a.second < b.second;
});
int ans = 1, arrow = points[0].second;
for(int i = 1; i < points.size(); i++){
if(points[i].first > arrow){
ans++;
arrow = points[i].second;
}
}
return ans;
}
};

LeetCode 447. Number of Boomerangs

题目描述:

Given n points in the plane that are all pairwise distinct, a “boomerang” is a tuple of points (i, j, k) such that the distance between iand j equals the distance between i and k (the order of the tuple matters).

Find the number of boomerangs. You may assume that n will be at most 500 and coordinates of points are all in the range [-10000, 10000](inclusive).

Example:

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

Output:
2

Explanation:
The two boomerangs are [[1,0],[0,0],[2,0]] and [[1,0],[2,0],[0,0]]

这道题似乎还没有什么好解法, 现在Runtime1368ms都可以beats 100%的C++提交…

现在已经不是了, 1000多ms的Runtime算是比较慢啦, 但是我暂时还没有时间来搞这道题…

使用双重循环+哈希表. 如果认为unordered_map的查询复杂度是O(1), 总体复杂度是O(n^2).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int numberOfBoomerangs(vector<pair<int, int>>& points) {
int ans = 0;
for(int i = 0; i < points.size(); i++){
unordered_map<long long, int> distance;
for(int j = 0; j < points.size(); j++){
if(j == i) continue;
long long d = getDistance(points[i], points[j]);
if(distance.count(d)){
ans += 2 * distance[d];
}
distance[d]++;
}
}
return ans;
}

long long getDistance(pair<int, int> &p1, pair<int, int> &p2){
return (long long)(p1.first - p2.first) * (long long)(p1.first - p2.first)
+ (long long)(p1.second - p2.second) * (long long)(p1.second - p2.second);
}
};

LeetCode 437. 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

这道题用很直接的遍历方法就可以AC, 所以才会是Easy. 我还想了很久有没有O(n)或者O(nlogn)的办法…

对于每一棵子树, 都计算从根节点开始的路径和有没有等于sum的, 这一步用DFS遍历所有路径. 然后递归地处理左子树和右子树.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
int ans = 0;
public:
int pathSum(TreeNode* root, int sum) {
if(!root) return 0;
DFS(root, sum);
pathSum(root->left, sum);
pathSum(root->right, sum);
return ans;
}

void DFS(TreeNode *node, int target){
if(!node) return;
target -= node->val;
if(0 == target) ans++;
DFS(node->left, target);
DFS(node->right, target);
target += node->val;
}
};

LeetCode 421. Maximum XOR of Two Numbers in an Array

题目描述:

Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231.

Find the maximum result of ai XOR aj, where 0 ≤ ij < n.

Could you do this in O(n) runtime?

Example:

1
2
3
4
5
Input: [3, 10, 5, 25, 2, 8]

Output: 28

Explanation: The maximum result is 5 ^ 25 = 28.

这道题是使用递归的方法. 先找到出现不同的最高位, 然后按照该位是0还是1分为两类, 接下来处理下一位, 这时可以把数字分为4类, 分别以00, 01, 1011开头, 最大的异或值会出现在0011, 0110两种组合中, 递归地处理接下来的位数.

有可能出现0011, 0110两种组合中每个组合都至少有一种分类是空的, 这个时候说明该位不可能为1, 只能为0, 可以用循环跳过这些位.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
list<int> one, zero;
int pos;
for(pos = 31; pos >= 0; pos--){
// 找到有不同的最高位
one.clear();
zero.clear();
for(auto i : nums){
if(i & (1 << pos)) one.push_back(i);
else zero.push_back(i);
}
if(!one.empty() && !zero.empty()) break;
}
if(one.empty() || zero.empty()) return 0;

return (1 << pos) + findXORHelper(zero, one, pos - 1);
}

int findXORHelper(list<int> &zero, list<int> &one, int pos){
if(pos < 0) return 0;
list<int> zeroZero, zeroOne, oneZero, oneOne;

for(; pos >= 0; pos--){
// 跳过只能为0的位
zeroZero.clear();
zeroOne.clear();
oneZero.clear();
oneOne.clear();

for(auto i : zero){
if(i & (1 << pos)) zeroOne.push_back(i);
else zeroZero.push_back(i);
}
for(auto i : one){
if(i & (1 << pos)) oneOne.push_back(i);
else oneZero.push_back(i);
}
// 该位可以为1
if(!((zeroZero.empty() || oneOne.empty()) && (oneZero.empty() || zeroOne.empty()))) break;
}
// 所有位数都处理完毕
if(pos < 0) return 0;

return (1 << pos) + max(findXORHelper(zeroZero, oneOne, pos - 1), findXORHelper(oneZero, zeroOne, pos - 1));
}
};

LeetCode 187. Repeated DNA Sequences

题目描述:

All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: “ACGAATTCCG”. When studying DNA, it is sometimes useful to identify repeated sequences within the DNA.

Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule.

For example,

1
2
3
4
Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT",

Return:
["AAAAACCCCC", "CCCCCAAAAA"].

因为序列长度是固定的十位, 所以如果把A, C, G, T对应到1, 2, 3, 4的数字的话, 十位的字符串可以映射到一个整数. 然后就可以用hash表来记录是否出现过相同的字符串.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
vector<string> re;
if(s.size() <= 10) return re;
unordered_map<unsigned int, int> tSet;
for(int i = 0; i <= s.size() - 10; i++){
string str = s.substr(i, 10);
unsigned int t = strToInt(str);
if(tSet.count(t)){
if(tSet[t] == 1) re.push_back(str);
}
tSet[t]++;
}
return re;
}

unsigned int strToInt(string s){
unsigned int re = 0;
for(int i = 0; i < 10; i++){
re *= 5;
re += charToNum(s[i]);
}
return re;
}

unsigned int charToNum(char c){
if(c == 'A') return 1;
else if(c == 'C') return 2;
else if(c == 'G') return 3;
else return 4;
}
};