您的位置:首页 > 编程语言

leetcode题解日练--2016.7.10

2016-07-10 21:17 393 查看
日练三题,冰冻三尺非一日之寒。

今日题目: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode 日记 编程