和为 s 的两个数字(对撞双指针)
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
输入:nums = [2,7,11,15], target = 9 输出:[2,7] 或者 [7,2]
题解
-
对撞双指针(在有序序列的情况下)
-
算法思想
-
在排序数组下,利用对撞双指针
-
正确性证明
-
记每个状态为 S(i, j)S(i,j) ,即 S(i, j) = nums[i] + nums[j]S(i,j)=nums[i]+nums[j] 。假设 S(i, j) < targetS(i,j)<target ,则执行 i = i + 1i=i+1 ,即状态切换至 S(i + 1, j)S(i+1,j) 。
- 消去行或者列都不会导致解的丢失(因为我们是从右下角开始的,如果右下角「0,5」都太大的话,那么「1,5」的和会更大,如「0,4」太小的话「0,1」更小,因此可以消去)
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ony3acsP-1597135088730)(/Users/apple/Library/Application Support/typora-user-images/image-20200811025653398.png)]
-
-
-
复杂度分析
- 时间复杂度 O(n)
- 空间复杂度 O(1)
class Solution { public int[] twoSum(int[] nums, int target) { int i = 0,j = nums.length -1; while(i < j){ int sum = nums[i] + nums[j]; if(sum > target)j--; else if(sum < target)i++; else return new int[]{nums[i],nums[j]}; } return new int[0]; } }
-
II 和为 s 的连续正数序列
输入一个正整数
target
,输出所有和为target
的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
输入:target = 9 输出:[[2,3,4],[4,5]]
题解
-
对撞双指针(滑动窗口)
- 算法思想
- 数组是递增序列,使用两个指针(前指针A、后指针B),指针的区间就是目标序列,目标序列的和即为 sum,目标值为 target
- 当 sum 大于 target 的时候,A 指针向后移动
- 当 sum 小于 target 的时候,B 指针向后移动
- 当 sum 等于 target 的时候,记录下此时的序列,然后 A 指针向后移动
- A 指针移动到target/2的位置就行了
- 正确性证明
- 当 sum 大于 target 时,证明 sum 不再需要 A 此刻指向「以及 A 之前」的 元素,因为序列是连续的「加上此刻的 A 都已经大于 target 了,加上前面的元素一定会大于 target」
- 数组是递增序列,使用两个指针(前指针A、后指针B),指针的区间就是目标序列,目标序列的和即为 sum,目标值为 target
- 复杂度分析
- 时间复杂度 O(n)
- 空间复杂度 O(1)
class Solution { public int[][] findContinuousSequence(int target) { ArrayList<int[]> arraylist = new ArrayList<>(); List<Integer> list = new ArrayList<>(); int i = 1,j = 1,sum = 0; while(i <= target/2){ if(sum < target){ sum += j; j++; }else if(sum > target){ sum -= i; i++; }else{ int[] res = new int[j-i]; for(int k = i;k < j;k++)res[k-i]=k; arraylist.add(res); sum -= i; i++; } } return arraylist.toArray(new int[arraylist.size()][]); } }
- 算法思想
-
等差数列求和解方程
-
算法思想
- 下面的**-1/2是改为1/2**,作者写错了
-
复杂度分析
- 时间复杂度 O(n)
- 空间复杂度 O(1)
-
-
数学方法
-
算法思想
-
对于目标target,若其为连续m个整数之和,则存在起始数x,有:
1 1 1 1 1 1 减一个1,看能不能分成两份 再减两个1,看能不能分成三份 再减三个1,看能不能分成四份 。。。
-
-
复杂度分析
- 时间复杂度 O(n) 不太确定
- 空间复杂度 O(1)
class Solution { public int[][] findContinuousSequence(int target) { List<int[]> list = new ArrayList<>(); int i = 1; while(target > 0){ target -= i++; if(target > 0 && target % i == 0){ int[] arr = new int[i]; for(int k = target/i,j = 0;j < i;k++,j++){ arr[j] = k; } list.add(arr); } } Collections.reverse(list); return list.toArray(new int[list.size()][]); } }
-
总结
- 连续数组的时候可以考虑使用双指针
- 连续递增,且增速固定的可以考虑数学方式