leetCode打卡——数组归位法

403 阅读3分钟
No. 题目名 中文简介
287 Find the Duplicate Number 寻找重复数
442 Find All Duplicates in an Array 数组中重复的所有数字
41 First Missing Positive 首个缺失的正数
268 Missing Number 缺失数字
448 Find All Numbers Disappeared in an Array 找到所有数组中消失的数字

这些题可以用相同的思路进行解题,题目的数据结构都是数字数组,个数有限,并且数字都被数组的长度限制。先遍历把数组中的数字放到对应的位置上,然后再遍历,如果一个位置上的数字不符合预期,则能找出结果。

var swap = function(nums, i, j) {
    nums[i] ^= nums[j];
    nums[j] ^= nums[i];
    nums[i] ^= nums[j];
}

No.287

No.287我是用的快慢指针的方式解题的,这个题目跟寻找回路链表的交叉节点很像,只是数据结构换成了数组,可以完全根据回路链表的交叉节点的思路解题。 当然,这道题也可以使用数组归位法。

var findDuplicate = function(nums) {
    var fast = nums[0];
    var slow = nums[0];

    var isFirst = true
    while (fast !== slow || isFirst) {
        isFirst = false;
        if (fast && nums[fast]) {
            fast = nums[fast];
            fast = nums[fast];
        } else {
            return false;
        }
        
        slow = nums[slow];
    }

    slow = nums[0];

    while (fast !== slow) {
        fast = nums[fast];
        slow = nums[slow];
    }

    return slow;
};

No.442

No.442由于同一个数字可能出现超过两次,如果使用数组归位法会输出多个相同的重复数字,这是不符合题意的,所以以下的代码不能通过。

// 重复频次多于两个的会重复输出。
var findDuplicates2 = function(nums) {
    const res = [];
    for (let i = 0; i < nums.length; i++) {
        const pos = nums[i] - 1;
        if (nums[pos] === nums[i]) {
            if (i !== pos)
                res.push(nums[i]);
        } else {
            swap(nums, pos, i);
            i--;
        }
    }

    return res;
}

由于题意表明 1 ≤ a[i] ≤ n (n = size of array),我们可以想象数字是地标指引,指引我们去另一个地标,当这个地标在之前已经出现过了,说明我们已经来过这个地方,也就是说该地标指引之前出现过了。当一个数字多次出现的时候,也就是该数字作为索引会发现多次同样的数字,此时我们将目标地数字置反表明来过即可。

// 访问记录法
var findDuplicates = function(nums) {
    var res = [];

    for (let i = 0; i < nums.length; i++) {
        if (
            // 发现刻字了,表明来过
            nums[
                Math.abs(nums[i]) - 1
            ] < 0
        ) {
            res.push(
                Math.abs(nums[i])
            );
        } else {
            // 第一次来,刻字- -
            nums[
                Math.abs(nums[i]) - 1
            ] *= -1;
        }
    }

    return res;
};

No.41

这道题使用归位法,将数字归位到对应的索引上,然后遍历归位后的数组,发现对应位置上的数字不符合,则该索引下本该存在的数字就是最小正数了

var firstMissingPositive = function(nums) {
    for (let i = 0; i < nums.length; i++) {
        const val = nums[i]
        if (val >= 0 && val <= nums.length) {
            const pos = val - 1;
            if (val !== nums[pos]) {
                swap(nums, i, pos);
                i--;
            }
        }
    }

    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== i + 1) {
            return i + 1;
        }
    }

    return nums.length + 1;
};

No.268

大家看了上面几个归位法,相信这道题也是很简单的能想到归位法(包括我),但是这道题有个很简单的思路(偷窥发现),就是利用同数字异或等于0这个特点。

题意是0 ≤ a[i] ≤ n (n = size of array),n + 1个数字,但是数组长度是n,所以有一个数字是不在数组里的,可以想象一下,如果把数组归位一下,其实数组里基本上是索引等于索引指向的内容,例如[0, 1, 2, 3],[0, 1, 2, 4],如果将索引和数字异或一下,可以根据归位后的数组,大多数情况下都等于0,只有缺失了数字才不为0

// 异或法
var missingNumber2 = function(nums) {
    var res = nums.length;
    for (let i = 0; i < nums.length; i ++) {
        res ^= nums[i] ^ i;
    }
    return res;
}

No.448

归位法,在这类题目里最朴素实用

var findDisappearedNumbers = function(nums) {
    var res = []
    for (let i = 0; i < nums.length; i++) {
        const pos = nums[i] - 1;
        if (nums[pos] !== nums[i]) {
            swap(nums, pos, i);
            i--
        }
    }

    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== i + 1) {
            res.push(i + 1);
        }
    }

    return res;
};