35岁拿到腾讯11级的offer,总包160w,涨幅15%左右。说实话总包看起来很高,但这个涨幅才15%也太少了,跳槽工资涨幅至少要达到30%以上才有意义,既然目前工作比较稳定,并且年龄也大了,还是不要折腾了。
--------------下面是今天的算法题-------------- 来看下今天的算法题,这题是LeetCode的第40题:组合总和 II。 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次 。输入: candidates = [10,1,2,7,6,1,5], target = 8, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ] 昨天我们刚讲过和这题类似的一道题《组合总和》,不过昨天那道题数组中是没有重复数字的,而今天这题数组中可能会有重复数字,如果有重复数字就会出现重复的组合,所以解这题的关键点是怎么过滤掉重复的组合,我们画个图看下:选择的过程可以把它看作是一棵树,因为每个数字最多只能选择一次,所以选择完当前数字之后下一步要从他的下一个数字开始。上面图中因为有相同的数字,所以结果出现了重复。只需要把重复的剪掉即可。 在计算之前我们需要先对数组排序,这样重复的数字就会挨着了,当出现重复数字的时候,前面数字构成的子集实际上是包含后面数字构成的所有子集,所以当出现重复数字的时候我们需要把后面数字构成的分支剪掉即可。 public List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> ans = new ArrayList<>(); Arrays.sort(candidates);// 先排序 dfs(ans, new ArrayList<>(), candidates, target, 0); return ans; }
private void dfs(List<List<Integer>> ans, List<Integer> path, int[] candidates, int target, int start) { if (target == 0) { ans.add(new ArrayList<>(path)); return; } for (int i = start; i < candidates.length; i++) { // 因为是有序的,后面的值越来越大,直接终止。 if (target < candidates[i]) break; if (i > start && candidates[i] == candidates[i - 1]) continue; // 去掉重复的,后面分支就不要选择了 path.add(candidates[i]);// 选择 dfs(ans, path, candidates, target - candidates[i], i + 1); path.remove(path.size() - 1);// 撤销选择 } }
public: vector<vector<int>> combinationSum2(vector<int> &candidates, int target) { vector<vector<int>> ans; vector<int> path; sort(candidates.begin(), candidates.end());// 先排序 dfs(ans, path, candidates, target, 0); return ans; }
void dfs(vector<vector<int>> &ans, vector<int> &path, vector<int> &candidates, int target, int start) { if (target == 0) { ans.emplace_back(path); return; } for (int i = start; i < candidates.size(); i++) { // 因为是有序的,后面的值越来越大,直接终止。 if (target < candidates[i]) break; if (i > start && candidates[i] == candidates[i - 1]) continue; // 去掉重复的,后面分支就不要选择了 path.emplace_back(candidates[i]);// 选择 dfs(ans, path, candidates, target - candidates[i], i + 1); path.pop_back();// 撤销选择 } }
int cmp(const void *a, const void *b) { return *(const int *) a - *(const int *) b; }
void dfs(int **ans, int *path, int *candidates, int candidatesSize, int target, int start, int *returnSize, int **returnColumnSizes, int pathCount) { if (target == 0) { ans[*returnSize] = (int *) malloc(pathCount * sizeof(int)); memcpy(ans[*returnSize], path, pathCount * sizeof(int)); (*returnColumnSizes)[*returnSize] = pathCount; (*returnSize)++; return; } for (int i = start; i < candidatesSize; i++) { // 因为是有序的,后面的值越来越大,直接终止。 if (target < candidates[i]) break; if (i > start && candidates[i] == candidates[i - 1]) continue; // 去掉重复的,后面分支就不要选择了 path[pathCount++] = candidates[i];// 选择 dfs(ans, path, candidates, candidatesSize, target - candidates[i], i + 1, returnSize, returnColumnSizes, pathCount); --pathCount;// 撤销选择 } }
int **combinationSum2(int *candidates, int candidatesSize, int target, int *returnSize, int **returnColumnSizes) { // 先进行排序 qsort(candidates, candidatesSize, sizeof(int), cmp); int n = 3000; *returnSize = 0; int **ans = malloc(n * sizeof(int *)); *returnColumnSizes = malloc(n * sizeof(int)); int *path = malloc(n * sizeof(int)); dfs(ans, path, candidates, candidatesSize, target, 0, returnSize, returnColumnSizes, 0); return ans; }
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: def backtrack(num: int, start: int): if num == 0: ans.append(path[:]) return for i in range(start, len(candidates)): # 因为是有序的,后面的值越来越大,直接终止。 if num < candidates[i]: break if i > start and candidates[i] == candidates[i - 1]: continue # 去掉重复的,后面分支就不要选择了 path.append(candidates[i]) # 选择 backtrack(num - candidates[i], i + 1) path.pop() # 撤销选择
candidates.sort() # 先对数组进行排序 ans = [] # 需要返回的结果 path = [] backtrack(target, 0) return ans
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。
|