数组

双指针

双指针是一种常用的解题思路,可以使用两个相反方向或相同方向的指针扫描数组从而达到解题目的。值得注意的是,本书在不同的章节都提到了双指针。本书中的“指针”并不专指C语言中的指针,而是一个相对宽泛的概念,是能定位数据容器中某个数据的手段。在数组中它实际上是数字的下标。

相反指针

面试题6:排序数组中的两个数字之和

题目:输入一个递增排序的数组和一个值k,请问如何在数组中找出两个和为k的数字并返回它们的下标?假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。例如,输入数组[1,2,4,6,10],k的值为8,数组中的数字2与6的和为8,它们的下标分别为1与3。


/**
 * @param {number[]} numbers
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(numbers, target) {

    let l = 0
    let r = numbers.length - 1

    while(l < r) {
        if(numbers[l] +numbers[r]===target) {
            return [l,r]
        } else if(numbers[l] +numbers[r]>target) {
            r--
        } else {
            l++
        }
    }
};

没啥好说的,相反指针的经典题目:2数之和,明显特征有序数组。

面试题7:数组中和为0的3个数字

题目:输入一个数组,如何找出数组中所有和为0的3个数字的三元组?需要注意的是,返回值中不得包含重复的三元组。例如,在数组[-1,0,1,2,-1,-4]中有两个三元组的和为0,它们分别是[-1,0,1]和[-1,-1,2]。

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    nums.sort((a, b) => a-b);
    let res = []
    for(let i = 0; i < nums.length-2;i++) {        
        // 去重
        if (i > 0 && nums[i] == nums[i - 1]) continue
        let l = i+1;
        let r = nums.length-1
        while(l < r) {
            // 怎么去重
            if(nums[l]+nums[r]+nums[i]===0) {
                res.push([nums[i], nums[l], nums[r]])
                while(l<r&&nums[l] === nums[++l]);
                while(l<r&&nums[r] === nums[--r]);
            } else if(nums[l]+nums[r]+nums[i]>0) {
                r--
            } else {
                l++
            }
        }
    }
    return res
};

首先是排序,难的是处理去重。

面试题8:和大于或等于k的最短子数组

题目:输入一个正整数组成的数组和一个正整数k,请问数组中和大于或等于k的连续子数组的最短长度是多少?如果不存在所有数字之和大于或等于k的子数组,则返回0。例如,输入数组[5,1,4,3],k的值为7,和大于或等于7的最短连续子数组是[4,3],因此输出它的长度2。

特征:子数组?

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let sum  = 0
    let l = 0
    let min = Infinity
    for(let r = 0; r < nums.length; r++) {
        sum += nums[r];
        // 满足条件,开始缩减
        while(sum>=target) {
            // 若满足缩减则减小sun 并 移动l
            if(sum-nums[l]>= target) {
                sum-=nums[l]
                l++
            } else {
            // 若不满足则记录比较最小值,同时跳出缩减循环
                min = Math.min(min,  r - l + 1)
                break
            }
        }
    }
    if (min===Infinity) return 0
    return min

};

快慢指针

累加数组数字求子数组之和

面试题10:和为k的子数组

题目:输入一个整数数组和一个整数k,请问数组中有多少个数字之和等于k的连续子数组?例如,输入数组[1,1,1],k的值为2,有2个连续子数组之和等于2。
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function(nums, k) {
    let map = {}
    let sum = 0
    let count = 0
    map[0] = 1
    for (let num of nums) {
        sum += num
        count += map[sum - k] == undefined ? 0 : map[sum - k]
        if(map[sum] == undefined) {
            map[sum] = 1
        } else {
            map[sum] = map[sum] +  1 
        }
    }
    return count
};

面试题11:0和1个数相同的子数组

输入一个只包含0和1的数组,请问如何求0和1的个数相同的最长连续子数组的长度?例如,在数组[0,1,0]中有两个子数组包含相同个数的0和1,分别是[0,1]和[1,0],它们的长度都是2,因此输出2。
var findMaxLength = function(nums) {
    let maxLength = 0;
    const map = new Map();
    let counter = 0;
    map.set(counter, -1);
    const n = nums.length;
    for (let i = 0; i < n; i++) {
        const num = nums[i];
        if (num == 1) {
            counter++;
        } else {
            counter--;
        }
        if (map.has(counter)) {
            const prevIndex = map.get(counter);
            maxLength = Math.max(maxLength, i - prevIndex);
        } else {
            map.set(counter, i);
        }
    }
    return maxLength;
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/A1NYOS/solution/0-he-1-ge-shu-xiang-tong-de-zi-shu-zu-by-xbyt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

面试题12:左右两边子数组的和相等

题目:输入一个整数数组,如果一个数字左边的子数组的数字之和等于右边的子数组的数字之和,那么返回该数字的下标。如果存在多个这样的数字,则返回最左边一个数字的下标。如果不存在这样的数字,则返回-1。例如,在数组[1,7,3,6,2,9]中,下标为3的数字(值为6)的左边3个数字1、7、3的和与右边两个数字2和9的和相等,都是11,因此正确的输出值是3。

/**
 * @param {number[]} nums
 * @return {number}
 */
var pivotIndex = function(nums) {
    let sum = 0
    nums.forEach(i => sum += i);
    let count = 0
    for(let i = 0; i < nums.length; i++) {
        count += nums[i]
        if(count - nums[i] === sum - count) {
            return i
        }
    }

    return -1
};

总结

如果关于子数组的数字之和的面试题并没有限定数组中的所有数字都是正数,那么可以尝试从第1个数字开始累加数组中前面若干数字之和,两个累加的和的差值对应一个子数组的数字之和。这种累加数组中前面若干数字之和的思路,不仅适用于一维数组,还适用于二维数组。这也是前缀和的常见题型。


看见了
876 声望16 粉丝

前端开发,略懂后台;