剑指Offer对答如流系列 - 数组中数字出现的次数

面试题56:数组中数字出现的次数

题目描述

问题(1)数组中只出现一次的两个数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

问题(2)数组中唯一只出现一次的数字

在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

问题分析

问题(1)分析

在这篇文章剑指Offer对答如流系列 - 二进制中 1 的个数中,我们详细探讨了位运算,其中有重要的一条:两个相同的数异或的结果是 0,一个数和 0 异或的结果是它本身

如果数组中只有一个数字只出现一次,我们从头到尾异或每个数字,那么最终的结果刚好是那个只出现一次的数字。

本题里数组中有两个数字只出现一次,所以要做一些处理:

  1. 我们依旧从头到尾异或每个数字,最终的结果就是这两个只出现一次的数字的异或结果,由于两个数不同,结果数字中一定有一位为1,把结果中第一个为1的位置记为第n位。由于是两个只出现一次的数字的异或结果,因此这两个数字在第n位上的数字一定是1和0。
  2. 接下来根据数组中每个数字的第n位上的数字是否为1来进行分组,恰好能将数组分为两个都只有一个数字只出现一次的数组,对两个数组从头到尾异或,就可以得到这两个数了。

问题(2)分析

数字出现了三次,不能直接利用异或位运算进行消除相同个数字。但是仍然可以沿用位运算的思路。

将所有数字的二进制表示的对应位都加起来,如果某一位能被三整除,那么只出现一次的数字在该位为0;反之,为1。

问题解答

问题(1)

  public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {

        if(array==null || array.length<2) {
            return;
        }
        int resultExclusiveOR=0;
        for(int i=0;i<array.length;i++) {
            resultExclusiveOR^=array[i];
        }

        int indexOf1=0;
        while(((resultExclusiveOR&1) == 0) && (indexOf1 <= 32)){
            //易错点:只有n>>1不完整,要n=n>>1
            resultExclusiveOR = resultExclusiveOR>>1;
            indexOf1++;
        }

        num1[0]=0;
        num2[0]=0;
        for(int i=0;i<array.length;i++){
            if(((array[i]>>indexOf1)&1) == 1) {
                num1[0]^=array[i];
            } else {
                num2[0]^=array[i];
            }
        }
    }

问题(2)

    public  int findNumberAppearingOnce(int[] arr) {
        if(arr==null || arr.length<=0) {
            throw new RuntimeException();
        }

        int[] bitSum = new int[32];
        Arrays.fill(bitSum, 0);

        for(int i=0;i<arr.length;i++) {
            int bitMask=1;
            for(int j=31; j>=0; j--) {

                int bit= arr[i]&bitMask;
                if(bit!=0) {
                    bitSum[j]+=1;
                }
                bitMask=bitMask<<1;
            }
        }
        int result=0;
        for(int i=0;i<32;i++) {
            result=result<<1;
            result+=(bitSum[i]%3);
        }
        return result;
    }

原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12246898.html

时间: 2024-08-04 15:20:26

剑指Offer对答如流系列 - 数组中数字出现的次数的相关文章

剑指Offer对答如流系列 - 数组中出现次数超过一半的数字

面试题39:数组中出现次数超过一半的数字 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 问题分析 大家最容易想到的思路是 数字次数超过一半,则说明排序之后数组中间的数字一定就是所求的数字. 既然是数组,要牵扯到排序,大家一般都会选用经典快速排序或者随机快速排序.随机快速排序由于每次划分的依据是从数组随机选出的,所以数据状况对它

剑指Offer对答如流系列 - 数组中的逆序对

面试题51:数组中的逆序对 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 问题分析 大多数人的第一反应就是顺序扫描整个数组,对每个数字都和后面的数字比较大小,时间复杂度为O(n^2),效率太低. 利用归并排序的思想,先将数组分解成为n个长度为1的子数组,然后进行两两合并同时排好顺序.(在排序的时候计算逆序对) 归并排序是经典排序算法之一,其核心是将待排数组不断细分,然后排序最后再合并,这是经典的分治策略(分

剑指Offer对答如流系列 - 二进制中 1 的个数

面试题14:二进制中 1 的个数 题目描述 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数.例如把9表示成二进制是1001,有2位是1.因此如果输入9,该函数输出2. 问题分析与解决 这道面试题归属于 <剑指Offer>位运算章节.遇到二进制相关的问题,很容易想到位运算,虽然种类不多(与.或.异或.左移.右移),但是搞起来是千变万化的.待会再和你侃一些骚操作,我们先看这道题. (一)思路一 "与运算"有一个性质:通过与对应位上为1,其余位为0的数进行与运算,可以

剑指Offer对答如流系列 - 圆圈中最后剩下的数字

面试题62:圆圈中最后剩下的数字 题目描述 0, 1, -, n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,从数字0开始每次删除第3个数字,则删除的前四个数字是2 0 4 1 因此最后剩下的数字是3 问题分析 思路一: 既然涉及到数据的频繁删除,可以考虑使用链表来存放数据,每次对长度取余数可以实现循环操作. 思路二: 这种问题规律性非常强,其实已经有对这一规律背后的数学模型的探究,即约瑟夫环 举一个具体的场景: 据说著名犹太历

剑指Offer对答如流系列 - 数据流中的中位数

面试题41:数据流中的中位数 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 所谓数据流,就是不会一次性读入所有数据,只能一个一个读取,每一步都要求能计算中位数. 问题分析 相信上一道题 最小的k个数 给了你容器的启示. 我们将读入的数据分为两部分,一部分数字小,另一部分大. 小的一部分采用大顶堆存放,大的一部分采用小顶堆存放.当总个数为偶数时,使

剑指Offer对答如流系列 - 链表中倒数第k个结点

面试题22:链表中倒数第k个结点 题目描述 输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点,从头结点开始它们的值依次是1.2.3.4.5.6.这个链表的倒数第3个结点是值为4的结点. 链表结点定义如下: public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } 问题分析 链表

剑指Offer对答如流系列 - 求1+2+…+n

面试题64:求1+2+-+n 题目描述 求1+2+-+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 问题分析 有了那么多限制,剩下的我们可以选择 单目运算符:++和--,双目运算符:+,-,移位运算符<>,关系运算符>,<等 逻辑运算符&&,||,&,|,^,赋值= 既然是一个等差数列,和为(n+1)*n/2 我们之前详细探讨了位运算剑指Offer对答如流系列 - 二进制中 1 的个

剑指Offer对答如流系列 - 不用加减乘除做加法

面试题65:不用加减乘除做加法 题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.×.÷四则运算符号. 问题分析 我们之前详细探讨了位运算 剑指Offer对答如流系列 - 二进制中 1 的个数,已经非常非常详细了. 这道题仅仅是让做加法,我们除此之外还是做了乘除与减法. 记不清的朋友可以回头看看. 这里象征性地做一次解答吧 问题解答 public int add(int num1,int num2) { while(num2!=0){ int sum=num1^num2; in

剑指Offer对答如流系列 - 把数组排成最小的数

面试题45:把数组排成最小的数 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3, 32, 321},则打印出这3个数字能排成的最小数字321323. 问题分析 之前我们做过字符全排列的习题 剑指Offer对答如流系列 - 字符串的排列,但是将算法思想应用到这一题的话,效果不好,求出所有的组合,再计算出组合的最小值,这效率该多低啊. 我们还要进一步探究,看看有没有不错的规律,供我们使用. 因为数字拼接后的长度一样,拼接后的结果