leetcode题解日练--2016.7.10
2016-07-10 21:17
393 查看
日练三题,冰冻三尺非一日之寒。
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
Find the minimum element.
You may assume no duplicate exists in the array.
题意:一个已经经过移位的数组,找出其中最小的元素,假设没有重复元素的存在。
思路:
1、一个时间复杂度为O(N)的方法,遍历数组的元素,如果如果后一个元素比前一个小,那么那个就是我们要找的。
结果:4ms
2、直接用二分查找的思想,mid=left+(right-left)/2;如果nums[mid]>nums[right],就说明待查找元素在mid位置元素的右边,left=mid+1,如果nums[mid]
结果:4ms
For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
题意:给定n和k,写出所有n个数中选k个数的组合。
思路:
1、直接回溯就可以搞定
结果:580ms
居然这么慢,应该是有什么地方没写对
稍微改进下
结果:92ms
2、再做一点改进
结果:84ms
3、利用k元素子集的方法去做
换个角度来看,从1-n中选择k个数字进行组合。n=4,k=2是不是某种程度上等价于
XXXX这样一个四位二进制数,其中只能某两位取1,有多少种组合相比不难得出,就是C4取2种,那么如何获得这些可能性呢?
分四步来解决:
1、求出最低位的1开始的连续的1的区间(00010111->00000111);
2、将这一区间全部变为0,并将区间左侧的那个0变为1(00010111->00011000);
3、将第1步里取出的区间右移,直到剩下的1个数减少1个(00000111->00000011);
4、将第2步和第3步的结果按位取或(00011000|00000011-> 00011011)。
伪代码如下:
结果:100ms
Example 1
Input: “2-1-1”.
((2-1)-1) = 0
(2-(1-1)) = 2
Output: [0, 2]
Example 2
Input: “2*3-4*5”
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
Output: [-34, -14, -10, -10, 10]
题意:寻找不同的加括号方式,将可能得到的计算值返回。
思路:
1、首先思考一个问题,先不考虑如何计算具体加括号之后的数值,先想下有多少种加括号的方法?经过试验:有1个运算符只有一个划分方式,有2个运算符有2种划分方式,有3个运算符有5种划分方式。是不是对这个式子很熟悉,没错,加括号的方法数就是一个卡特兰数,当算式中有n个运算符的时候,一共就有f(n)=(2n!)/(n!)(n+1)!种的加括号方法,那么如何做才能做到不重复不遗漏的将每种情况都考虑进来呢?
首先,问题整体来看有些复杂,这个时候可以尝试去找下问题的子问题,从一个简单栗子来看:
2*5-3,这个式子只有2个运算符,我想一个小学毕业生应该都能知道划分方式有((2*5)-3)=7和(2*(5-3))=4这两种,两个运算符是很好看出来的,我们首先考虑第一个号,我们将号左边的结果记为res1,*号右边的结果记为res2,那么我们只需要先后计算出res1和res2,然后最后再两者相乘就是最终结果,而计算res1和res2中结果的时候,也是一样的思想,对res1中的每个算式遍历一次,对每次选中的运算符又分成两个子问题,这样递归的执行下去直到平凡情况也就是res1和res2都是一个数值,这个时候直接对两个数进行四则运算就可以了。
基于以上思路写出如下代码:
结果:8ms
2、详见https://discuss.leetcode.com/topic/19906/c-4ms-recursive-dp-solution-with-brief-explanation
DP的思路,开创额外的空间用来保存子式的中间计算的结果,栗如:3*3*3,在以第一个分割的时候,会保存dp[“3”]=3,dp[“3*3”]=9,然后在以第二个分割的时候,左边的3*3和右边的3都在dp中有所保存,直接调用无需再去递归求。
结果:4ms
For example,
[1,2,3] have the following permutations:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
题意:写出一个列表中元素所有排列可能性
1、python有直接现成的库可以调用
结果:86ms
2、但是做题不能总是import,so考虑用下迭代器
结果:92ms
3、可以用DFS来做
结果:12ms
今日题目:1、在一个旋转的数组中找最小数;❤2、数的组合(k元素子集);3、不同的加括号方式;4、数的排列。
今日摘录:最初我们来到这个世界,是因为不得不来;最终我们离开这个世界,是因为不得不走。做人不能忘记四条,话不要说错,床不要睡错,门槛不要踏错,口袋不要摸错。 —《活着》
153. Find Minimum in Rotated Sorted Array | Difficulty: Medium
Suppose a sorted array 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).
Find the minimum element.
You may assume no duplicate exists in the array.
题意:一个已经经过移位的数组,找出其中最小的元素,假设没有重复元素的存在。
思路:
1、一个时间复杂度为O(N)的方法,遍历数组的元素,如果如果后一个元素比前一个小,那么那个就是我们要找的。
class Solution { public: int findMin(vector<int>& nums) { for(int i=0;i<nums.size()-1;i++) { if(nums[i]>nums[i+1]) return nums[i+1]; } return nums[0]; } };
结果:4ms
2、直接用二分查找的思想,mid=left+(right-left)/2;如果nums[mid]>nums[right],就说明待查找元素在mid位置元素的右边,left=mid+1,如果nums[mid]
class Solution { public: int findMin(vector<int>& nums) { int left = 0,right=nums.size()-1; while(left<right) { //if(nums[left]<nums[right]) return nums[left]; int mid = left+(right-left)/2; if(nums[mid]>nums[right]) left = mid+1; else right = mid; } return nums[left]; } };
结果:4ms
77. Combinations | Difficulty: Medium
Given two integers n and k, return all possible combinations of k numbers out of 1 … n.For example,
If n = 4 and k = 2, a solution is:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
题意:给定n和k,写出所有n个数中选k个数的组合。
思路:
1、直接回溯就可以搞定
class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> res; vector<int> tmp; combineSub(res,tmp,n,k); return res; } void combineSub(vector<vector<int>> &res,vector<int>tmp,int n,int k ) { if(k==0) { res.push_back(tmp); return; } if(k>0) { for(int i=tmp.empty()?1:tmp.back()+1;i<=n;i++) { tmp.push_back(i); combineSub(res,tmp,n,k-1); tmp.pop_back(); } } } };
结果:580ms
居然这么慢,应该是有什么地方没写对
稍微改进下
class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> res; vector<int> tmp(k,0); combineSub(res,tmp,1,n,k); return res; } void combineSub(vector<vector<int>> &res,vector<int>tmp,int start,int n,int k ) { if(k==0) { res.push_back(tmp); return; } for(int i=start;i<=n-k+1;i++) { tmp[tmp.size()-k]=i; combineSub(res,tmp,i+1,n,k-1); tmp[tmp.size()-1] = 0; } } };
结果:92ms
2、再做一点改进
class Solution { public: vector<vector<int> > combine(int n, int k) { // dfs method if(k == 0 || n == 0 || n<k) return result; v.resize(k); combinecore(1, n, k); return result; } void combinecore(int start, int n, int k) { int i = start; while(i<=(n-k+1)) { v[v.size()-k] = i; i++; if(k>1) combinecore(i, n, k-1); else result.push_back(v); } } private: vector<int> v; vector<vector<int> > result; };
结果:84ms
3、利用k元素子集的方法去做
换个角度来看,从1-n中选择k个数字进行组合。n=4,k=2是不是某种程度上等价于
XXXX这样一个四位二进制数,其中只能某两位取1,有多少种组合相比不难得出,就是C4取2种,那么如何获得这些可能性呢?
分四步来解决:
1、求出最低位的1开始的连续的1的区间(00010111->00000111);
2、将这一区间全部变为0,并将区间左侧的那个0变为1(00010111->00011000);
3、将第1步里取出的区间右移,直到剩下的1个数减少1个(00000111->00000011);
4、将第2步和第3步的结果按位取或(00011000|00000011-> 00011011)。
伪代码如下:
int comb=(1<<k)-1; //最小的k元素子集 while(comb < 1<<n) { //处理每一个子集 …… int x=comb & -comb; //x表示最低位1的位置 int y=comb + x; //y表示4步中的第2步 comb= (((comb&~y)/x )>>1)|y; }
class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> result; int comb=(1<<k)-1; while(comb< 1<<n) { vector<int> r; for(int j=0;j<n;j++) { if(comb>>j&1) r.push_back(j+1); } result.push_back(r); int x=comb&-comb; int y=comb+x; comb = (((comb&~y)/x)>>1)|y; } return result; } };
结果:100ms
241. Different Ways to Add Parentheses | Difficulty: Medium
Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *.Example 1
Input: “2-1-1”.
((2-1)-1) = 0
(2-(1-1)) = 2
Output: [0, 2]
Example 2
Input: “2*3-4*5”
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
Output: [-34, -14, -10, -10, 10]
题意:寻找不同的加括号方式,将可能得到的计算值返回。
思路:
1、首先思考一个问题,先不考虑如何计算具体加括号之后的数值,先想下有多少种加括号的方法?经过试验:有1个运算符只有一个划分方式,有2个运算符有2种划分方式,有3个运算符有5种划分方式。是不是对这个式子很熟悉,没错,加括号的方法数就是一个卡特兰数,当算式中有n个运算符的时候,一共就有f(n)=(2n!)/(n!)(n+1)!种的加括号方法,那么如何做才能做到不重复不遗漏的将每种情况都考虑进来呢?
首先,问题整体来看有些复杂,这个时候可以尝试去找下问题的子问题,从一个简单栗子来看:
2*5-3,这个式子只有2个运算符,我想一个小学毕业生应该都能知道划分方式有((2*5)-3)=7和(2*(5-3))=4这两种,两个运算符是很好看出来的,我们首先考虑第一个号,我们将号左边的结果记为res1,*号右边的结果记为res2,那么我们只需要先后计算出res1和res2,然后最后再两者相乘就是最终结果,而计算res1和res2中结果的时候,也是一样的思想,对res1中的每个算式遍历一次,对每次选中的运算符又分成两个子问题,这样递归的执行下去直到平凡情况也就是res1和res2都是一个数值,这个时候直接对两个数进行四则运算就可以了。
基于以上思路写出如下代码:
class Solution { public: vector<int> diffWaysToCompute(string input) { vector<int> res; for(int i=0;i<input.length();i++) { if(input[i]=='+'||input[i]=='-'||input[i]=='*') { string inputLeft = input.substr(0,i); string inputRight = input.substr(i+1,input.length()-i-1); vector<int> resLeft = diffWaysToCompute(inputLeft); vector<int> resRight = diffWaysToCompute(inputRight); for(auto res1:resLeft) for(auto res2:resRight) { switch (input[i]) { case '+': res.push_back(res1+res2); break; case '-': res.push_back(res1-res2); break; default: res.push_back(res1*res2); } } } } if (res.empty()) { res.push_back(stoi(input)); } return res; } };
结果:8ms
2、详见https://discuss.leetcode.com/topic/19906/c-4ms-recursive-dp-solution-with-brief-explanation
DP的思路,开创额外的空间用来保存子式的中间计算的结果,栗如:3*3*3,在以第一个分割的时候,会保存dp[“3”]=3,dp[“3*3”]=9,然后在以第二个分割的时候,左边的3*3和右边的3都在dp中有所保存,直接调用无需再去递归求。
class Solution { public: vector<int> diffWaysToCompute(string input) { unordered_map<string, vector<int>> dp; return computeWithDP(input, dp); } vector<int> computeWithDP(string input, unordered_map<string, vector<int>> &dp) { vector<int> res; for(int i=0;i<input.length();i++) { if(input[i]=='+'||input[i]=='-'||input[i]=='*') { vector<int> resultLeft,resultRight; string inputLeft = input.substr(0,i); if(dp.find(inputLeft)!=dp.end()) resultLeft = dp[inputLeft]; else resultLeft = computeWithDP(inputLeft,dp); string inputRight = input.substr(i+1,input.length()-i-1); if(dp.find(inputRight)!=dp.end()) resultRight = dp[inputRight]; else resultRight = computeWithDP(inputRight,dp); for(auto res1:resultLeft) for(auto res2:resultRight) { switch (input[i]) { case '+': res.push_back(res1+res2); break; case '-': res.push_back(res1-res2); break; default: res.push_back(res1*res2); } } } } if (res.empty()) { res.push_back(stoi(input)); } dp[input] = res; return dp[input]; } };
结果:4ms
46. Permutations | Difficulty: Medium
Given a collection of distinct numbers, return all possible permutations.For example,
[1,2,3] have the following permutations:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
题意:写出一个列表中元素所有排列可能性
1、python有直接现成的库可以调用
class Solution(object): def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ import itertools permute = list(itertools.permutations(nums)) permute = [list(i) for i in permute] return permute
结果:86ms
2、但是做题不能总是import,so考虑用下迭代器
class Solution(object): def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ def gen(nums): if not nums: yield [] for i, n in enumerate(nums): for p in gen(nums[:i] + nums[i+1:]): yield + p return list(gen(nums))
结果:92ms
3、可以用DFS来做
class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; dfs(nums,0,res); return res; } void dfs(vector<int>& nums,int start,vector<vector<int>>& res) { if(start>=nums.size()) { res.push_back(nums); return ; } for(int i=start;i<nums.size();i++) { swap(nums[i],nums[start]); dfs(nums,start+1,res); swap(nums[i],nums[start]); } } };
结果:12ms