妙!妙用二进制进行分组!

今天的两道题目来源于《剑指 offer》,是对之前很熟悉的题目只出现一次的数字的扩展,十分巧妙的用二进制进行了分组。妙,太妙了!

题目一 数组中只出现一次的两个数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。

请写程序找出这两个只出现一次的数字。

你可以假设这两个数字一定存在。

数据范围

数组长度 [1,1000]。

样例

输入:[1,2,3,3,4,4]

输出:[1,2]

解答

根据找唯一只出现一次的经验,会想到用异或运算,这里异或运算会将所有重复的数计算掉,只剩下只出现一次的两个数字的异或 x^y,如何分别求得 x 和 y 呢?

我们知道, x^y 中一位为 1,说明 x 和 y 的这一位一定一个为 1,另一个为 0。

根据这点将 nums 分为两簇,一簇是这一位为 1 的,一簇是这一位为 0 的。就将问题转化为了在两簇内分别求唯一的只出现一次的数字。妙!!!

int temp = 0;
        for (int num : nums) {
            temp ^= num;
        }
        // 找temp二进制 中 第一个为1的位
        int k = 0;
        while (((temp >> k) & 1) == 0) k++;
        int[] result = new int[2];
        result[0] = 0;
        // 根据 这一位是不是 1 来分组
        for (int num : nums) {
            if (((num >> k) & 1) == 1) result[0] ^= num;
        }
        result[1] = result[0] ^ temp;
        return result;

题目二 137. 只出现一次的数字 II

这道题的不同在于重复的数字会出现三次而不是两次。因此不能用异或运算一轮将重复的全部清除。

  • 但是仍然可以通过统计每一位上 1 出现的次数,如果是重复的数,这一位的 1 的次数一定是 3 的倍数,如果是 3*n + 1,那就是独立数字在这一位是 1。
  • 本题还需要注意的是,可能出现负数,因此我们用到的是无符号的右移。避免负数在右移的过程中高位补 1,影响计数。
class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        // 统计每一位 1 出现的次数(无符号右移)
        int[] cnts = new int[32];
        for (int i = 0; i < 32; i++){
            for (int num : nums) {
                cnts[i] += (num >>> i) & 1;
            }
        }

        // 复现出结果
        for (int i = 31; i >= 0; i--) {
            result <<= 1;
            result |= cnts[i] % 3;
        }
        
        return result;
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