如何找到数组中出现指定次数的数字?

这篇具有很好参考价值的文章主要介绍了如何找到数组中出现指定次数的数字?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

问题一

一个数组中有一种数出现了奇数次, 其他数都出现了偶数次, 怎么找到这个种数?

在线OJ: LeetCode136. 只出现一次的数字

解题思路:

因为 a ^ a = 0, 所以出现过偶次的数异或结果都是 0, 又因为 0^a=a, 所以把数组中所有的数进行异或以后的结果, 就是出现了奇数次的那个数.

代码实现:

class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        for (int num : nums) {
            ans ^= num;
        }
        return ans;
    }
}

问题二

一个数组中有两种数出现了奇数次, 其他数都出现了偶数次, 怎么找到这两种数.

在线OJ: LeetCode260. 只出现一次的数字 III

根据问题一的结论, 假设数组中 ab 这两个数出现了奇数次, 这个数组中的所有数字异或以后得到的结果一定 a^b, 但因为 ab 是两种不同的数, 所以 a^b 的结果一定不等于 0.

所以, a^b 的结果如果转换成二进制的话, 一定至少有一个二进制位是 1.

我们假设 a^b 转换成二进制后最右侧位置的 1i 位置, 由此可以得出一个结论: a 和 b 的二进制在i位置一定一个为 0, 一个为 1.

不妨假设 ai 位置为 0, bi 位置为 1.

此外, 容易得知, 整个数组中的数, i 位置为 0 的数除了 a 以外, 其他数一定有偶数个, i 位置为 1 的数除了 b 之外, 其他数一定有偶数个.

那么我们可以只对 i 位置为 1 的数求异或, 最后得到的值一定是 b, 然后通过 b^(a^b) = a, 可以得到 a 的值.

最后只剩下一个问题: 如何求一个数只有其最右侧的1对应的的数呢?

假设某个数x二进制为: 00010010, 那么其只有最右侧的 1 对应的数就是: 00000010.

算法是: 对于一个数 x 来说, 其只有最右侧的 1 对应的数等于 x & ((~x) + 1) 或者 x & (-x).

所以, 如果一个数是 a^b, 那么它只有最右侧的1对应的数就是 (a^b) & (~(a^b) + 1).

我们用 (a^b) & (~(a^b) + 1) 这个值去和数组中每个值进行与运算, 如果与完以后的结果是 0, 说明这个数 i 位置是 0, 否则说明这个数 i 位置是1; 我们前面已经得到一个结论, i 位置为0的数除了 a 以外, 其他数一定有偶数个; 所以, 用 (a^b) & (~(a^b) + 1) 这个值和每个 i 位置是 0 的数组元素做与运算以后, 最后的结果一定是 a; 得到 a 以后, 然后通过 a^(a^b) = b, 可以得到 b 的值.

代码实现:

class Solution {
    public int[] singleNumber(int[] arr) {
        int ret = 0;
        for (int num : arr) {
            ret ^= num;
        }
        // 此时ans的结果是 a ^ b 的结果
        // 随便提取出 ans 随便哪一位置的 1 进行之后过程
        // 因为 a ^ b 的结果的这一位置为1, 那么这两个数的这一二进制位置一定不不同, 就可以以此进行区分
        // 一个数 & 上 它的相反数(取反+1)就可以可以提取出它最右侧的 1, 其余二级制位为0
        int rightOne = ret & (-ret);

        int ans1 = 0;
        for (int num : arr) {
            if ((num & rightOne) != 0) {
                ans1 ^= num;
            }
        }
        int ans2 = ret ^ ans1;

        return new int[] {ans1, ans2};
    }
}

当有了如下公式可以计算一个数最右侧的 1 以后, 也可以完成这样一个题

x & ((~x) + 1)

在线OJ: LeetCode191. 位1的个数

解题思路:

可以使用上面的公式提取出最右侧的 1 以后, 与目标数进行或运算, 得到一个新的目标数, 然后继续提取新目标数的最右侧的 1, 如此往复, 直到目标值为 0, 即可把所有位置的 1 都提取出来.

代码实现:

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        if (n == 0) {
            return 0;
        }
        int count = 0;
        int rightOne = 0;
        while (n != 0) {
            rightOne = n & (-n);
            count++;
            n ^= rightOne;
        }
        return count;
    }
}

