Leetcode演算法Java全解答--015. 三數之和
阿新 • • 發佈:2018-11-11
Leetcode演算法Java全解答–015. 三數之和
題目
給定一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?
找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
示例:
給定陣列 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
想法
- 暴力破解:
- 巢狀三層迴圈
- 注意:遍歷結果是否相同
- 時間複雜度比較高
- 先排序,以0位分割點,再進行計算
- 找到負數最大位置、正數最小位置、0的個數
- 0超過三個,則肯定有個答案是000
- 當沒有負數或者沒有正數的時候,直接返回
- 滑動列表的方法
- 以負數們為迴圈(最大值為數最大位置)
- 負數的絕對值為目標
- 雙指標 left為下一個數,right為最大長度
- 兩數相加小於目標,則right左移,否則left右移
- 通過nums[leftIndex] == nums[leftIndex + 1] 控制重複資料
- 注意這裡陣列越界,要加個left<right判斷
- 以負數們為迴圈(最大值為數最大位置)
結果
超過99%的測試案例
時間複雜度:複雜度最高的應該就是排序演算法了Arrays.sort(nums)
空間複雜度:接收結果List<List> result
總結
- 想到了排序
- 沒有想到使用滑動列表
- 在書寫程式碼的時候,很多細節沒有考慮到,比如重複
程式碼
我的答案
/* * Copyright (C), 2015-2018 * FileName: Solution015 * Author: zhao * Date: 2018/11/9 19:45 * Description: 15. 三數之和 * History: * <author> <time> <version> <desc> * 作者姓名 修改時間 版本號 描述 */ package com.lizhaoblog.mid; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * 〈一句話功能簡述〉<br> * 〈15. 三數之和〉 * * @author zhao * @date 2018/11/9 19:45 * @since 1.0.1 */ public class Solution015 { /************************************** * 題目 給定一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ? 找出所有滿足條件且不重複的三元組。 注意:答案中不可以包含重複的三元組。 例如, 給定陣列 nums = [-1, 0, 1, 2, -1, -4], 滿足要求的三元組集合為: [ [-1, 0, 1], [-1, -1, 2] ] **************************************/ /************************************** * * 想法: * 1.暴力破解: * 巢狀三層迴圈 * 注意:遍歷結果是否相同 * 時間複雜度比較高 * 2.先排序,以0位分割點,再進行計算 * 找到負數最大位置、正數最小位置、0的個數 * 0超過三個,則肯定有個答案是000 * 當沒有負數或者沒有正數的時候,直接返回 * 滑動列表的方法 * 以負數們為迴圈(最大值為數最大位置) * 負數的絕對值為目標 * 雙指標 left為下一個數,right為最大長度 * 兩數相加小於目標,則right左移,否則left右移 * 通過nums[leftIndex] == nums[leftIndex + 1] 控制重複資料 * 注意這裡陣列越界,要價格left<right判斷 * 我的做法 * 超過99%的測試案例 * 時間複雜度:複雜度最高的應該就是排序演算法了Arrays.sort(nums) * 空間複雜度:接收結果List<List<Integer>> result * 程式碼執行過程: * 找到負數最大位置、正數最小位置、0的個數 * 0超過三個,則肯定有個答案是000 * 當沒有負數或者沒有正數的時候,直接返回 * 滑動列表的方法 * 以負數們為迴圈(最大值為數最大位置) * 負數的絕對值為目標 * 雙指標 left為下一個數,right為最大長度 * 兩數相加小於目標,則right左移,否則left右移 * 通過nums[leftIndex] == nums[leftIndex + 1] 控制重複資料 * 注意這裡陣列越界,要價格left<right判斷 * 總結: * 1. 想到了排序 * 2. 沒有想到使用滑動列表 * 3. 在書寫程式碼的時候,很多細節沒有考慮到,比如重複 * ***********************************/ public List<List<Integer>> threeSum(int[] nums) { if (nums == null || nums.length < 1) { return Collections.EMPTY_LIST; } Arrays.sort(nums); int target = 0; int targetCount = 0; // 負數最大索引 int spilt0 = -1; // 正數最小索引 int spilt2 = -1; for (int i = 0; i < nums.length; i++) { if (nums[i] < target) { spilt0 = i; } else if (nums[i] == target) { targetCount++; } else if (nums[i] > target) { spilt2 = i; break; } } // 出現超過三個0,就肯定有一個答案是000 List<List<Integer>> result = new ArrayList<>(); if (targetCount >= 3) { List<Integer> list = new ArrayList<>(); list.add(0); list.add(0); list.add(0); result.add(list); } // 這種時候只有非負數,或者只有非正數,除了000,否則是湊不齊的 if (spilt0 == -1 || spilt2 == -1) { // 這裡有可能全部都是0,不然就肯定無法滿足條件 return result; } // 當整體資料有正有負,就遍歷負數就好 // 然後使用滑動列表 // 以負數的絕對值為目標,後面2數相加小於目標,則左移,否則右移 for (int i = 0; i <= spilt0; i++) { // 如果和之前那個資料相同,則會是重複事件 if (i > 0 && nums[i] == nums[i - 1]) { continue; } int val = nums[i] * -1; int leftIndex = i + 1; int rightIndex = nums.length - 1; while (leftIndex < rightIndex) { int tmpVal = nums[leftIndex] + nums[rightIndex]; if (tmpVal > val) { rightIndex--; } else if (tmpVal < val) { leftIndex++; } else { List<Integer> list = new ArrayList<>(); list.add(nums[i]); list.add(nums[leftIndex]); list.add(nums[rightIndex]); result.add(list); // 用nums[leftIndex] == nums[leftIndex + 1]控制重複 while (leftIndex < rightIndex && nums[leftIndex] == nums[leftIndex + 1]) { leftIndex++; } while (leftIndex < rightIndex && nums[rightIndex] == nums[rightIndex - 1]) { rightIndex--; } leftIndex++; rightIndex--; } } } return result; } }
大佬們的答案
/**************************************
* 比我好的答案 better
* ***********************************/
public List<List<Integer>> better(int[] nums) {
if (nums.length < 3) {
return Collections.emptyList();
}
List<List<Integer>> res = new ArrayList<>();
int minValue = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
int negSize = 0;
int posSize = 0;
int zeroSize = 0;
for (int v : nums) {
if (v < minValue) {
minValue = v;
}
if (v > maxValue) {
maxValue = v;
}
if (v > 0) {
posSize++;
} else if (v < 0) {
negSize++;
} else {
zeroSize++;
}
}
if (zeroSize >= 3) {
res.add(Arrays.asList(0, 0, 0));
}
if (negSize == 0 || posSize == 0) {
return res;
}
if (minValue * 2 + maxValue > 0) {
maxValue = -minValue * 2;
} else if (maxValue * 2 + minValue < 0) {
minValue = -maxValue * 2;
}
int[] map = new int[maxValue - minValue + 1];
int[] negs = new int[negSize];
int[] poses = new int[posSize];
negSize = 0;
posSize = 0;
for (int v : nums) {
if (v >= minValue && v <= maxValue) {
if (map[v - minValue]++ == 0) {
if (v > 0) {
poses[posSize++] = v;
} else if (v < 0) {
negs[negSize++] = v;
}
}
}
}
Arrays.sort(poses, 0, posSize);
Arrays.sort(negs, 0, negSize);
int basej = 0;
for (int i = negSize - 1; i >= 0; i--) {
int nv = negs[i];
int minp = (-nv) >>> 1;
while (basej < posSize && poses[basej] < minp) {
basej++;
}
for (int j = basej; j < posSize; j++) {
int pv = poses[j];
int cv = 0 - nv - pv;
if (cv >= nv && cv <= pv) {
if (cv == nv) {
if (map[nv - minValue] > 1) {
res.add(Arrays.asList(nv, nv, pv));
}
} else if (cv == pv) {
if (map[pv - minValue] > 1) {
res.add(Arrays.asList(nv, pv, pv));
}
} else {
if (map[cv - minValue] > 0) {
res.add(Arrays.asList(nv, cv, pv));
}
}
} else if (cv < nv) {
break;
}
}
}
return res;
}
測試用例
@Test
public void test015() {
// 建立測試案例
int[] arr1 = new int[] { -1, 0, 1, 2, -1, -4 };
// 測試案例期望值
List<List<Integer>> expResult1 = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
list1.add(-1);
list1.add(0);
list1.add(1);
List<Integer> list2 = new ArrayList<>();
list2.add(-1);
list2.add(-1);
list2.add(2);
expResult1.add(list1);
expResult1.add(list2);
// 執行方法
Solution015 solution015 = new Solution015();
List<List<Integer>> result1 = solution015.threeSum(arr1);
// 判斷期望值與實際值
Assert.assertEquals(expResult1, result1);
}
其他
程式碼託管碼雲地址:https://gitee.com/lizhaoandroid/LeetCodeAll.git
檢視其他內容可以點選專欄或者我的部落格哈:https://blog.csdn.net/cmqwan
“大佬們的答案” 標籤來自leetcode,侵權請聯絡我進行刪改
如有疑問請聯絡,聯絡方式:QQ3060507060