三数之和
15. 三数之和 - 力扣(LeetCode) (leetcode-cn.com)
这个题最难的地方在于去重的实现。如果想实现去重,就必须对结果进行记录,还需要有一个比较的环节。
首先第一个解法是哈希+排序,很显然,炸了
public List<List<Integer>> threeSum(int[] nums) {
int numsLen = nums.length;
Map<Integer, List<Integer>> sum1_2 = new HashMap<>();
Arrays.sort(nums);
Set<List<Integer>> checkRes = new HashSet<>();
List<List<Integer>> res =new ArrayList<>();
for (int i = 0; i < numsLen; ++i) {
for (int j = i + 1; j < numsLen; ++j) {
List<Integer> temp = sum1_2.getOrDefault(nums[i] + nums[j],new ArrayList<>());
temp.add(i);
temp.add(j);
sum1_2.put(nums[i] + nums[j],temp);
}
}
for (int i = 0; i < numsLen; ++i) {
if (sum1_2.containsKey(-nums[i])) {
List<Integer> temp = sum1_2.get(-nums[i]);
for (int j = 1; j < temp.size(); j+=2) {
int temp2 = temp.get(j);
if (i > temp2) {
int temp1 = temp.get(j - 1);
List<Integer> resTemp = new ArrayList<>();
List<Integer> checkString = new ArrayList<>();
checkString.add(nums[temp1]);
checkString.add(nums[temp2]);
checkString.add(nums[i]);
if (!checkRes.contains(checkString)) {
// resTemp.add(nums[temp1]);
// resTemp.add(nums[temp2]);
// resTemp.add(nums[i]);
res.add(checkString);
checkRes.add(checkString);
}
}
}
}
}
return res;
}
解法二:排序加滑动窗口
这里去重的思路值得深入思考,本质上是固定一个,在其余的情况中找剩下的。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums); //对数组进行排序,求和时,窗口有序可以让滑动窗口变得有意义
int numsLen = nums.length;
for (int i = 0; i < numsLen; ++i) {
int left = i + 1,right = numsLen - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] == 0) { //找到一组解
// List<Integer> temp = new ArrayList<>();
// temp.add(nums[i]);
//加入集合
while(left < right && nums[left] == nums[left + 1]) { //和right等价的一切数都可以掠过不管
left++;
}
while(left < right && nums[right] == nums[right - 1]) {//和left等价的一切数都可以掠过不管
right--;
}
//移到下一处
res.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right])));
left++;
right--;
}
else if (nums[i] + nums[left] + nums[right] > 0) { //总和太大,让右左移减小结果
while(left < right && nums[right] == nums[right - 1]) {//和left等价的一切数都可以掠过不管
right--;
}
right--;
}
else {
while(left < right && nums[left] == nums[left + 1]) { //和right等价的一切数都可以掠过不管
left++;
}
left++;
}
}
while(i < numsLen - 1 && nums[i+1] == nums[i]) {++i; };
}
return res;
}
18. 四数之和 - 力扣(LeetCode) (leetcode-cn.com)
这个题目是上面的题目的升级版。把三数之和升级为四数之和,基本思路是一样的,都是吧复杂度降低一个数量级。本身滑动窗口就避免了遍历,可以用来降数量级。
解法一:排序+滑动窗口
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
int numsLen = nums.length;
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < numsLen; ++i) {
for (int j = i + 1 ; j < numsLen; ++j) {
int left = j + 1,right = numsLen - 1;
while( left < right) {
if (nums[i] + nums[j] + nums[left] + nums[right] == target) {
res.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));
while (left < right && nums[left + 1] == nums[left]) {
++left;
}
while (left < right && nums[right - 1] == nums[right]) {
--right;
}
++left;
--right;
}
else if (nums[i] + nums[j] + nums[left] + nums[right] < target) {
while (left < right && nums[left + 1] == nums[left]) {
++left;
}
++left;
}
else {
while (left < right && nums[right - 1] == nums[right]) {
--right;
}
--right;
}
}
while ( j < numsLen - 1 && nums[j] == nums[j + 1]) {
++j;
}
}
while ( i < numsLen - 1 && nums[i] == nums[i + 1]) {
++i;
}
}
return res;
}
这里需要注意,看四个数相加的范围,很可能会超过long 的范围,需要考虑将变量类型定义为long或者把加法转换成减法来对待。
如果是五数之和,六数之和,也是一样的解法。