第一道
题目名称:78. Subsets
题目难度:Medium
题目描述:Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,3], a solution is:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
题目分析:
题目的意思就是给定一个序列,找出该序列的所有子序列。子序列或者是给定序列本身,或者是给定序列删除其中一些元素之后的序列。由于每删除一个元素获得一个新的子序列,且删除不同元素得到的子序列不同。由此想到了可以用递归解法。
递归的每一层中依次删除当前序列的所有位的值,每做一次删除,就进行下一层的递归。直至待操作序列为空。
然而这个递归算法有一个问题需要解决,那就是递归过程中会产生重复的值。如例子中给的[1,2,3]。第一层递归之后产生了[1,2]、[1,3]、[2,3]三个子序列,对[1,2]进行第二层递归操作,得[1]、[2],对[1,3]进行第二层递归操作,得[1]、[3],明显子序列[1]被重复生成了。
为了解决这个问题,笔者最初的想法是先将每次生成的子序列临时放进set容器中,利用set容器的自动排重特性去剔除掉重复的值。此时的代码是:
class Solution {
public:
void getAll(set<vector<int> > & tempS, vector<int> nums) {
tempS.insert(nums);
for (vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
vector<int> temp(nums);
nums.erase(it);
getAll(tempS, nums);
nums = temp;
}
}
vector<vector<int> > subsets(vector<int>& nums) {
set<vector<int> > tempS;
vector<vector<int> > result;
getAll(tempS, nums);
for (set<vector<int> >::iterator it = tempS.begin(); it != tempS.end(); ++it)
result.push_back(*it);
return result;
}
};
然而,此代码最主要的缺点就是严重耗时。当给定的序列的大小超过10时,该算法需要好几秒的时间才能得出结果。究其原因,应该是set容器中对元素进行排序和排重操作耗时太久,导致算法性能的下降,过不了后面序列较大的测试样例。
于是采用另一种方法,避免重复生成子序列。每次递归的时候,通过用一个变量记录上一层操作的数的index,来确定本层应当从哪里开始。避免了重复操作。
最后AC的代码如下:
class Solution {
public:
void getSubs(vector<vector<int> > & result, vector<int> & sub, int start, vector<int> & nums) {
result.push_back(sub);
for (int i = start; i < nums.size(); ++i) {
sub.push_back(nums[i]);
getSubs(result, sub, i + 1, nums);
sub.pop_back();
}
}
vector<vector<int> > subsets(vector<int>& nums) {
vector<vector<int> > result;
vector<int> sub;
getSubs(result, sub, 0, nums);
return result;
}
};
第二道
题目名称:64. Minimum Path Sum
题目难度:Medium
题目描述:Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Example 1:
[[1,3,1],
[1,5,1],
[4,2,1]]
Given the above grid map, return 7. Because the path 1→3→1→1→1
minimizes the sum.
题目分析:
题目的意思是让我们从左上角出发,搜索所有到达右下角的路径,找出路径长度最短的一条路的总长度。
思路之一是利用启发式A*搜索,降低搜索的次数,找到最优解,这种做法理论上来说是可以的。但是仔细观察本题之后可以发现,这是一道较典型的动态规划题。每一次,我们要找走到当前位置的最短路径的长度,只需要在当前位置的前一步中找到一个最小值即可。在本题中,因为每一个当前步的前一步只有两种情况,所以当前步的最小值是这两种情况的最小值中加上走完当前步需要的路径。
具体的代码如下:
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
vector<vector<int>> sum(m, vector<int>(n, grid[0][0]));
for (int i = 1; i < m; ++i) {
sum[i][0] = sum[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; ++j) {
sum[0][j] = sum[0][j - 1] + grid[0][j];
}
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
sum[i][j] = min(sum[i - 1][j], sum[i][j - 1]) + grid[i][j];
}
}
return sum[m-1][n-1];
}
};