LeetCode每日一练【15】
LeetCode每日一练
Tree Sum
我的解法
第一次提交
思路:
- 升序排序输入的数组
- 找到最小非负数, 然后设置两个指针:
left
(负数) 和right
(非负数) - 因为
arr[i] + arr[j] + arr[k] = 0
必须的形式一定是[负数, 负数, 非负数(>0)]
,[负数, 非负数(>=0), 非负数(>0)]
和[0, 0, 0]
这三种形式, 因此只需要依次判断原始数组在这三种情况下的输出即可 - 考虑到二维数组中的数组重复问题, 还需要对二维数组进行去重处理
- 返回去重后的结果
- 虽然可以正常返回结果,但是在面对大数据的时候. 遗憾的是,我的解法显示超时
/*
* @Author: fox
* @Date: 2022-05-01 00:34:12
* @LastEditors: fox
* @LastEditTime: 2022-05-01 20:09:35
* @Description: https://leetcode.com/problems/3sum/
*/
/**
* @description: 不解释,超过时间限制 Time Limit Exceeded
* @param {number[]} nums
* @return {number[][]}
*/
const threeSum = (arr) => {
const res = [] // 临时存放返回结果
const obj = new Object()
const ret = [] // 存放返回结果
const myArr = arr.sort((a, b) => a - b) // 将原始数组升序排序
let middle; // 最小非负数的索引值
let differ; // 差值
if (arr.length < 3) return [] // 如果原始数组的长度不超过3,直接返回空数组
// 1. 找到最小非负数的索引值
for (let i in myArr) {
if (myArr[i] >= 0) {
middle = i
break
}
}
// 2. 遍历数组,取得正整数和非负整数的差值,存储到集合中 [-, -, +] [-, +, +]
for (let left = middle - 1; left >= 0; left--) {
for (let right = middle; right < arr.length; right++) {
differ = -(myArr[left] + myArr[right])
const index = differ > 0 ? myArr.lastIndexOf(differ) : myArr.indexOf(differ);
if (index !== -1) {
if (index < left) {
res.push([myArr[index], myArr[left], myArr[right]])
} if (index > right) {
res.push([myArr[left], myArr[right], myArr[index]])
}
}
}
}
// 3. 如果存在[0, 0, 0]的情况
if(myArr.filter(item => item === 0).length >= 3) {
res.push([0, 0, 0])
}
// 4. 二维数组去重
for (let index in res) {
if (!obj[res[index]]) {
obj[res[index]] = true
ret.push(res[index])
}
}
return ret
}
// let arr = [-1, 0, 1, 2, -1, -4]
// console.log(threeSum(arr)) // [[-1,-1,2],[-1,0,1]]
// arr = [1, 1, -2]
// console.log(threeSum(arr)) // [1, 1, -2]
arr = [-1,0,1,2,-1,-4,-2,-3,3,0,4]
console.log(threeSum(arr))
第二次提交
改良思路:
- 升序排序输入的数组
- 找到最小非负数, 然后设置两个指针:
left
(负数) 和right
(非负数) - 因为
arr[i] + arr[j] + arr[k] = 0
必须的形式一定是[负数, 负数, 非负数(>0)]
,[负数, 非负数(>=0), 非负数(>0)]
和[0, 0, 0]
这三种形式, 因此只需要依次判断原始数组在这三种情况下的输出即可
4. 考虑到二维数组中的数组重复问题, 还需要对二维数组进行去重处理 - 去重处理不再使用对象key的唯一性来去重,而是直接判断
arr[left] === arr[left + 1] ?
和arr[right] === arr[right - 1]?
- 返回去重后的结果
- 虽然可以正常返回结果,但是在面对大数据的时候. 遗憾的是,我的解法显示超时
/*
* @Author: fox
* @Date: 2022-05-01 00:34:12
* @LastEditors: fox
* @LastEditTime: 2022-05-02 08:19:27
* @Description: https://leetcode.com/problems/3sum/
*/
/**
* @description: 改良了一下,但是仍然超过时间限制 Time Limit Exceeded,看来问题不在这里,应该是在Array().indexOf中
* @param {number[]} nums
* @return {number[][]}
*/
const threeSum = (arr) => {
const res = [] // 临时存放返回结果
const myArr = arr.sort((a, b) => a - b) // 将原始数组升序排序
let middle; // 最小非负数的索引值
let differ; // 差值
if (arr.length < 3) return [] // 如果原始数组的长度不超过3,直接返回空数组
// 1. 找到最小非负数的索引值
for (let i in myArr) {
if (myArr[i] >= 0) {
middle = i
break
}
}
// 2. 遍历数组,取得正整数和非负整数的差值,存储到集合中 [-, -, +] [-, +, +]
for (let left = middle - 1; left >= 0; left--) {
if (myArr[left] === myArr[left + 1]) continue
for (let right = middle; right < arr.length; right++) {
if (myArr[right] === myArr[right - 1]) continue
differ = -(myArr[left] + myArr[right])
const index = differ > 0 ? myArr.lastIndexOf(differ) : myArr.indexOf(differ); // 负值从左向右遍历 正值从右往左遍历
if (index !== -1) {
// 如果存在,就让它以从小到大的形式存储到数组中
if (index < left) {
res.push([myArr[index], myArr[left], myArr[right]])
} if (index > right) {
res.push([myArr[left], myArr[right], myArr[index]])
}
}
}
}
// 3. 如果存在[0, 0, 0]的情况
if(myArr.filter(item => item === 0).length >= 3) {
res.push([0, 0, 0])
}
return res
}
let arr = [-1, 0, 1, 2, -1, -4]
console.log(threeSum(arr)) // [[-1,-1,2],[-1,0,1]]
arr = [1, 1, -2]
console.log(threeSum(arr)) // [-2, 1, 1]
arr = [-1,0,1,2,-1,-4,-2,-3,3,0,4]
console.log(threeSum(arr))
第三次提交
二次改良思路:
- 升序排序输入的数组
2. 找到最小非负数, 然后设置两个指针:left
(负数) 和right
(非负数) - 设置两个指针:
left
(数组最小值) 和right
(数组最大值) - 因为
arr[i] + arr[j] + arr[k] = 0
必须的形式一定是[负数, 负数, 非负数(>0)]
,[负数, 非负数(>=0), 非负数(>0)]
和[0, 0, 0]
这三种形式, 因此只需要依次判断原始数组在这三种情况下的输出即可
4. 考虑到二维数组中的数组重复问题, 还需要对二维数组进行去重处理 - 去重处理不再使用对象key的唯一性来去重,而是直接判断排序数组中
arr[left] === arr[left + 1] ?
和arr[right] === arr[right - 1]?
- 返回去重后的结果
- 正确提交.
/*
* @Author: fox
* @Date: 2022-05-01 00:34:12
* @LastEditors: fox
* @LastEditTime: 2022-05-02 10:24:54
* @Description: https://leetcode.com/problems/3sum/
*/
/**
* @description: Runtime: 5.00% Memory Usage: 99.90%
* @param {number[]} nums
* @return {number[][]}
*/
const threeSum = (arr, len = arr.length) => {
if (len < 3) return [] // 如果原始数组的长度不超过3,直接返回空数组
const res = [] // 存放返回结果
const myArr = arr.sort((a, b) => a - b) // 将原始数组升序排序
let differ; // 差值
let [left, right] = [0, len - 1] // 定义左右两个指针,分别指向排序数组的最小值和最大值
while (left < right && myArr[left] < 0 && myArr[right] >= 0) {
if (myArr[left] !== myArr[left - 1]) {
while (left < right && myArr[left] <= 0 && myArr[right] >= 0) {
if (myArr[right] !== myArr[right + 1]) {
differ = -(myArr[left] + myArr[right]);
if (differ < myArr[right] && differ > myArr[left]) {
if (myArr.includes(differ)) {
res.push([myArr[left], differ, myArr[right]]);
}
} else if (differ === myArr[left] && differ === myArr[left + 1]) {
res.push([myArr[left], differ, myArr[right]]);
} else if (differ === myArr[right] && differ === myArr[right - 1]) {
res.push([myArr[left], differ, myArr[right]]);
}
}
right--;
}
}
right = len - 1
left++;
}
if (myArr.filter(_ => _ === 0).length >= 3) {
res.push([0, 0, 0])
}
return res
}
let arr = [-1, 0, 1, 2, -1, -4]
console.log(threeSum(arr)) // [[-1,-1,2],[-1,0,1]]
arr = [1, 1, -2]
console.log(threeSum(arr)) // [-2, 1, 1]
arr = [-1,0,1,2,-1,-4,-2,-3,3,0,4]
console.log(threeSum(arr))
arr = [3,2,-2,3,-2,-5,4,-1,-5,4]
console.log(threeSum(arr))
大神解法
思路:
-
数组排序
-
使用三指针 -- 二和法的方法:
arr[x] + arr[y] + arr[k] === 0?
arr[x] + arr[y] + arr[k] > 0
移动y
指针arr[x] + arr[y] + arr[k] < 0
移动k
指针arr[x] + arr[y] + arr[k] = 0
数据进入数组ret
, 移动y
指针和k
指针
-
数组去重
while (nums[j] === nums[j+1]) j++; // 去重处理
while (nums[k] === nums[k-1]) k--; // 去重处理
/*
* @Author: fox
* @Date: 2022-05-02 11:20:41
* @LastEditors: fox
* @LastEditTime: 2022-05-02 11:23:19
* @Description: https://leetcode.com/problems/3sum/
*/
/**
* @description: Runtime: 89.32% Memory Usage: 88.32%
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
function threeSum(nums, target = 0) {
const ret = [];
if (nums.length < 3) {
return ret;
}
nums.sort((a,b) => a - b); // 数组排序
// 二和法
for (let i = 0; i < nums.length - 2 && nums[i] <= target; i++) { // 限制nums[i]只能在负数范围内移动
if (i > 0 && nums[i] === nums[i - 1]) { // 去重处理
continue;
}
for (let j = i + 1, k = nums.length - 1; j < k;) {
const sum = nums[i] + nums[j] + nums[k];
if (sum < target) { // 如果 sum < target 则移动j指针
j++;
continue;
} else if (sum > target) { // 同上,如果sum > target 则移动k指针
k--;
continue;
}
ret.push([nums[i], nums[j], nums[k]]); // 如果 sum === target 数据入组
while (nums[j] === nums[j+1]) j++; // 去重处理
while (nums[k] === nums[k-1]) k--; // 去重处理
// 注意: 下面两条语句的位置,必须放在这里 因为考虑到了continue语句的情况
j++;
k--;
}
}
return ret;
}
let arr = [-1, 0, 1, 2, -1, -4]
console.log(threeSum(arr)) // [[-1,-1,2],[-1,0,1]]
arr = [1, 1, -2]
console.log(threeSum(arr)) // [-2, 1, 1]
arr = [-1,0,1,2,-1,-4,-2,-3,3,0,4]
console.log(threeSum(arr))
arr = [3,2,-2,3,-2,-5,4,-1,-5,4]
console.log(threeSum(arr))