本文首发在我的个人博客:https://jlice.top/p/7qvfi/。欢迎大家前去参观,么么哒~
N-sum 问题还是比较典型的,这里进行一下小结。
首先描述一下 N-sum 问题:有一个数组 nums,要求从数组中选择 n 个数,使得这些数的和恰好为 target ,输出所有不重复的可行组合。
如果采用暴力解法,显然时间复杂度为 \(O(N^n)\),这一般是不可取的。
下面是 N-sum 问题的LeeCode链接:
Two-Sum
先来解决Two-Sum问题,这是N-sum问题的基础。如果我们能把Two-Sum的时间复杂度降为 \(O(N)\) ,然后就能把N-sum的时间复杂度降为 \(O(N^{n-1})\) 了。
如果采用暴力解法,每次选择一个数时,都要遍历数组来选择另一个数,并判断和是否为 target,这样显然是低效的。当我们选择一个数 x 时,我们希望数组里有一个数为 target - x。为了快速判断数组里是否有某个数,可以用 HashSet。但是,这样存在问题,因为数组里可能有重复的数,当 x 等于 target - x 时,这种做法就错了。因此,正确的做法应该是用 HashMap,用来存储各个数还有多少可用。
双指针法
另一种解法是使用“双指针”,这种解法要求 nums 是有序的。例如,nums 为 [1, 2, 4, 7, 13, 16],target 为 15。初始时,指针p、q位于两端:
如果p、q所指向的值之和大于 target,那么q往左移,这是因为q左侧的值比q所指的值小,所以q往左移能使得之和变小; 如果之和小于 target,那么p往右移,这是因为p右侧的值比p所指的值大,所以p往右移能使得之和变大; 如果之和等于 target,那么p往右移且q往左移(有点像夹挤),这个稍微有点难理解,这也是“双指针”法的思想精髓。首先,如果p往左移或q往右移,其实就回到了之前的状态;其次,如果只是p往右移,显然之和会大于 target,如果只是q往左移,显然之和会小于 target;于是乎,p往右移且q往左移。
然后就是考虑如何判断可行组合是否重复。由于数组是有序的,可行组合的加入也是有序的,因此,只需判断当前可行组合与最后一个已加入的可行组合是否重复即可。
代码
vector<vector<int>> twoSum