复杂度的分析
今日电面F,代码相对顺利写出。
但run test by hand以及复杂度分析陷入黑洞,在多次提示下算是给出答案。
所以,要好好总结
1,run test case by hand,在平时刷题时要加以练习。
要学会些main(),并且打印结果!!!//
z再分析下排列组合subset类
但run test by hand以及复杂度分析陷入黑洞,在多次提示下算是给出答案。
所以,要好好总结
1,run test case by hand,在平时刷题时要加以练习。
要学会些main(),并且打印结果!!!//
| |
对于DFS,还有recursion,他们的复杂度一直没有搞的太清楚,所以今天出现了黑洞。 再借此分析下 number of island
元素被访问了一遍,所以是O(m * n).
DFS的最大深度是 max(m, n),所以是系统stack memory space O(max(m, n)). heap是O(1)。
const int dir = 4;
const vector<int> dx = {0, 0, -1, 1};
const vector<int> dy = {-1, 1, 0, 0};
int numIslands(vector<vector<bool>> &grid) {
// write your code here
const int m = grid.size();
if(m == 0){
return 0;
}
const int n = grid[0].size();
int res = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 1){
res++;
dfs(i, j, grid);
}
}
}
return res;
}
void dfs(int i, int j, vector<vector<bool>> &grid){
//if(i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == 0){
// return;
//}
//grid[i][j] = 0;
//for(int k = 0; k < dir; k++){
// dfs(i + dx[k], j + dy[k], grid);
//}
//return;
//把条件放在里面
grid[i][j] = 0;
for(int k = 0; k < dir; k++){
int newX = i + dx[k];
int newY = j + dy[k];
if(newX < 0 || newY < 0 || newX >= grid.size() || newY >= grid[0].size() || grid[newX][newY] == 0){
continue;
}
//grid[newX][newY] = 0;
dfs(newX, newY, grid);
}
return;
}
再分析下排列,组合,subset类的DFS解法复杂度,和graph,tree是不太一样。该类型的时间复杂度是就是所有可能性的个数,也就是结果的个数,因为要找到所有结果,就要遍历到。
所以时间复杂度 是O(2^N).
因recursion消耗的系统stack 空间复杂度是O(N),recursion的深度。
class Solution {
public:
/**
* @param nums: the given array
* @param s: the given target
* @return: the number of ways to assign symbols to make sum of integers equal to target S
*/
int findTargetSumWays(vector<int> &nums, int s) {
// Write your code here
//完美的DFS类 combination类型,复杂度O(2^n), 每个元素有两种选择,- or +。
//所有可能的情况个数是 2^n.
//和425. Letter Combinations of a Phone Number思路一样
const int size = nums.size();
if(size == 0){
return 0;
}
int cnt = 0;
dfs(nums, s, 0, 0, cnt);
return cnt;
}
void dfs(vector<int> &nums, int s, int idx, int sum, int &cnt){
if(idx == nums.size()){
if(sum == s){
cnt++;
}
return;
}
dfs(nums, s, idx + 1, sum + nums[idx], cnt);
dfs(nums, s, idx + 1, sum - nums[idx], cnt);
return;
}
|
这是permutation题目
结果个数是N!,所以时间复杂度是O(N!).
结果大小是N,总的结果空间复杂度是O(N*N!).
系统stack memory消耗O(N);
class Solution {
public:
/*
* @param nums: A list of integers.
* @return: A list of permutations.
*/
vector<vector<int>> permute(vector<int> &nums) {
// write your code here
const int size = nums.size();
vector<vector<int>> res;
if(size == 0){
res.push_back(vector<int>());
return res;
}
vector<int> resElem;
vector<int> visited(size, 0);
dfs(nums, res, resElem, visited);
return res;
}
void dfs(vector<int> &nums, vector<vector<int>> &res, vector<int> &resElem, vector<int> &visited){
if(resElem.size() == nums.size()){
res.push_back(resElem);
return;
}
for(int i = 0; i < nums.size(); i++){
if(visited[i]){
continue;
}
resElem.push_back(nums[i]);
visited[i] = 1;
dfs(nums, res, resElem, visited);
visited[i] = 0;
resElem.pop_back();
}
return;
}
};
subset类型的复杂度
有两种解法,但两种解法的复杂度为啥感觉不一样呢
第一种用了for循环,感觉复杂度是O(N!)
class Solution { public: /** * @param nums: A set of numbers * @return: A list of lists */ vector<vector<int>> subsets(vector<int> &nums) { // write your code here vector<vector<int>> res; vector<int> resElem; const int size = nums.size(); if(size == 0){ res.push_back(resElem); return res; } dfs(nums, res, resElem, 0); return res; } void dfs(vector<int> &nums, vector<vector<int>> &res, vector<int> &resElem, int start){ res.push_back(resElem); for(int i = start; i < nums.size(); i++){ // resElem.push_back(nums[i]); dfs(nums, res, resElem, i + 1); resElem.pop_back(); } return; } };
第二种是
复杂度是O(2^N),因为有2^N种可能。代码实现也是2^N,可以解释,dfs内核是2,dfs深度是N,所以2^N.
感觉2^N是对的,但第一种方法看出来是2^N。奇怪
vector<vector<int>> subsets(vector<int> &nums) { // write your code here vector<vector<int>> res; vector<int> resElem; const int size = nums.size(); sort(nums.begin(), nums.end()); dfs(nums, res, resElem, 0); return res; } void dfs(vector<int> &nums, vector<vector<int>> &res, vector<int> &resElem, int start){ if(start == nums.size()){ res.push_back(resElem); return; } //for(int i = start; i < nums.size(); i++){ dfs(nums, res, resElem, start + 1); resElem.push_back(nums[start]); dfs(nums, res, resElem, start + 1); resElem.pop_back(); //} return; }
653. Expression Add Operators
vector<string> addOperators(string &num, int target) { // write your code here // 这是一道典型的DFS应用 // 拆分挡板放在哪里的问题。然后还加上中间选择的3个可能。 // 所以复杂度是4^N (不拆,放+,-,*,4个可能) vector<string> res; dfs(num, target, 0, "", res, 0, 0); return res; } void dfs(string &num, int target, int sum, string path, vector<string> &res, int start, int val){ if(start == num.size()){ if(sum == target){ res.push_back(path); } return; } for(int i = start; i < num.size(); i++){ // if(num[start] == '0'){ // substr only 0 if(path.size() == 0){ dfs(num, target, sum, path + "0", res, start + 1, 0); } else{ dfs(num, target, sum, path + "+0", res, start + 1, 0); dfs(num, target, sum, path + "-0", res, start + 1, 0); dfs(num, target, sum - val, path + "*0", res, start + 1, 0); // } break; } else{ string sub = num.substr(start, i - start + 1); long long subV = stoll(sub); if(path.size() == 0){ dfs(num, target, sum + subV, path + sub, res, i + 1, subV); } else{ dfs(num, target, sum + subV, path + "+" + sub, res, i + 1, subV); dfs(num, target, sum - subV, path + "-" + sub, res, i + 1, -subV); dfs(num, target, sum - val + val * subV, path + "*" + sub, res, i + 1, subV * val); } } } return; }
subset类型的复杂度
有两种解法,但两种解法的复杂度为啥感觉不一样呢
第一种用了for循环,感觉复杂度是O(N!)
class Solution { public: /** * @param nums: A set of numbers * @return: A list of lists */ vector<vector<int>> subsets(vector<int> &nums) { // write your code here vector<vector<int>> res; vector<int> resElem; const int size = nums.size(); if(size == 0){ res.push_back(resElem); return res; } dfs(nums, res, resElem, 0); return res; } void dfs(vector<int> &nums, vector<vector<int>> &res, vector<int> &resElem, int start){ res.push_back(resElem); for(int i = start; i < nums.size(); i++){ // resElem.push_back(nums[i]); dfs(nums, res, resElem, i + 1); resElem.pop_back(); } return; } };
第二种是
复杂度是O(2^N),因为有2^N种可能。代码实现也是2^N,可以解释,dfs内核是2,dfs深度是N,所以2^N.
感觉2^N是对的,但第一种方法看出来是2^N。奇怪
vector<vector<int>> subsets(vector<int> &nums) { // write your code here vector<vector<int>> res; vector<int> resElem; const int size = nums.size(); sort(nums.begin(), nums.end()); dfs(nums, res, resElem, 0); return res; } void dfs(vector<int> &nums, vector<vector<int>> &res, vector<int> &resElem, int start){ if(start == nums.size()){ res.push_back(resElem); return; } //for(int i = start; i < nums.size(); i++){ dfs(nums, res, resElem, start + 1); resElem.push_back(nums[start]); dfs(nums, res, resElem, start + 1); resElem.pop_back(); //} return; }
653. Expression Add Operators
vector<string> addOperators(string &num, int target) { // write your code here // 这是一道典型的DFS应用 // 拆分挡板放在哪里的问题。然后还加上中间选择的3个可能。 // 所以复杂度是4^N (不拆,放+,-,*,4个可能) vector<string> res; dfs(num, target, 0, "", res, 0, 0); return res; } void dfs(string &num, int target, int sum, string path, vector<string> &res, int start, int val){ if(start == num.size()){ if(sum == target){ res.push_back(path); } return; } for(int i = start; i < num.size(); i++){ // if(num[start] == '0'){ // substr only 0 if(path.size() == 0){ dfs(num, target, sum, path + "0", res, start + 1, 0); } else{ dfs(num, target, sum, path + "+0", res, start + 1, 0); dfs(num, target, sum, path + "-0", res, start + 1, 0); dfs(num, target, sum - val, path + "*0", res, start + 1, 0); // } break; } else{ string sub = num.substr(start, i - start + 1); long long subV = stoll(sub); if(path.size() == 0){ dfs(num, target, sum + subV, path + sub, res, i + 1, subV); } else{ dfs(num, target, sum + subV, path + "+" + sub, res, i + 1, subV); dfs(num, target, sum - subV, path + "-" + sub, res, i + 1, -subV); dfs(num, target, sum - val + val * subV, path + "*" + sub, res, i + 1, subV * val); } } } return; }
Comments
Post a Comment