手摸手提桶跑路——LeetCode多数元素系列

123 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

169. 多数元素 [easy]

题目描述

给定一个大小为 n 的数组 nums ,返回其中的多数元素。

多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

解题思路——hashmap

使用一个 hashmap 来统计每个数字出现的次数,最后输出次数最多的那个。

这里除了对象外,我还使用了两个额外的变量 maxCntmaxValue,用于标记当前出现次数最多的数字及其对应的下标。当出现次数 hashmap[number] > maxCnt 时,更新 maxCntmaxValue 的值,这样最后输出的时候就不用再次遍历 hashmap 来找出现最多的数字了。

题解

var majorityElement = function(nums) {
    const lens = nums.length;
    if(lens == 1) return nums[0];
    const hashmap = {};
    let maxCnt = -Infinity, maxValue = -Infinity;
    for(let v of nums) {
        hashmap[v]++ || (hashmap[v] = 1);
        if(hashmap[v] > maxCnt) {
            maxCnt = hashmap[v];
            maxValue = v;
        }
    }
    return maxValue;
};

解题思路——摩尔投票算法

说实话第一次看的时候就觉得很巧妙了,不过没看懂。

写下今天的题解的时候,自己动手试了一遍,边写脑海里边过逻辑,噼里啪啦行云流水点击执行代码!没问题!点击提交!通过!

回到正题。

摩尔投票算法代入到本题,大概意思就是:把这每一个数字想象成一个士兵,那么出现的次数最多的数字代表的士兵数就最多,一换一的情况下,战争的最后赢家一定是人多的那边。

拿 [2,2,1,1,2,2] 来说:

  1. 初始化当前士兵为 2,士兵数量为1,遍历数组。
  2. 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为2。
  3. 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为3。
  4. 遇到1,非已阵营,一换一,那么2的士兵数量减1,此时2的士兵数量只剩1了。
  5. 遇到1,非已阵营,一换一,那么2的士兵数量减1,此时2的士兵数量为0了,gg,更换阵营,当前士兵为1,士兵数量1。
  6. 遇到2,非已阵营,一换一,那么1的士兵数量减1,此时1的士兵数量为0了,gg,更换阵营,当前士兵为2,士兵数量1。
  7. 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为2。

遍历到最后,存活士兵数最多的数字就是所求的解。

题解

var majorityElement = function(nums) {
    const lens = nums.length;
    if(lens == 1) return nums[0];
    let solider = nums[0], soliderCnt = 1;
    for(let i=0; i<lens; ++i) {
        if(solider === nums[i]) {
            soliderCnt++;
        } else {
            soliderCnt--;
        }
        if(soliderCnt === 0) {
            solider = nums[i];
            soliderCnt = 1;
        }
    }
    return solider;
};

解题思路——排序

官方题解有个娱乐解法,使用排序,那么 n/2 位置的一定是数量过半的。

229. 多数元素Ⅱ [middle]

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 1:

输入:nums = [3,2,3]
输出:[3]

提示:

  • 1 <= nums.length <= 5 * 104
  • -109 <= nums[i] <= 109

**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。

解题思路——Map

这道题和上一道题有点不一样了:所有出现次数超过 n/3 的元素。

也就是说结果是个数组

这道题可以用 Map 来进行统计,随后通过遍历 Map 集合,找到所有出现次数大于 n/3 的元素输出就好了。

题解

var majorityElement = function(nums) {
    const lens = nums.length, targetLens = lens / 3;
    if(lens == 1) return [nums[0]];
    const hashmap = new Map();
    for(let v of nums) {
        if(hashmap.has(v)) {
            hashmap.set(v, hashmap.get(v) + 1);
        } else {
            hashmap.set(v, 1);
        }
    }
    const res = []
    for(let [k,v] of hashmap) {
        v>targetLens && res.push(k);
    }
    return res;
};

解题思路——摩尔投票算法

对于 n 个数来说,如果我们要找到数量大于 n/2 的数字,那么这样的数字必然只存在一个,否则就超过 n 的范围了;同样的,找到数量大于 n/3 的数组,那么这样的数字必然只存在两个;由此推得,n 个数中,大于 n/k 的数字的个数不会超过 k-1 个。

那么对于上一题来说,需要找的是大于 n/2 的,结果只可能是一个数字,那么这道题需要找的是大于 n/3 的数字,答案至多有两个,因此我们的士兵数量增加到两个。

  1. 初始化两个变量 solider1solider2 代表接下来会遇到的两个阵营,数量都为0;开始遍历数组。

  2. 如果 cnt1 === 0 时,说明 solider1 还有空位放一个士兵,此时给 solider1 赋值并让 cnt1++

  3. 如果 cnt2 === 0 时,说明 solider2 还有空位放一个士兵,此时给 solider2 赋值并让 cnt2++

  4. 如果 cnt1 > 0,且当前遍历到的数字为 solider1 阵营时,士兵数 cnt1++

  5. 如果 cnt2 > 0,且当前遍历到的数字为 solider2 阵营时,士兵数 cnt2++

  6. 否则说明既不是 solider1 阵营也不是 solider2 阵营的,三方抵消,cnt1--;cnt2--

  7. 到最后 solider1solider2 的士兵数如果不为 0,则说明它们的士兵在数量上是取胜的,大于其他所有的数字,仅仅知道这个还不够,这会我们只是知道它俩的数量是 top2,而不知道它们的数量具体是多少?有没有大过 n/3

  8. 所以需要再遍历一次 nums 数组,判断存活士兵数量大于0的阵营的士兵数,得到的结果存到数组中。

题解

var majorityElement = function(nums) {
    const lens = nums.length, targetLens = lens / 3;
    if(lens == 1) return [nums[0]];
    let solider1 = 0, solider2 = 0, cnt1 = 0, cnt2 = 0;
    for(let i=0; i<lens; ++i) {
        if(cnt1 > 0 && solider1 === nums[i]){
            cnt1++;
        } else if(cnt2 > 0 && solider2 === nums[i]) {
            cnt2++;
        } else if(cnt1 === 0) {
            solider1 = nums[i];
            cnt1 = 1;
        } else if(cnt2 === 0) {
            solider2 = nums[i];
            cnt2 = 1;
        } else {
            cnt2--;
            cnt1--;
        }
    }
    let count1 = 0, count2 = 0, res = [];
    for(let v of nums) {
        if(v === solider1 && cnt1) {
            count1++;
        } else if(v === solider2 && cnt2) {
            count2++;
        }
    }
    if(count1 > targetLens) res.push(solider1);
    if(count2 > targetLens) res.push(solider2);
    return res;
};