这个题目还是nsum类型的,不过比上个题更难。上个题的复杂性不是很高,因为不需要考虑到元素的重复性对结果造成干扰,但这道题的话,还是在“集合”里面找出 nsum = target, 每个元素只能使用一次且最后的结果不能重复。那么对于“集合”里面出现的重复的元素的处理就特别棘手。其实代码和上个题的差不多,其实只多了几行,但是这几行代码我却想了一天,甚至睡觉前还想了一段时间,但是下午在做实验的时候突然大脑一闪,突然想到了思路,我今天刚好想到了二叉树的后续非递归遍历的做法,我在这个题也是借鉴相似的做法。不说别的了,说说思路吧:
我们知道上个题的代码是每次递归传递进去的index和本次的index相同,但是这个题index传递进去的时候要加1,其实这样还远远达不到要求,想想看,“集合”中的重复元素还是没有排除掉,比如集合是“1, 1, 2, 5, 6, 7 ,10”而 target=10,按上题的做法结果肯定会有重复项,‘1, 2, 5’‘1, 7’,因为集合中有两个‘1’,那么我们该如何解决呢?我也想到了一种办法,可以解决问题,但是时间复杂度不够,主要代码如下:
if (target == 0) { int end = ret.size(); if (end == 0 || tmp != ret[end - 1]) { ret.push_back(tmp); } return; }
这样的做法的结果应该是对的,但是重复的判断还是做了,这样没有从根本上解决问题。
真正的做法是使用一个prev记录从vector里面删掉的数,若再次出现这个数就说明该数会导致结果的重复出现,所以当遇到这个数的时候我们跳过,这样就解决问题了。代码如下:
class Solution { public: vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { /* 这个题和上个题差不多吧 */ vector<vector<int>> ret; ret.clear(); vector<int> tmp; tmp.clear(); sort(candidates.begin(), candidates.end()); _findAllSolve(candidates, ret, tmp, target, 0); return ret; } void _findAllSolve(vector<int>& candidates, vector<vector<int>>& ret, vector<int>& tmp, int target, int index) { if (target == 0) { /* int end = ret.size(); if (end == 0 || tmp != ret[end - 1]) { ret.push_back(tmp); } */ ret.push_back(tmp); return; } else { int count = 0; int prev = 0; //保存tmp.back();可以用它来过滤掉重复元素 for (int i = index; i < candidates.size(); ++i) { if (candidates[i] > target) { return; } if (candidates[i] != prev) { tmp.push_back(candidates[i]); } else { continue; } _findAllSolve(candidates, ret, tmp, target - candidates[i], i + 1); //保存此次vector尾部元素,若下次再遇到该元素直接跳过,避免重复,去掉重复的结果全靠prev prev = tmp.back(); tmp.pop_back(); } } } };
相应的结果如下:
时间: 2024-11-10 11:58:49