从给定的n位数组中找到所有的数对,从而使得三个数的合为0
输入:n整数数组 输出 数字对的数组,并且数字对各异
思路:emm对于每一个数进行循环,然后2sum?
3sum的特点和2sum不一样,2sum要求和为一个给定的输入值,但是3sum要求和为固定的0
也就是说,其中两者可以当第三者的相反数?2sum循环一遍? 2sum是On。那么3sum就是O?2。可以推导出Ksum的时间复杂度为O??−1
有一个问题:3sum要求出所有的对数,所以固定第一个数后后面的2sum要求出所有的对数,因此要对1的2sum进行改造。
2sum之所以只能求出一个对数,就是因为2sum要求输出的是元素的位置,
但是3sum只要求输出元素组合,因此可以输出所有的对数。
那么问题来了,该怎么改?
2sum将所有的数存到map中,相同的会覆盖。所以经过覆盖我们获得了一个不具有相同数字的vector
因此不存在相同的pair每一个都是不同的。
然后写出的代码是这样的
vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> result; unordered_map<int, int> mapping; int size = nums.size(); for(int i =0 ; i<size; i++){ mapping[nums[i]] = i; //初始化完成 } //3sum for(int i = 0; i< size - 2; i++){ const int target = 0 - nums[i]; //然后开始two sum for(int j = i+1; j< size - 1; j++){ const int gap = target - nums[j]; if(mapping.find(gap) != mapping.end() && mapping[gap] > j){ vector<int> pair; pair.push_back(nums[i]); pair.push_back(gap); pair.push_back(nums[j]); if(find(result.begin(), result.end(),pair ) == result.end()){ result.push_back(pair); } } } } return result; } |
运行比较短的输入还是可以的,但是提交之后就发现超时了。。
既然如此,那么只能看看别人的思路了
vector<vector<int>> threeSum(vector<int>& nums) { std::sort(nums.begin(),nums.end()); vector<vector<int>> v2; for (int c = nums.size()-1; c >= 2; ){ for (int a = 0, b = c-1; a < b; ){ int tmp_sum = nums[a]+nums[b]; if (tmp_sum < -nums[c]){ ++a; } else if (tmp_sum > -nums[c]){ --b; } else { vector<int> v = {nums[a], nums[b], nums[c]}; v2.push_back(v); do{//去重复 a b ++a; } while (a < b && nums[a-1] == nums[a]); do{ --b; } while (a < b && nums[b+1] == nums[b]); } } do{//去重复 c --c; } while (c >= 2 && nums[c+1] == nums[c]); } return v2; } |
执行完毕,发现没超时。来学习一下先进经验吧。
首先,排序。排序保证了后面的过程中可以忽略已经被考虑过的元素。而导致超时的测试用例就是那些输入串很长的用例。
因此从这一点上修改我原来的代码,终于通过了
vector<vector<int>> threeSum(vector<int>& nums) { sort(nums.begin(),nums.end()); vector<vector<int>> result; unordered_map<int, int> mapping; int size = nums.size(); for(int i = 0 ; i < size; i++) mapping[nums[i]] = i; //初始化完成 //3sum int lastNumI = 0; int lastNumJ = 0; for(int i = 0; i< size - 2; i++){ if(i!=0 && lastNumI == nums[i]) continue; else lastNumI = nums[i]; const int target = 0 - nums[i]; //然后开始two sum for(int j = i+1; j< size - 1; j++){ if(j!= i+1 && lastNumJ == nums[j]) continue; else lastNumJ = nums[j]; const int gap = target - nums[j]; if(mapping.find(gap) != mapping.end() && mapping[gap] > j){ vector<int> pair = {nums[i], nums[j],gap}; result.push_back(pair); } } } return result; } |
由于是按顺序的,因此通过判断相邻元素相同来忽略所有考虑过的数字。
但是程序的运行速度还算可以,因此再来看有什么其他的因素。
三个指针分别代表三个数,然后对第一个指针进行遍历,对另外两个指阵寻找2sum。值得注意的是2sum的方式。首先经过排序,整个nums的顺序是从左至右由小变大,然后跟11题一样,如果两数之和大于想要的数,那么右数左移,反之左数右移。然后两数相遇,则推出里循环,寻找下一个。