题目
分析
这道题用一句话来归纳就是:不重复地找出所有和为0的三个数。
要想和为0, 则3个数里必须有正数也有负数,从这里可以考虑是否先将数组进行排序,这样正数和负数会被分别放到一起。
另一方面,如果只是找两个数之和,我们可以进行一个双重遍历,找个数之和,我们固然可以进行高时间复杂度的三重遍历,但是如果在第二个循环中用pointer(指针),我们便可以避免使用三重循环。
解决方法
const threeSum = function(nums) {
//先将数组进行排序
nums.sort((a, b) => a - b);
// 如果数组全是正数,或全是负数,则返回空数组;
if (nums[0] > 0 || nums[nums.length - 1] < 0) {
return [];
}
const res = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] > 0) { //负数和0部分已经遍历完后,后面的正数就不用再遍历了。
break;
}
if (i > 0 && nums[i] == nums[i - 1]) { //跳过重复元素
continue;
}
let sum = 0; // 定义sum和两个指针
let left = i + 1;
let right = nums.length - 1;
while (left < right) {
sum = nums[i] + nums[left] + nums[right]; //计算当前sum
if (sum > 0) { //如果sum>0,就将右指针左移
right--;
}
else if (sum < 0) { //如果sum<0,就将左指针右移
left++;
}
else {
res.push([nums[i], nums[left], nums[right]]);
right = shiftRightPointer(nums, left, right); //往中间移动左右指针,跳过重复元素,避免push重复数据。
left= shiftLeftPointer(nums, left, right);
}
}
}
return res;
};
// **********
function shiftRightPointer(nums, left, right) { // 将right指针向左移,跳过重复元素
let last_right_item = nums[right];
do {
right--;
}
while (left < right && nums[right] == last_right_item)
return right;
}
function shiftLeftPointer(nums, left, right) { // 将left指针向右移,跳过重复元素
let last_left_item = nums[left];
do {
left++;
}
while (left < right && nums[left] == last_left_item)
return left;
}