java之简单的Single Number算法

来源:互联网 发布:网络社交的利与弊英语 编辑:程序博客网 时间:2024/06/06 01:51

转载自:http://blog.csdn.net/ylyg050518/article/details/48345703

demo下载地址:http://download.csdn.net/download/qq_35559358/9938556

今天要说的是来源于leetcode(www.leetcode.com)网站的一道题目。

问题描述

原文描述:Given an array of integers, every element appears twice except for one.Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

大意:给定一个整形数组,数组当中除了一个元素外,其他元素都出现两次。找到这个唯一的元素。要求时间复杂度O(N),不使用额外辅助空间(空间复杂度O(1))。

思路分析

     这个题目貌似非常简单,但是仔细一想要考虑的问题还是挺多的。假如我们使用常规的循环来判定,你就会发现很多问题,比如给定的数组并没有说是有序的,可能是任意排列的。当用循环来判定的时候,如何保存每个元素出现的次数,如果可以的话完全可以构造一个hash表来存储每个元素出现次数,然后从hash表中找到次数为1的元素,但是这样就违背了题目中不使用额外的辅助空间的要求。所以,我们只能切换思路,思考有没有更加简单的办法。 
     根据给定数组的特征描述,我们可以知道,数组中的元素出现次数很有规律,仅有一个元素不符合出现两次的规律。那么,我们可以仅仅从次数出发,应用数据运算来筛选出这个元素。这里我们采用了位运算中异或(^)的操作。对于任意一个整形数A,A^0=A. 
A^A=0,而且异或操作满足交换律,A^B=B^A完全可以利用异或操作,对数组中的全部元素连续异或,全部操作执行完后,异或的最终结果就是数组中那个特殊的值。

代码实现

public int findSingle(int[] array) {        int temp = 0;        for (int i = 0; i < array.length; i++)            temp ^= array[i];        return temp;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

说明:上述代码时间复杂度为O(n),空间复杂度为O(1).只要数组中其余元素出现次数为偶数次,都可以运用异或来寻找这个single number.

问题变形

如果把上述问题中,数组中其他元素出现的次数改为3次呢?显然,以上的代码不能满足要求。那么,我们还能采用异或的操作吗?很明显单纯的异或操作不行。那么我们还能运用位运算吗?答案是可以的。 
思考这样一个问题,在计算机中储存的所有数据数据均是二进制的,在Java中,作为基本数据类型的整型数int在所占字节数为4(Byte),共有32个二进制位(bit)。我们是不是可以从整型变量的位特征出发,找到一些规律呢?如果是相同的整型值,那么展开之后的二级制位特征也是完全一样的,统计数组中每个int 元素的32位二进制表示中1的个数,我们可以创建一个长度为32 的数组 count[32] , count[i] 表示在在 i 位出现的 1 的次数。如果 count[i] 是 3 的整数倍,则忽略;否则就把该位取出来组成答案。

思路分析

问题1:如何取出每一位? 
这里我们用到了,向右移位运算(>>)和与运算(&),举个例子(注意为了描述方便,我们仅仅给出4位的二进制表示)例如14(二进制形式1110)(其实32位形式的话,前面还有很多0),要想分离出(1,1,1,0),方法就是与1(0001)做&运算取得一次结果0,然后将1110右移>>得到(0111),再与1(0001)做&运算取得结果1,依次类推。 
问题2:如何重新生成结果? 
由上面的分析,可以知道,统计过程完成之后,数组count[]中的值就是single number 的二进制表示,例如,这个single number 是14(1110,同样为了描述方便,我们只给出4位的二进制表示),保存在数组中的形式是这样的{0,1,1,1},同样我们用到移位运算,这次是左移位(<<),通过移位然后累加结果的方法可以得到10进制的结果(其实相当于2进制转换成10进制)。

代码实现

/*     * 数组中其余元素数量为3     *      */    public static int findSingle2(int[] array) {        int[] count = new int[32];        for (int i = 0; i < array.length; i++) {            for (int j = 0; j < 32; j++) {                count[j] += (array[i] >> j) & 1;// 获得最后一位数字                count[j] %= 3;            }        }        int result = 0;        // 数字恢复        for (int i = 0; i < 32; i++) {            result += (count[i] << i);        }        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

注意:上述算法时间复杂度O(n),空间复杂度O(1),但这时用到了常量个辅助存储,辅助空间大小不随问题规模N的增大而变化,所以空间复杂度仍然为O(1).

问题推广

我们同样将问题引申开来,如果其余元素数量为5或者任意指定的值num呢?经由以上分析,我们可以发现,只需将上述问题中的当中的3变成num,就可以将问题推广到任意的情况。

代码

/**时间复杂度O(n),空间复杂度O(1)*/public int findSingle3(int[] array, int num) {        int[] count = new int[32];        for (int i = 0; i < array.length; i++) {            for (int j = 0; j < 32; j++) {                count[j] += (array[i] >> j) & 1;// 获得最后一位数字                count[j] %= num;//此处为num            }        }        int result = 0;        // 数字恢复        for (int i = 0; i < 32; i++) {            result += (count[i] << i);        }        return result;    }demo下载地址:http://download.csdn.net/download/qq_35559358/9938556
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
原创粉丝点击