问题三

一个数组中有一种数出现 k 次, 其他数都出现了 m 次 (m > 1, k < m), 找到出现了 k 次的数.

要求: 假设数组中所有数都是 int 类型, 额外空间复杂度 O(1), 时间复杂度 O(N).

在线OJ: LeetCode137. 只出现一次的数字 II

我们可以这样考虑, 设置一个 32 位的数组,

int[] help = new int[32];

遍历原始数组中每个数 num 的每一个二进制位, 伪代码如下:

for (int num : arr) {
  for (int i = 0; i < 32; i++) {
    help[i] += num的二进制中i位置的值(只能是0或者1)
  }
}

经过以上循环, help 数组就把数组中的所有数的二进制位上的信息累加起来了;

help[0] 表示数组中所有数二进制中0位置的值之和;

help[1] 表示数组中所有数二进制中1位置的值之和;

help[31] 表示数组中所有数二进制中31位置的值之和.

然后 i0 位置开始拿出 help[i] 的值, 假设 help[i]=x, 用 x % m, 如果结果是 k, 说明出现 k 次的元素在这个位置上是 1, 否则, 这个出现了 k 次的数在 i 位置上是 0, 遍历完 help 数组, 出现 k 次元素的每一位信息都拿到了, 然后还原出来即可.

通用代码:

/*一个数组中有一种数出现K次,其他数都出现了M次,
已知M > 1,K < M,找到出现了K次的数, 如果不存在出现了K次的数, 返回-1
要求额外空间复杂度O(1),时间复杂度O(N)*/
public static int onlyKTimes(int[] arr, int k, int m) {
    // 用来将 arr 数组中元素相应位置二进制位为 1 的个数
    int[] help = new int[32];
    // 遍历arr数组
    for(int num : arr) {
        // 将每一个元素二进制为 1 的个数进行累加
        for (int i = 0; i <= 31; i++) {
            // 判断每一位二进制位值是否为 1
            if (((num >> i) & 1) != 0) {
                // 为 1 就累加到相应位置
                help[i]++;
            }
            // 这个判断和 help[i] += (num >> i) & 1 等效
        }
    }
    int ans = 0;
    // 如果出现 m 次元素中的某个二进制位为 1, 那么 help 数组中相应位置的累加和一定是 m 的倍数
    // 如果出现 k 次元素中某个二进制为 1, 出现 m 次元素中的这个二进制位也为 1, 那么 help 数组中相应位置的累加和一定不能被 m 整数
    // 出现的不是 k 次, 但要比 m 小, 也是不能被 m 整除的, 返回-1
    for (int i = 0; i < 32; i++) {
        if (help[i] % m == 0) {
            continue;
        }
        // 如果找到了不能被 m 整除的, 等于k, 那么说明结果中这个二进制位一定是为 1 的
        // 不等于k, 那么除了出现 m 次的元素, 剩下的这个元素就不是出现 k 次的, 返回 -1 即可
        if (help[i] % m == k) {
            ans |= (1 << i);
        } else {
            return -1;
        }
    }
    // 出现 k 次的元素如果是0的话, 上面的遍历就会一直 continue, 就没有判断的时机了
    // 所以这里对于 0 的情况需要特殊判断一下
    if (ans == 0) {
        int count = 0;
        for (int num : arr) {
            if (num == 0) {
                count++;
            }
        }
        if (count != k) {
            return -1;
        }
    }

    // 如果题意是数组中出现 k 的数字一定存在, 可以换成下面的代码
    /*for (int i = 0; i < 32; i++) {
        if (help[i] % m != 0) {
            ans |= (1 << i);
        }
    }*/

    return ans;
}

解题代码:文章来源地址https://www.toymoban.com/news/detail-455226.html

