当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法;或者通过哈希表判断是否有目标元素存在
给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/cnkjg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的题解
暴力
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] index = {0,0};
for(int i =0;i<numbers.length;i++){
for(int j=i+1;j<numbers.length;j++){
if(numbers[i]+numbers[j] == target){
index[0] = i+1;
index[1] = j+1;
}
}
}
return index;
}
}
复杂度
2022/10/18
由于数组从小到大排列, 设置双指针分别指向数组的首部(left)和尾部(right)
若首部尾部相加等于目标值,返回结果集
若首部尾部相加小于目标值,右移left,使和变大
若首部尾部相加大于目标值,左移right,使和减小
代码
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
int len = numbers.length;
int left = 0;
int right = len-1;
while(left < right){
if (numbers[left] + numbers[right] == target){
res[0] = left;
res[1] = right;
return res;
}else if(numbers[left] + numbers[right] > target){
right--;
}else {
left++;
}
}
return res;
}
}
复杂度
2023/7/8
依次遍历数组中的元素,然后使用二分法查找数组中是否存在元素的数值为targe-numbers[i]
class Solution {
public int[] twoSum(int[] numbers, int target) {
int n = numbers.length;
for (int i = 0; i < n; i++){
int index = search(numbers, target - numbers[i], i + 1, n - 1);
if (index != -1){
return new int[]{i + 1, index + 1};
}
}
return null;
}
public int search(int[] nums, int target, int l, int r){
while (l <= r){
int mid = (l + r) >> 1;
if (nums[mid] == target){
return mid;
}else if (nums[mid] > target){
r = mid - 1;
}else{
l = mid + 1;
}
}
return -1;
}
}
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0, j = numbers.length - 1;
while (i < j) {
int m = (i + j) >>> 1;
if (numbers[i] + numbers[m] > target) {
j = m - 1;
} else if (numbers[m] + numbers[j] < target) {
i = m + 1;
} else if (numbers[i] + numbers[j] > target) {
j--;
} else if (numbers[i] + numbers[j] < target) {
i++;
} else {
return new int[]{i + 1, j + 1};
}
}
return new int[]{0, 0};
}
}
https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/solution/liang-shu-zhi-he-ii-shu-ru-you-xu-shu-zu-by-leet-2/502315
给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为
0
且不重复的三元组。**注意:**答案中不可以包含重复的三元组。
思路
nums[i]
的情况下,在数组nums[i+1,n-1]
中使双指针法(前后指针),确定另外两个元素,判断这三个元素之和sum
与target
的大小:
sum
减小sum
增大r、l
跳过相同元素,以便过滤掉重复的三元组nums[i]==nums[i-1]
, n u m s [ i ] nums[i] nums[i]的三元组是 n u m s [ i − 1 ] nums[i-1] nums[i−1]的子集,需要跳过实现
首先将数组排序;
然后有一层for循环, i i i从下表0的地方开始,同时定义下标left在i+1位置上,定义下标right在数组末尾的位置上
如果nums[i]>0
,不可能存在三元组,直接退出循环,返回结果集
去重:如果(i > 0 && nums[i] == nums[i - 1])
, n u m s [ i ] nums[i] nums[i]的三元组是 n u m s [ i − 1 ] nums[i-1] nums[i−1]的子集,需要跳过
移动left和right
如果nums[i] + nums[left] + nums[right] > 0,
说明right下标应该向左移动
去重:while (left < right && nums[right] == nums[right + 1]) right--;
【非必须】
如果nums[i] + nums[left] + nums[right] < 0,
说明left下标应该向右移动
去重: while (left < right && nums[left] == nums[left - 1]) left++;
【非必须】
如果nums[i] + nums[left] + nums[right] == 0,
说明找到了三元组,双指针同时收缩,right--;left++
去重: while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++){
if (nums[i] > 0){
return res;
}
if (i > 0 && nums[i] == nums[i-1] ){
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right){
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0){
right--;
// 非必须
while(left < right && nums[right] == nums[right+1]){
right--;
}
}else if (sum < 0){
// 非必须
left++;
while (left < right && nums[left-1] == nums[left]){
left++;
}
}else{
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
while(left < right && nums[right] == nums[right-1]){
right--;
}
while (left < right && nums[left+1] == nums[left]){
left++;
}
right--;
left++;
}
}
}
return res;
}
}
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
int len = nums.length;
int i = 0;
while (i < len - 2){
twoSum(nums,i,res);
int temp = nums[i];
while (i < len && temp == nums[i]){
i++;
}
}
return res;
}
public void twoSum(int[] numbers, int i, List<List<Integer>> res ) {
int len = numbers.length;
int left = i + 1;
int right = len - 1;
while(left < right){
if ( numbers[i] + numbers[left] + numbers[right] == 0){
res.add(Arrays.asList(numbers[i],numbers[left],numbers[right]));
int temp = numbers[left];
while (numbers[left] == temp && left < right){
left++;
}
}else if(numbers[i] + numbers[left] + numbers[right] > 0){
right--;
}else {
left++;
}
}
}
}
复杂度
给你一个长度为
n
的整数数组nums
和 一个目标值target
。请你从nums
中选出三个整数,使它们的和与target
最接近。返回这三个数的和。
假定每组输入只存在恰好一个解。
2023/1/14
思路:
nums[i]
的情况下,在数组nums[i+1,n-1]
中使双指针法(前后指针),确定另外两个元素,判断这三个元素之和sum
与target
的大小:
sum
减小sum
增大sum
更接近target
那么更新sum
。如果sum==target
,那么可以直接返回target
。本题不去重也不影响结果实现
// 2023/7/10
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length ;
int res = Integer.MAX_VALUE;
for (int i = 0; i < n - 2; i++){
int l = i + 1, r = n - 1;
if (i > 0 && nums[i] == nums[i - 1]){
continue;
}
while (l < r){
int sum = nums[i] + nums[l] + nums[r];
if (Math.abs(sum - target) < Math.abs(res - target)){
res = sum;
}
if (sum == target){
return target;
}else if(sum > target){
r--;
}else{
l++;
}
}
}
return res;
}
}
复杂度
给定一个长度为
n
的整数数组和一个目标值target
,寻找能够使条件nums[i] + nums[j] + nums[k] < target
成立的三元组i, j, k
个数(0 <= i < j < k < n
)。
2023/1/14
思路:首先对数组排序,然后固定 n u m s [ i ] nums[i] nums[i],使用双指针在 n u m s [ i + 1 , n − 1 ] nums[i+1,n-1] nums[i+1,n−1]内搜索左指针最小右指针最大的二元组,。左指针起始位置为 i + 1 i+1 i+1,右指针起始位置为 n − 1 n-1 n−1,假设三数之和为sum
sum
i
和l
时,右指针r
的可取位置为 [ l + 1 , r ] [l+1,r] [l+1,r],此时的三元组之和均小于target
,对结果的贡献为 r − l r-l r−l。然后右移左指针,寻找新的二元组本题不要求去重,因为统计的是下标的个数
实现
class Solution {
public int threeSumSmaller(int[] nums, int target) {
int count = 0, n = nums.length;
Arrays.sort(nums);
for (int i = 0; i < n - 2; i++){
int l = i + 1, r = n - 1;
while (l < r){
int sum = nums[i] + nums[l] + nums[r];
if (sum < target){
count += r - l;
l++;
}else{
r--;
}
}
}
return count;
}
}
复杂度
思路:我们从小到大枚举 i i i 和 j j j,规定 i < j i
实现
public int threeSumSmaller(int[] nums, int target) {
Arrays.sort(nums);
int sum = 0;
for (int i = 0; i < nums.length - 2; i++) {
sum += twoSumSmaller(nums, i + 1, target - nums[i]);
}
return sum;
}
private int twoSumSmaller(int[] nums, int startIndex, int target) {
int sum = 0;
for (int i = startIndex; i < nums.length - 1; i++) {
int j = binarySearch(nums, i, target - nums[i]);
sum += j - i;
}
return sum;
}
private int binarySearch(int[] nums, int startIndex, int target) {
int left = startIndex;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right + 1) / 2;
if (nums[mid] < target) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
作者:力扣 (LeetCode)
链接:https://leetcode.cn/problems/3sum-smaller/solutions/11601/jiao-xiao-de-san-shu-zhi-he-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度
给你一个由
n
个整数组成的数组nums
,和一个目标值target
。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
思路
类似三数之和,先将数组排序,然后固定两个数 n u m s [ a ] , n u m s [ b ] nums[a],nums[b] nums[a],nums[b],然后在区间 [ b + 1 , n − 1 ] [b+1,n-1] [b+1,n−1]使用双指针搜索是否有两数之和为 t a r g e t − n u m s [ a ] − n u m s [ b ] target-nums[a]-nums[b] target−nums[a]−nums[b],如果有则记录答案;否则根据大小,右移左指针或者左移右指针。
实现
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
int n = nums.length;
Arrays.sort(nums);
for (int i = 0; i < n - 3; i++){
if (i > 0 && nums[i] == nums[i - 1]) continue;
long x = nums[i];
if (x + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break;
if (x + nums[n - 1] + nums[n - 2] + nums[n - 3] < target) continue;
for (int j = i + 1; j < n - 2; j++){
if (j > i + 1 && nums[j] == nums[j - 1]) continue;
long sum2 = nums[i] + nums[j];
int l = j + 1, r = n - 1;
long num = target - sum2;
while (l < r){
if (nums[l] + nums[r] == num){
res.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
l++;
while (l < n && nums[l - 1] == nums[l]){
l++;
}
r--;
while (r > l && nums[r + 1] == nums[r]){
r--;
}
}else if (nums[l] + nums[r] > num){
r--;
}else{
l++;
}
}
}
}
return res;
}
}
复杂度
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
注意:先在map中寻找是否存在target-num[i]
,若存在则返回下标,若不存在再将nums[i],i
放入map中,避免返回相同坐标或者坐标覆盖的情况
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
if(nums == null || nums.length == 0){
return res;
}
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
int temp = target - nums[i];
if(map.containsKey(temp)){
res[1] = i;
res[0] = map.get(temp);
}
map.put(nums[i], i);
}
return res;
}
}
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
2022/4/20
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int len = nums1.length;
Map<Integer,Integer> sum1 = new HashMap<>();
Map<Integer,Integer> sum2 = new HashMap<>();
for (int i = 0; i < len; i++){
for (int j = 0; j < len; j++){
sum1.put(nums1[i] + nums2[j], sum1.getOrDefault(nums1[i] + nums2[j],0) + 1);
sum2.put(nums3[i] + nums4[j], sum2.getOrDefault(nums3[i] + nums4[j],0) + 1);
}
}
Set<Map.Entry<Integer,Integer>> set1 = sum1.entrySet();
int res = 0;
for (Map.Entry<Integer,Integer> node:set1 ){
int target = -1 * node.getKey().intValue();
if(sum2.containsKey(target)){
res += node.getValue().intValue() * sum2.get(target);
}
}
return res;
}
}
改进
只用一个map记录nums1[i]+nums2[j],多使用一个双重for循环在map中寻找nums3[k]和nums4[l]
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int len = nums1.length;
Map<Integer,Integer> sum1 = new HashMap<>();
for (int i = 0; i < len; i++){
for (int j = 0; j < len; j++){
sum1.put(nums1[i] + nums2[j], sum1.getOrDefault(nums1[i] + nums2[j],0) + 1);
}
}
int res = 0;
for (int i = 0; i < len; i++){
for (int j = 0; j < len; j++){
int target = -nums3[i] - nums4[j];
if(sum1.containsKey(target)){
res += sum1.get(target);
}
}
}
return res;
}
}