多数和的问题

191 阅读4分钟

leetcode上和的问题有很多,两数之和,三数之和,四数之和,今天对这些问题做一个总结;

两数之和

两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。  

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

来源:leetcode-cn.com/problems/tw…

暴力解法

var twoSum = function(nums, target) {
    let i = nums.length;
    while(i) {
        let val = nums.pop();
        if(nums.indexOf(target - val) > -1) {
            return [nums.indexOf(target - val), nums.length]
        }
        i--;
    }
};

两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。 
**说明:
**

  • 返回的下标值(index1 和 index2)不是从零开始的。

  • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素

    输入: numbers = [2, 7, 11, 15], target = 9
    输出: [1,2]
    解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2

来源:leetcode-cn.com/problems/tw…

双指针

var twoSum = function(numbers, target) {
    let left = 0, right = numbers.length - 1;
    while(left <= right) {
        if(numbers[left] + numbers[right] < target) {
            left++;
        } else if(numbers[left] + numbers[right] > target) {
            right--;
        } else if(numbers[left] + numbers[right] === target) {
            return [left + 1, right + 1]
        }
    }
};

两数之和 IV - 输入 BST

给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

输入: 
    5
   / \
  3   6
 / \   \
2   4   7

Target = 9

输出: True

来源:leetcode-cn.com/problems/tw…

解法:

二叉搜索树中序遍历的结果是排序数组,然后对遍历结果进行二分法计算

var findTarget = function(root, k) {
    let arr = [], ans = false;
    let help = (node) => {
        if(!node) return;
        help(node.left);
        arr.push(node.val);
        help(node.right);
    }
    help(root);
    let left = 0, right = arr.length - 1;
    while(left < right) {
        let sum = arr[left] + arr[right];
        if(sum === k) {
            ans = true;
            break;
        } else if(sum > k) {
            right--
        } else {
            left++;
        }
    }
    return ans;
};

两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。  

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

来源:leetcode-cn.com/problems/ad…

var addTwoNumbers = function(l1, l2) {
    let dummy = { next: null };
    let tmp = dummy, sum, n = 0;
    while(l1 || l2) {
        const n1 = l1 ? l1.val : 0;
        const n2 = l2 ? l2.val : 0;
        sum = n1 + n2 + n;
        tmp.next = new ListNode(sum % 10);
        tmp = tmp.next;
        n = parseInt(sum / 10);
        if(l1) l1 = l1.next;
        if(l2) l2 = l2.next;
    }
    if(n > 0) tmp.next = new ListNode(n);
    return dummy.next;
};

两数相加 II

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外,这两个数字都不会以零开头。  

输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7

来源:leetcode-cn.com/problems/ad…

var addTwoNumbers = function(l1, l2) {
    let stack1  = [], stack2 = [];
    while(l1) {
        stack1.push(l1.val);
        l1 = l1.next;
    }
    while(l2) {
        stack2.push(l2.val);
        l2 = l2.next;
    }
    let dummy = null, sum = 0, n = 0;
    while(stack1.length > 0 || stack2.length > 0 || n != 0) {
        let n1 = stack1.length ? stack1.pop() : 0;
        let n2 = stack2.length ? stack2.pop() : 0;
        sum = n1 + n2 + n;
        let node = new ListNode(sum % 10);
        n = parseInt(sum / 10);
        node.next = dummy;
        dummy = node;
    }
    return dummy;
};

两整数之和

不使用运算符 +- ​​​​​​​,计算两整数 ​​​​​​​ab ​​​​​​​之和。

输入: a = 1, b = 2
输出: 3

来源:leetcode-cn.com/problems/su…

位运算

var getSum = function(a, b) {
    if (a === 0) return b;
    if (b === 0) return a;
    return getSum(a ^ b, (a & b) << 1);
};

三数之和

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 注意:答案中不可以包含重复的三元组。 

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

来源:leetcode-cn.com/problems/3s…

排序+双指针

var threeSum = function(nums) {
    if(nums.length < 3) return [];
    const list = [];
    nums.sort((a, b) => a - b);
    for (let i = 0; i < nums.length; i++) {
        if(nums[i] > 0) break;
        if(i > 0 && nums[i] === nums[i - 1]) continue;
        let left = i + 1, right = nums.length - 1;
        while(left < right) {
            if(nums[left] + nums[right] + nums[i] === 0) {
                list.push([nums[left], nums[right], nums[i]])
                while(nums[left] === nums[left + 1]) {
                    left++;
                }
                left++;
                while(nums[right] === nums[right - 1]) {
                    right--;
                }
                right--;
            } else if(nums[left] + nums[right] + nums[i] > 0) {
                right--;
            } else {
                left++;
            }
        }
    }
    return list;
};

最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

来源:leetcode-cn.com/problems/3s…

排序+双指针

var threeSumClosest = function(nums, target) {
    nums.sort((a, b) => a - b);
    let ans = nums[0] + nums[1] + nums[2];
    for (let i = 0; i < nums.length; i++) {
        let l = i + 1, r = nums.length - 1;
        while(l < r) {
            let sum = nums[i] + nums[l] + nums[r];
            if(Math.abs(target - sum) < Math.abs(target - ans)) {
                ans = sum;
            }
            if(sum > target) {
                r--;
            } else if(sum < target)  {
                l++;
            } else {
                return ans;
            }
        }
    }
    return ans;
};

三数之和的多种可能

给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。 由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。

输入:A = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(A[i]A[j]A[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。

来源:leetcode-cn.com/problems/3s…

排序+双指针

var threeSumMulti = function(A, target) {
    A.sort((a, b) => a - b)
    let res = 0;
    let len = A.length;
    for (let i = 0; i < len - 2; i++) {
        let l = i + 1, r = len - 1;
        while(l < r) {
            let total = A[i] + A[l] + A[r];
            if(total === target) {
                if(A[l] === A[r]) {
                    res += Math.floor((r - l + 1) * (r - l) / 2);
                    break;
                } else {
                    let tmp = 1;
                    while(A[l] === A[++l]) tmp++;
                    res += tmp;
                    while(A[r] === A[--r]) res += tmp;
                }
            } else if(total > target) {
                r--;
            } else {
                l++;
            }
        }
    }
    return res % (1e9 + 7)
};

四数之和

四数之和

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。  答案中不可以包含重复的四元组。

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

来源:leetcode-cn.com/problems/4s…

var fourSum = function(nums, target) {
    let res = [];
    if(nums.length < 4) return res;
    nums.sort((a, b) => a - b);
    for (let i = 0; i < nums.length; i++) {
        if(i > 0 && nums[i] === nums[i - 1]) {
            continue;
        }
        for (let j = i + 1; j < nums.length; j++) {
            if(j > i + 1 && nums[j] === nums[j - 1]) {
                continue;
            }
            let l = j + 1, r = nums.length - 1;
            while(l < r) {
                let sum = nums[i] + nums[j] + nums[l] + nums[r];
                if(sum === target) {
                    res.push([nums[i], nums[j], nums[l], nums[r]]);
                    while(l < r && nums[l] == nums[l + 1]) {
                        l++;
                    }
                    while(l < r && nums[r] === nums[r - 1]) {
                        r--;
                    }
                    l++;
                    r--;
                } else if (sum < target) {
                    l++;
                } else {
                    r--;
                }
            }
        }
    }
    return res;
};

 四数相加 II

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。 

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

来源:leetcode-cn.com/problems/4s…

哈希值

var fourSumCount = function(A, B, C, D) {
    const sumMapper = {};
    let res = 0;
    for (let i = 0; i < A.length; i++) {
        for (let j = 0; j < B.length; j++) {
            sumMapper[A[i] + B[j]] = (sumMapper[A[i] + B[j]] || 0) + 1;
        }
    }
    for (let i = 0; i < C.length; i++) {
    for (let j = 0; j < D.length; j++) {
        res += sumMapper[- (C[i] + D[j])] || 0;
    }
  }

  return res;
};