18 - 四数之和

前情提要:三数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

例1:

1
2
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

例2:

1
2
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

思路:

思路和三数之和 一样,排序后,枚举nums[a]作为第一个数,枚举nums[b]作为第二个数,那么问题变成找到另外两个数,使得这四个数的和等于target,这可以用双指针解决。

对于nums[a]的枚举:

  1. 设 s=nums[a]+nums[a+1]+nums[a+2]+nums[a+3]。如果s>target,由于数组已经排序,后面无论怎么选,选出的四个数的和不会比s还小,所以后面不会找到等于 target的四数之和了。所以只要 s>target,就可以直接 break 外层循环了。

  2. 设 s=nums[a]+nums[n−3]+nums[n−2]+nums[n−1]。如果s<target,由于数组已经排序,nums[a]加上后面任意三个数都不会超过s,所以无法在后面找到另外三个数与 nums[a]相加等于 target。但是后面还有更大的 nums[a],可能出现四数之和等于 target的情况,所以还需要继续枚举,continue外层循环。

  3. 如果 a>0且nums[a]=nums[a−1],那么 nums[a]和后面数字相加的结果,必然在之前算出过,所以无需执行后续代码,直接 continue 外层循环。(可以放在循环开头判断。)

对于 nums[b]的枚举(b从a+1开始),也同样有类似优化:

  1. 设 s=nums[a]+nums[b]+nums[b+1]+nums[b+2]。如果s>targett,由于数组已经排序,后面无论怎么选,选出的四个数的和不会比s还小,所以后面不会找到等于 target的四数之和了。所以只要 s>target,就可以直接 break。

  2. 设 s=nums[a]+nums[b]+nums[n−2]+nums[n−1]。如果 s<target,由于数组已经排序,nums[a]+nums[b]加上后面任意两个数都不会超过 sss,所以无法在后面找到另外两个数与 nums[a]和 nums[b]相加等于 target。但是后面还有更大的 nums[b],可能出现四数之和等于 target的情况,所以还需要继续枚举,continue。

  3. 如果 b>a+1且 nums[b]=nums[b−1],那么 nums[b]和后面数字相加的结果,必然在之前算出过,所以无需执行后续代码,直接 continue。注意这里 b>a+1的判断是必须的,如果不判断,对于示例 2 这样的数据,会直接 continue,漏掉符合要求的答案。

solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
int len = nums.length;
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < len - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if ((long)nums[i] + (long)nums[i + 1] + (long)nums[i + 2] + (long)nums[i + 3] > target) {
break;
}
if ((long)nums[i] + (long)nums[len - 1] + (long)nums[len - 2] + (long)nums[len - 3] < target) {
continue;
}
for (int j = i + 1; j < len - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long)nums[i] + (long)nums[j] + (long)nums[j + 1] + (long)nums[j + 2] > target) {
break;
}
if ((long)nums[i] + (long)nums[j] + (long)nums[len - 1] + (long)nums[len - 2] < target) {
continue;
}
int first = j + 1;
int end = len - 1;
while (first < end) {
long sum = nums[i] + nums[j] + nums[first] + nums[end];
if (sum > target) {
end--;
} else if (sum < target) {
first++;
} else {
result.add(List.of((int)nums[i], (int)nums[j], (int)nums[first], (int)nums[end]));
for (first++; first < end && nums[first] == nums[first - 1]; first++) ; // 跳过重复数字
for (end--; end > first && nums[end] == nums[end + 1]; end--) ; // 跳过重复数字
}
}
}
}
return result;
}
}

19 - 删除链表的倒数第N个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

例1:

1
2
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

例2:

1
2
输入:head = [1], n = 1
输出:[]

solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {

public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode pre = new ListNode(0);
pre.next = head;
ListNode start = pre;
ListNode end = pre;
while (n != 0) {
start = start.next;
n--;
}
while(start.next!=null){
start = start.next;
end = end.next;
}
end.next = end.next.next;
return pre.next;
}
}