class Solution {
    public int singleNumber(int[] arr) {
        int[] help = new int[32];
        for(int num : arr) {
            for (int i = 0; i <= 31; i++) {
                if (((num >> i) & 1) != 0) {
                    help[i]++;
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < 32; i++) {
            if (help[i] % 3 != 0) {
                ans |= (1 << i);
            }
        }
        if (ans == 0) {
            int count = 0;
            for (int num : arr) {
                if (num == 0) {
                    count++;
                }
            }
        }
        return ans;
    }
}

到了这里,关于如何找到数组中出现指定次数的数字?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 剑指 Offer 39. 数组中出现次数超过一半的数字

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🚁 个人主页:不 良 🔥 系列专栏:🛸剑指 Offer  🛹Linux 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸

    2024年02月07日
    浏览(9)
  • 剑指 Offer 56 - II. 数组中数字出现的次数 II(位运算 / 哈希表 / 排序)

    链接:剑指 Offer 56 - II. 数组中数字出现的次数 II 难度:中等 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。 示例 1: 输入:nums = [3,4,3,3] 输出:4 示例 2: 输入:nums = [9,1,7,9,7,9,7] 输出:1 限制: 1 = nums.length = 10000 1

    2024年02月13日
    浏览(7)
  • Practice1|1207. 独一无二的出现次数、1365. 有多少小于当前数字的数字、941. 有效的山脉数组

    Practice1|1207. 独一无二的出现次数、1365. 有多少小于当前数字的数字、941. 有效的山脉数组

    1.题目: 给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。 如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false。 示例 1: 输入:arr = [1,2,2,1,1,3] 输出:true 解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现

    2024年02月15日
    浏览(8)
  • JS操作数组神器——reduce(求和、出现次数、去重、分类)

    reduce() 对数组每个元素执行一次由您提供的reduce函数(升序执行),将其结果汇总为单个返回值。 循环遍历能做的,reduce都可以做。比如数组根据元素某个属性求和、数组元素出现次数、数组去重、数组根据某个元素属性分类等等。 参数介绍 prev 必需。累计器累计回调的返回值

    2023年04月26日
    浏览(10)
  • (数组) 1207. 独一无二的出现次数 ——【Leetcode每日一题】

    (数组) 1207. 独一无二的出现次数 ——【Leetcode每日一题】

    难度:简单 给你一个整数数组 arr ,请你帮忙统计数组中每个数的出现次数。 如果每个数的出现次数都是独一无二的,就返回 true ;否则返回 false 。 示例 1: 输入:arr = [1,2,2,1,1,3] 输出:true 解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出

    2024年02月08日
    浏览(9)
  • 每日一题——LeetCode1287.有序数组中出现次数超过25%的元素

    每日一题——LeetCode1287.有序数组中出现次数超过25%的元素

    方法一 一次循环统计 题目给出的数据相同的元素都是相邻的,那么直接从头开始遍历,统计每种元素出现次数,当有元素次数超过arr.length/4即为要求的元素   消耗时间和内存情况: 方法二 方法一简化版 设步长step=arr.length/4,如果某个元素arr[i],跨越一个步长后arr[i+step],即

    2024年01月22日
    浏览(13)
  • Leetcode448. 找到所有数组中消失的数字

    Leetcode448. 找到所有数组中消失的数字

    题目来源:448. 找到所有数组中消失的数字 set 是一个集合类型的容器,里面的元素具有唯一性,并且所有元素都会根据元素的键值自动被排序,以红黑树为底层数据结构。 我们使用集合set记录nums数组的元素,再遍历set找出找到所有的数组nums中消失的数字。 代码: 结果:

    2024年02月03日
    浏览(8)
  • Leetcode:【448. 找到所有数组中消失的数字】题解

    Leetcode:【448. 找到所有数组中消失的数字】题解

    题目 给你一个含  n  个整数的数组  nums  ,其中  nums[i]  在区间  [1, n]  内。请你找出所有在  [1, n]  范围内但没有出现在  nums  中的数字,并以数组的形式返回结果。 难度: 简单 题目链接:448. 找到所有数组中消失的数字 示例1 示例2 解题思路: 题目意思是再在有 n

    2024年02月11日
    浏览(9)
  • python输入一段英文,计算每个单词或数字出现的次数,并以字典方式输出。

    输入一段英文,计算每个单词或数字出现的次数。(这次有标点,但并没有加以区分,而是单独计数) 思路: 输入的字符串中会有多个重复的字符串,想要计数可以使用count函数。 这个题目更适合使用字典来解决,毕竟字典里的key不会重复,如果输入的内容重复了,就直接

    2023年04月16日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包