查找问题
704-二分查找
// 盲写通过
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
return -1;
}
215-数组中的第 K 个最大元素
代码:
public int findKthLargest(int[] nums, int k) {
int len = nums.length;
int idx = len - k;
return quicksort(nums, 0, len - 1, idx);
}
public int quicksort(int[] nums, int start, int end, int idx) {
if (start > end) {
return -1;
}
int pivotIdx = partition(nums, start, end, idx);
if (pivotIdx == idx) {
return nums[pivotIdx];
} else if (pivotIdx < idx) { // 基准pivotIdx小于idx时,说明预期结果在idx右侧,则对右侧进行递归遍历
return quicksort(nums, pivotIdx + 1, end, idx);
} else {
return quicksort(nums, start, pivotIdx - 1, idx);
}
}
public int partition(int[] nums, int start, int end, int idx) {
int pviot = nums[start];
int left = start, right = end;
// 左右开始扫描,将基准值pviot移动到合适的位置
while (left != right) {
while (left < right && nums[right] >= pviot) {
right--;
}
while (left < right && nums[left] <= pviot) {
left++;
}
// 交换 left 和 right 的值
if (left < right) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
}
//一次排序结束后,left和right抵达重合点,将基准值移动到这里
int p = nums[start];
nums[start] = nums[left];
nums[left] = p;
return left;
}
33-搜索旋转排序数组
代码:
public int search1(int[] nums, int target) {
Map<Integer, Integer> idxMap = new HashMap<>();
for (int i = 0; i< nums.length; i++) {
idxMap.put(nums[i], i);
}
if (idxMap.containsKey(target)) {
return idxMap.get(target);
}
return -1;
}
public int search2(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
return i;
}
}
return -1;
}
public int search(int[] nums, int target) {
if (nums.length == 0) {
return -1;
}
if (nums.length == 1) {
return nums[0] == target ? 0 : -1;
}
int left = 0, right = nums.length -1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
}
// 总有一半数据是有序的
if (nums[left] <= nums[mid]) {
//左边数据有序则查找左边数据
if (target >= nums[left] && target < nums[mid]){
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// 右边数据有序则查找右边数据
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid -1;
}
}
}
return -1;
}
81-搜索旋转排序数组II
代码:
public boolean search(int[] nums, int target) {
if (nums.length == 0) {
return false;
}
if (nums.length == 1) {
return nums[0] == target ? true : false;
}
int left = 0, right = nums.length -1;
int mid = 0;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] == target) {
return true;
}
// 如果数组中元素有重复的,那旋转后可能无法判断哪一半有序
if (nums[left] == nums[mid] ){
left++;
} else if (nums[mid] == nums[right]) {
right--;
} else{
if (nums[left] <= nums[mid]) {
//左边数据有序则查找左边数据
if (target >= nums[left] && target < nums[mid]){
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// 右边数据有序则查找右边数据
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid -1;
}
}
}
}
return false;
}
34-排序数组查元素第一个/最后一个idx(二分)
代码:
public int[] searchRange(int[] nums, int target) {
if (nums.length == 0) {
return new int[]{-1, -1};
}
int firstPosition = findFirstPosition(nums, target);
if (firstPosition == -1 ) {
return new int[]{-1, -1};
}
int lastPosition = findLastPosition(nums, target);
return new int[]{firstPosition, lastPosition};
}
/**
* 找到第一次出现的位置
* @param nums
* @param target
* @return
*/
public int findFirstPosition(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] < target) {
left = mid + 1;
} if (nums[mid] == target) {
right = mid;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}
/**
* 向右查找最后一次出现的位置
* @param nums
* @param target
* @return
*/
public int findLastPosition(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left < right) {
// 比如:left=4,right=5,此时 mid = 4,nums[mid]=target,left=mid 操作一直是 left=4,right=5,进入死循环
int mid = (left + right + 1) / 2;
if (nums[mid] < target) {
left = mid + 1;
} if (nums[mid] == target) {
left = mid;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return left;
}
35-搜索插入位置
题意分析:
- 查找和插入两个条件,根据二分查找判断数组中是否存在target元素,存在直接返回,不存在则返回元素插入位置(保证数组整体有序)
解题思路:
- 二分查找,判断条件记录什么时候更新索引位置。
注意点:
- 插入时需要记录元素要插入的位置。
扩展:
代码:
public int searchInsert(int[] nums, int target) {
return binarySearch(nums, target);
}
public int binarySearch(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1;
int ans = n;
while (left <= right) {
int mid = left + (right - left) / 2;
System.out.println("mid = " + mid);
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
ans = mid;
right = mid - 1;
} else if (nums[mid] < target){
left = mid + 1;
}
}
return ans;
}
去重问题
26-删除有序数组中的重复项
代码:
public int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return nums.length;
}
int slow = 1, fast = 1;
while (fast < nums.length){
if (nums[fast] == nums[slow-1]) {
fast++;
} else {
nums[slow++] = nums[fast++];
}
}
return slow;
}
80-删除有序数组中的重复项II
代码:
public int removeDuplicates(int[] nums) {
if (nums.length == 0 || nums.length == 1) {
return nums.length;
}
int slow = 2, fast = 2;
while (fast < nums.length){
if (nums[fast] == nums[slow-2]) {
fast++;
} else {
nums[slow++] = nums[fast++];
}
}
return slow;
}
710-黑名单中的随机数
题意分析:
- [0, n-1] 中去掉一些数,访问剩下的数。
解题思路:
- 直接用集合去重。(存储 [0, n-1] 个元素,内存超出限制)
- 黑名单元素重定向。构建 [whitesize, n-1] 长度的数组(和黑名单数组元素相同),然后将黑名单的元素直接映射到这个数组。
注意点:
- 黑名单中大于 whitesize 索引的元素直接过滤,比较随机数索引不到,只需要对黑名单中小于 whitesize 的元素映射即可。
- 求解返回时,如果是黑名单中数据重定向,白名单数据直接返回,会用到 Map 的 getOrDefault() 方法。
代码:
public class RandomBlackList_710 {
Set<Integer> set = new HashSet<>();
Map<Integer, Integer> map = new HashMap<>();
int whitesize;
/**
* 解题思路:
* 1、构建白名单set,初始元素和黑名单一致。
* 2、将黑名单中大于 whitesize 的元素从 set 移除,将小于 whitesize 的元素映射到 set 的剩余元素
* @param n
* @param blacklist
*/
public RandomBlackList_710(int n, int[] blacklist) {
whitesize = n - blacklist.length;
for (int i = whitesize; i < n; i++) set.add(i);
for (int val : blacklist) set.remove(val);
Iterator<Integer> iter = set.iterator();
// 将黑名单数据请求到白名单其他数据
for (int val : blacklist) {
if (val < whitesize) {
map.put(val, iter.next());
}
}
}
public int pick() {
int rand = new Random().nextInt(whitesize);
return map.getOrDefault(rand, rand);
}
public static void main(String[] args) {
int n = 10;
int[] blacklist = {1,2,4,8};
RandomBlackList_710 obj = new RandomBlackList_710(n, blacklist);
System.out.println("size = " + obj.set.toString());
System.out.println("map key = " + obj.map.keySet().toString() + "; map val = " + obj.map.values().toString());
for (int i = 0; i < 10; i++)
System.out.println(obj.pick());
}
}
岛屿问题
200-岛屿数量
题意分析:
- 当前节点可达的节点集合作为一个岛屿,问题可以归结为图的遍历。
解题思路:
- dfs 遍历。一次 dfs 遍历所有节点解决不了问题,需要对每个可行节点执行 dfs 逻辑,每一次找到一个岛屿。
注意点:
扩展:
代码:
public class IslandsQuestion_200 {
int[][] direction = {{-1,0},{1,0},{0,-1},{0,1}};
char[][] visited;
char[][] grid;
int m,n;
int num;
public int numIslands(char[][] grid) {
this.grid = grid;
m = grid.length;
n = grid[0].length;
visited = new char[m][n];
for (int i = 0; i < m; i++) {
Arrays.fill(visited[i], '0');
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1' && visited[i][j] == '0') {
num++;
dfs(i, j);
}
}
}
return num;
}
/**
* dfs 遍历:将 '1' 周围关联的 '1' 全部设置为 visited
* @param x
* @param y
*/
private void dfs(int x, int y) {
if (x < 0 || y < 0 || x >= m || y >= n) {
return;
}
// '0' 不访问, visited-1 表示已经访问
if ( grid[x][y] == '0' || visited[x][y] == '1') {
return;
}
// 每一轮 dfs 遍历,将 '1' 周围节点的 '1' 都设置为 visited
visited[x][y] = '1';
// 将 dfs 可达节点都置为 1,表示访问过
for (int i = 0; i < direction.length; i++) {
int tx = x + direction[i][0];
int ty = y + direction[i][1];
dfs(tx, ty);
}
}
public static void main(String[] args) {
IslandsQuestion_200 obj = new IslandsQuestion_200();
char[][] grid = {
{'1','1','0','0','0'},
{'1','1','0','0','0'},
{'0','0','1','0','0'},
{'0','0','0','1','1'}
};
int res = obj.numIslands(grid);
System.out.println("res = " + res);
}
}
1254-统计封闭岛屿的数量
题意分析:
- 和题 200 题目类似,无非就是要过滤掉四个边边上的节点。
解题思路:
- 判断是先判断边边的节点,过滤掉。
注意点:
扩展:
代码:
public class IslandsClosed_1254 {
int[][] grid;
int[][] direction = {{0,-1},{0,1},{-1,0},{1,0}};
int m,n;
int res;
public int closedIsland(int[][] grid) {
this.grid = grid;
m = grid.length;
n = grid[0].length;
// 对四周节点过滤
for (int i = 0; i < m; i++) {
dfs(i,0);
dfs(i, n - 1);
}
for (int i = 0; i < n; i++) {
dfs(0, i);
dfs(m - 1, i);
}
// 对所以 0(陆地)节点遍历,判断是否能形成一个岛屿
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
res++;
dfs(i,j);
}
}
}
return res;
}
public void dfs(int x, int y) {
if (x < 0 || y < 0 || x >= m || y >= n) {
return;
}
if (grid[x][y] == 1) {
return;
}
// 将访问过的 0(陆地) 设置为 1(海水)
grid[x][y] = 1;
for (int i = 0; i < direction.length; i++) {
int tx = x + direction[i][0];
int ty = y + direction[i][1];
dfs(tx, ty);
}
}
}
未分类
581-最短无序连续子数组
代码:
/**
* 排序 + 遍历(时间复杂度:O(N * logN)
*
* 复制一个数组并排序,遍历对比left和right区间
* @param nums
* @return
*/
public int findUnsortedSubarray2(int[] nums) {
int len = nums.length;
int[] sorted = Arrays.copyOf(nums, nums.length);
Arrays.sort(sorted);
System.out.println("nums = " + Arrays.toString(nums));
System.out.println("sorted = " + Arrays.toString(sorted));
int left = 0, right = len - 1;
int l = -1, r = -1;
for (; left < len; left++) {
if (nums[left] != sorted[left]) {
l = left;
break;
}
}
for (; right >= 0; right--) {
if (nums[right] != sorted[right]) {
r = right;
break;
}
}
System.out.println("l = " + l + "; r = " + r);
return l != r ? r - l + 1 : 0;
}
/**
* 双指针
* @param nums
* @return
*/
public int findUnsortedSubarray(int[] nums) {
int len = nums.length;
int left = 0, right = 0;
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
// 双指针左右扫描,找出 left 和 right
for (int i = 0; i < len; i++) {
// 找 right。从左到右遍历,如果某个元素小于前面所有元素的最大值,则无序,更新 right
if (nums[i] >= max) {
max = nums[i];
} else {
right = i;
}
// 找left。从右往左遍历,如果某个元素大于前面所有元素的最小值,则无序,更新left
if (nums[len - i -1] <= min) {
min = nums[len - i - 1];
} else {
left = len - i - 1;
}
}
System.out.println("left = " + left + "; right = " + right);
return left == right ? 0 : right - left + 1;
}
896-单调数列
代码:
public boolean isMonotonic(int[] nums) {
int len = nums.length;
boolean isInc = true, isDec = true;
// 如果不是单调递增或者单调递减,isInc 和 isDec 都为 false
for (int i = 0; i < len - 1; i++) {
// 一对递增数据,isDec = false;
if (nums[i] < nums[i+1]) {
isDec = false;
}
if (nums[i] > nums[i+1]) {
isInc = false;
}
}
return isInc || isDec;
}
1337-矩阵中战斗力最弱的 K 行
代码:
public int[] kWeakestRows(int[][] mat, int k) {
int m = mat.length, n = mat[0].length;
for(int i = 0; i < m; i++) {
for (int j =0; j< n; j++) {
System.out.print(mat[i][j] + " ");
}
System.out.println();
}
// 记录行号和1的个数
int[][] cntArr = new int[m][2];
for (int i = 0; i < m; i++) {
// 对每一行执行二分查找,找出 1 的个数
int[] arr = mat[i];
int pos = binarySearch(arr);
cntArr[i][0] = i;
cntArr[i][1] = pos + 1;
}
for (int i = 0; i < cntArr.length; i++) {
System.out.println("行:1数量 == " + cntArr[i][0] + ":" + cntArr[i][1]);
}
// 自定义比较器排序,先按1的个数升序排序,再按行号升序排序
Arrays.sort(cntArr, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[1] != o2[1]) {
return (o1[1] - o2[1]);
} else {
return (o1[0] - o2[0]);
}
}
});
System.out.println("-------排序后——--------");
for (int i = 0; i < cntArr.length; i++) {
System.out.println("行:1数量 == " + cntArr[i][0] + ":" + cntArr[i][1]);
}
// 输出前 k 行行号
int[] ret = new int[k];
for(int i = 0; i < k; i++) {
ret[i] = cntArr[i][0];
System.out.println(ret[i]);
}
return ret;
}
/**
* 二分查找每一行 1 的数量
* @param arr
* @return
*/
public int binarySearch(int[] arr) {
int left = 0, right = arr.length - 1;
int pos = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == 0) {
right = mid - 1;
} else {
pos = mid;
left = mid + 1;
}
}
return pos;
}
88-合并两个有序数组
代码:
/**
* 申请长度为 m+n 的额外数组
* @param nums1
* @param m
* @param nums2
* @param n
*/
public void merge2(int[] nums1, int m, int[] nums2, int n) {
int[] tmp = new int[m+n];
int k = 0;
int i = 0, j = 0;
while (i < m && j < n) {
tmp[k++] = nums1[i] < nums2[j] ? nums1[i++] : nums2[j++];
}
while(i < m) {
tmp[k++] = nums1[i++];
}
while (j < n) {
tmp[k++] = nums2[j++];
}
for(int p = 0 ;p < tmp.length; p++) nums1[p] = tmp[p];
System.out.println("tmp = " + Arrays.toString(nums1));
}
/**
* 原地复制,从尾部空位置开始填充
* @param nums1
* @param m
* @param nums2
* @param n
*/
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (i >= 0 && j >= 0) {
nums1[k--] = nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];
}
System.arraycopy(nums2, 0 , nums1, 0, j + 1);
// System.out.println("nums = " + Arrays.toString(nums1));
}
611-有效三角形的个数
代码:
/**
* 排序 + 二分查找 (时间复杂度 O(N^2 * logN)
*
* 注意:System.out 会增加统计时间,造成时间超时
*
* @param nums
* @return
*/
public int triangleNumber(int[] nums) {
int len = nums.length;
if (len <= 2) {
return 0;
}
int ans = 0;
Arrays.sort(nums);
for (int i = 0; i < len-1; i++) {
for (int j = i+1; j < len; j++) {
int left = j + 1, right = len - 1;
int k = j;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[i] + nums[j] > nums[mid]) {
// System.out.println(nums[i] + "-" + nums[j] + "-" + nums[mid]);
k = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
ans += k - j;
}
}
return ans;
}
912-排序数组
代码:
public int[] sortArray(int[] nums) {
quicksort(nums, 0, nums.length - 1);
return nums;
}
public void quicksort(int[] nums, int startIdx, int endIdx) {
if (startIdx < endIdx) {
int pivot = partition(nums, startIdx, endIdx);
quicksort(nums, startIdx, pivot);
quicksort(nums, pivot + 1, endIdx);
}
}
/**
* 一次快排操作
* @param nums
* @param startIdx
* @param endIdx
* @return
*/
public int partition(int[] nums, int startIdx, int endIdx) {
int pivot = startIdx;
int left = startIdx, right = endIdx;
while (left != right) {
while (left < right && nums[right] >= nums[pivot]) right--;
while (left < right && nums[left] <= nums[pivot]) left++;
// 交换left 和 right 的值
if (left < right) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
}
// left/right 重合,交换 pivot 和 left 的值,一次快排完成
int tmp = nums[pivot];
nums[pivot] = nums[left];
nums[left] = tmp;
return left;
}
189-旋转数组
代码:
public void rotate(int[] nums, int k) {
k = k % nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
printArray(nums);
}
// 倒序数组
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int tmp = nums[start];
nums[start] = nums[end];
nums[end] = tmp;
start++;
end--;
}
}
915-分隔数组
题意分析:
- 分隔数组,使得left数组数据都小于right数组数据,且保证left数组长度最小。
解题思路:
- 多次遍历数组。判断条件 max(left) < min(right),满足这个条件的最小 idx 值。
- 时间复杂度:O(n)
- 空间复杂度:O(n)
注意点:
- 最后条件判断的时候要考虑 <= 相等的情况。
扩展:
- 能否减少数组遍历次数。
代码:
/**
* 解题思路:
* 判断条件:max(left) < min(right),满足这个条件的最小 idx 值。
* 1、统计 left 数组每个分隔点的最大值
* 2、统计 right 数组每个分隔点的最小值
* @param nums
* @return
*/
public int partitionDisjoint(int[] nums) {
int n = nums.length;
int[] maxleft = new int[n];
int[] minright = new int[n];
// left 数组每个位置的最大值
int max = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
max = Math.max(max, nums[i]);
maxleft[i] = max;
}
System.out.println("maxleft = " + Arrays.toString(maxleft));
// right 数组每个位置的最小值
int min = Integer.MAX_VALUE;
for (int j = n-1; j >= 0; j--) {
min = Math.min(min, nums[j]);
minright[j] = min;
}
System.out.println("minright = " + Arrays.toString(minright));
// 比较切分后 left 和 right 数组的值
for (int i = 1; i < n; i++) {
if (maxleft[i-1] <= minright[i]) {
return i;
}
}
return n;
}
27-移除元素
题意分析:
- 原地修改数组,删除数组中值为 val 的元素。
解题思路:
- 左右指针。左指针维护更新后数组元素,右指针扫描数组。
注意点:
- 右指针何时扫描。
扩展:
代码:
/**
* 解题思路:双指针
* @param nums
* @param val
* @return
*/
public int removeElement(int[] nums, int val) {
System.out.println(Arrays.toString(nums));
int left = 0, right = 0;
while (right != nums.length) {
if (nums[right] != val) {
nums[left++] = nums[right];
}
right++;
}
// System.out.println(Arrays.toString(nums));
return left;
}
1909-删除一个元素使数组严格递增
题意分析:
- 删除一个元素,数组严格递增。
解题思路:
- 贪心算法。当遇到当前小于前一个数时,则比较当前数和前两个数大小,如果当前数小于前两个数,则删除当前数,如果当前数小于前两个数中的一个(即小于前一个数),则删除前一个数。
注意点:
扩展:
代码:
- https://leetcode-cn.com/problems/remove-one-element-to-make-the-array-strictly-increasing/solution/tan-xin-by-hu-li-hu-wai-1zni/
public boolean canBeIncreasing(int[] nums) {
int cnt = 0;
int n = nums.length;
for (int i = 1; i < n && cnt <= 1; i++) {
if (nums[i] <= nums[i - 1]) {
cnt++;
// 如果当前数小于前面两个数,则删除当前数
if (i - 2 >= 0 && nums[i] <= nums[i - 2]) {
nums[i] = nums[i-1];
} else {
// 如果当前数只小于前两个数中的第二个数,则删除前一个数
nums[i - 1] = nums[i];
}
}
}
return cnt <= 1;
}
1-两数之和
题意分析:
- 数组中两个元素之和等于 target。
解题思路:
- 思路一:HashMap 保存 target-nums[i] 元素。
- 思路二:排序+双指针。
注意点:
扩展:
- 返回所有和为 target 的元素对,且不存在重复项。
代码:
/**
* 思路一:HashMap 保存遍历过的元素和索引
* @param nums
* @param target
* @return
*/
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
res[0] = map.get(nums[i]);
res[1] = i;
return res;
} else {
map.put(target - nums[i], i);
}
}
return res;
}
/**
* 思路二:双指针(求所有两数之和为 sum 的元素对,且不重复)
* @param nums
* @param target
* @return
*/
public List<List<Integer>> twoSum2(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
// 对数组升序排序
Arrays.sort(nums);
int low = 0, high = nums.length - 1;
while (low < high) {
int low_val = nums[low];
int high_val = nums[high];
int sum = low_val + high_val;
if (sum < target) {
low++;
} else if (sum > target) {
high--;
} else if (sum == target) {
List<Integer> list = new ArrayList();
list.add(nums[low++]); list.add(nums[high--]);
// 如果 low 往右走出现重复元素,则继续右走,high 逻辑也一样
while (nums[low] == low_val) low++;
while (nums[high] == high_val) high--;
res.add(list);
}
}
return res;
}
15-三数之和
题意分析:
- 数组中三个元素和为 target。
解题思路:
- 排序+双指针。排序数组,遍历数组中每个元素,调用 2Sum 求解,遇到重复遍历元素跳过。
注意点:
扩展:
代码:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length <= 2) {
return res;
}
Arrays.sort(nums);
// System.out.println(Arrays.toString(nums));
for (int i = 0; i < nums.length - 2; i++) {
List<List<Integer>> twoRes = twoSum(nums,i + 1, 0 - nums[i]);
// System.out.println("nums[i] = " + nums[i] + "; twoRes = " + twoRes);
for (int j = 0; j < twoRes.size(); j++) {
twoRes.get(j).add(nums[i]);
res.add(twoRes.get(j));
}
while (i < nums.length -1 && nums[i+1] == nums[i]) i++;
}
return res;
}
/**
* 两数只和
* @param nums
* @param start
* @param target
* @return
*/
private List<List<Integer>> twoSum(int[] nums, int start, int target) {
List<List<Integer>> twoRes = new ArrayList<>();
int low = start, high = nums.length - 1;
while (low < high) {
int low_val = nums[low], high_val = nums[high];
int sum = low_val + high_val;
if (sum < target) {
// 优化
while (low < high && nums[low] == low_val) low++;
} else if (sum > target) {
while (low < high && nums[high] == high_val)high--;
} else if (sum == target) {
List<Integer> list = new ArrayList<>();
list.add(low_val); list.add(high_val);
while (low < high && nums[low] == low_val) low++;
while (low < high && nums[high] == high_val) high--;
twoRes.add(list);
}
}
return twoRes;
}
18-四数之和(nSum问题)
题意分析:
- 数组中四个数或者 n 个数和为 target。
解题思路:
- 思路一:调用 3Sum 求解,没意义。
- 思路二:通用 nSum 求解,递归调用 (n-1)Sum 求解。
注意点:
扩展:
代码:
public List<List<Integer>> fourSum(int[] nums, int target) {
int n = 4;
Arrays.sort(nums);
return nSumTarget(nums, n, 0, target);
}
/**
*
* @param nums
* @param n 表示几个数之和,比如 n=2,表示两个数之和
* @param start 数组的开始元素
* @param target
* @return
*/
public List<List<Integer>> nSumTarget(int[] nums, int n, int start, int target) {
int size = nums.length;
List<List<Integer>> res = new ArrayList<>();
// 至少 2Sum,且数组大小不应该小于 n
if (n < 2 || size < n) return res;
int low = start, high = nums.length - 1;
if (n == 2) {
// 双指针
while (low < high) {
int low_val = nums[low], high_val = nums[high];
int sum = low_val + high_val;
if (sum < target) {
// 优化
while (low < high && nums[low] == low_val) low++;
} else if (sum > target) {
// 优化
while (low < high && nums[high] == high_val)high--;
} else if (sum == target) {
List<Integer> list = new ArrayList<>();
list.add(low_val); list.add(high_val);
while (low < high && nums[low] == low_val) low++;
while (low < high && nums[high] == high_val) high--;
res.add(list);
}
}
} else {
// n > 2 时,递归计算 (n-1)Sum 结果
for (int i = start; i < size; i++) {
List<List<Integer>> nRes = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (List<Integer> list : nRes) {
list.add(nums[i]);
res.add(list);
}
while (i < size - 1 && nums[i] == nums[i+1]) i++;
}
}
return res;
}
模版
题意分析:
解题思路:
注意点:
扩展:
代码: