每日算法题 2——(多数之和)

245 阅读2分钟

排序数组中的两个数字之和的下标

题目描述:

输入一个递增排序的数组和一个值target,在数组中找出两个和为target的数字并返回它们的下标 提示: 数组中有且只有一对符合要求 同时一个数字不能使用两次

示例:输入数组: [1,2,4,6,10], k的值为8 输出[1,3]

分析: 采用双指针进行遍历,因为是递增的,若首尾之和不等于target,且左指针小于右指针,则进入循环判断让左指针左移或右指针右移。

        function twoSumSortedArray (nums, target) {
            let left = 0, right = nums.length - 1 //初始化指针left, right
            while (left < right && nums[left] + nums[right] != target) {
                if(nums[left] + nums[right] < target) {
                    left++
                } else {
                    right--
                }
            }
            return [left, right]
        }
  • 时间复杂度:O(n),只遍历一次数组
  • 空间复杂度:O(1)

求数组中两数之和的值的下标

        function twoSum(nums, target) {
            let map = new Map() //用于存储nums[i], i之间的关系
            for(let i=0; i< nums.length; i++) {
                let expectValue = target - nums[i]
                // 先从map 中找,是否存在指定值
                if(map.has(expectValue)) {
                    // 如果有,直接返回与值相对应的下标
                    return [map.get(expectValue), i]
                }
                // 存储 nums[i],i 之间的关系
                map.set(nums[i], i)
            }
            return null
        }
  • 时间复杂度:O(n),只遍历一次数组
  • 空间复杂度:O(n),使用了Map

数组中和为target 的3个数字

剑指 Offer II 007. 数组中和为 0 的三个数 - 力扣(LeetCode) 题目描述

输入一个数组,找出数组中所有和为target的3个数字的三元组 提示: 返回值不得包含重复的三元组

示例:输入数组: [-1,0,1,2,-1,-4],target的值为0 输出[[-1,0,1],[-1,-1,2]]

方法:排序 + 双指针

枚举的三元组(a, b, c)满足 a<= b <=c,保证了只有(a, b, c)这个顺序会被枚举到,而(b,a,c), ( c,a,b)等等这些不会,减少了重复。要实现这一点,可以将数组中的元素从小到大进行排序

var threeSum = function(nums, target) {
    let res = []
    if(nums.length < 3) return []
    // 先排序
    nums.sort((a, b) => a - b)
    for(let i = 0; i < nums.length - 2; i++) {
        // nums[i] > 0说明后面的元素肯定也大于0,最后结果肯定>0,故直接跳出
        if(nums[i] > target) break
        // 如果当前元素和前面一个元素一样,忽略重复元素
        if(i > 0 && nums[i] === nums[i - 1]) continue
        let start = i + 1, end = nums.length - 1
        while(start < end) {
            let sum = nums[i] + nums[start] + nums[end]
            // 如果三数之和 >0,说明最右边的值太大了
            if(sum > target) {
                end--
                // 如果移动过程中碰到重复元素,则继续移动
                while( start < end && nums[end + 1] == nums[end]) {
                    end--
                }
            } else if( sum < target) {
                start++
                while(start < end && nums[start - 1] ==nums[start]) {
                    start++
                }
            } else {
                res.push([nums[i], nums[start], nums[end]])
                start++
                end--
                // 同时左指针往右移动,右指针往左移动
                // 如果移动过程中碰到重复元素,则继续移动
                while(start < end && nums[start - 1] == nums[start]) {
                    start++
                }
                while(start < end && nums[end + 1] == nums[end]) {
                    end--
                }
            }
        }

    }
    return res
}

复杂度分析:

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(logN),忽略储存答案的空间,额外的排序的空间复杂度为O(logN),然而修改了输入的数组nums,在实际情况下不一定允许,因此也可以看成使用一个额外的数组储存了nums的副本并进行排序,空间复杂度为O(N)

数组中和为target的四个数字

与三数之和 相似,解法也类似

题目描述: 给一个由 n 个整数组成的数组nums,和一个目标值 target。请找出并返回满足下述全部条件且不重复的四元组:

  • 0 <= a, b, c, d <n
  • a, ,b, c, d互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] === target
    var fourSum = function (nums, target) {
        let res = []
        if (nums.length < 4) return res
        nums.sort((a, b) => a - b)
        let n = nums.length
        for (let i = 0; i < n - 3; i++) {
            //注意判断 i>0时出现的前后两个数相同的情况时,直接进入下一轮,必须要带上 i>0,否则对于例如数组[2,2,2,2],判断出错
            if (i > 0 && nums[i] == nums[i - 1]) continue
            if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break
            if (nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target) continue
            for (let j = i + 1; j < n - 2; j++) {
                //注意要判断 j大于i+1,否则当第二轮循环中的第一个数与第一轮循环的数相等时会退出循环
                if (j > i + 1 && nums[j] == nums[j - 1]) continue
                if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) break
                if (nums[i] + nums[j] + nums[n - 2] + nums[n - 1] < target) continue
                let left = j + 1, right = n - 1
                while (left < right) {
                    let sum = nums[i] + nums[j] + nums[left] + nums[right]
                    if (sum > target) {
                        right--
                        while (left < right && nums[right] === nums[right + 1]) {
                            right--
                        }
                    } else if (sum < target) {
                        left++
                        while (left < right && nums[left] == nums[left - 1]) {
                            left++
                        }
                    } else {
                        res.push([nums[i], nums[j], nums[left], nums[right]])
                        left++
                        right--
                        while (left < right && nums[left] == nums[left - 1]) {
                            left++
                        }
                        while (left < right && nums[right] === nums[right + 1]) {
                            right--
                        }
                    }
                }
​
            }
        }
        return res
    };

复杂度分析:

  • 时间复杂度:O(N^3)
  • 空间复杂度:O(logN),忽略储存答案的空间,额外的排序的空间复杂度为O(logN),然而修改了输入的数组nums,在实际情况下不一定允许,因此也可以看成使用一个额外的数组储存了nums的副本并进行排序,空间复杂度为O(N)