01 - 二进制

01 | 二进制:不了解计算机的源头,你学什么编程的学习总结;

一:对于进制我现在的理解就是几进制就是以几为基数,然后按照左高右低规则进行基数幂运算然后在乘以数量然后在相加。例如:二进制110,首先基数是“2”;坐高右低原则就是“2 1 0”分别对应最左边的“1 1 0”;其实坐高右低就是从右边以0开始然后依次加一,这个是进行幂运算的多少次方的数字。所以这个二进制数转换成我们日常的十进制的计算规则就是:12^2 + 12^1 + 0*2^0;最终结果就是 4 + 2 + 1 = 7;

二:二进制的位操作;
1.二进制的移位操作;分为左移(<<)和右移;其中右移又分为算数右移(>>)和逻辑右移(>>>);
二进制左移<<:110 其实就是一次把数字往左移动一位,最右边补0所以最终就是1100;左移规律是数字的值翻倍;
二进制右移>>:同上就是把数字往右边移动一位,然后最左边数字看情况;如果是算术右移就要考虑正负数的问题;正负数在计算机里面java实现是看你操作系统位数,最后一位代表正负数的标识;0:代表正数;1:代表负数;所以算术右移我理解就是考虑计算,既然考虑计算也就是考虑正负数的问题;对于负数的算术右移左边是要上1的,对于正数的算数右移左边是上0的;但是对于逻辑右移,不管你是正数还是负数,左边都是上0的;

2.二进制的逻辑操作;
逻辑或 | : 就是2个二进制数,从右到左,对相同位置的数字进行或运算;或运算就是 全是0才表示0,其余全是1;或顾名思义,只要有真就代表真;
逻辑与 & : 就是2个二进制数,从右到左,对相同位置的数字进行与运算;与运算就是参与运算数字全1才是1,其余的都是0;与顾明思议,相同的而且都是真才为真;
逻辑异或 ^: 就是2个二进制数吗,从右到左,对相同位置的数字进行运算;异或运算就是参与运算的数字相同结果为0,其余全为1;异或顾明思议,不同的才为真;

在计算机世界,1代表真,0代表假;

异或的一个运用

// 异或性质:
    1、交换律:a^b = b^a;

    2、结合律:(a^b)^c = a^(b^c);

    3、对于任意的a:a^a=0,a^0=a,a^(-1)=~a。

    了解了上面这些,来看看这个,很重要,后面的程序都要用到这个结论:

    对于任意的a,有a^b^c^d^a^k = b^c^d^k^(a^a) = b^c^d^k^0 = b^c^d^k,
    也就是说,如果有多个数异或,其中有重复的数,则无论这些重复的数是否相邻,都可以根据异或的性质将其这些重复的数消去,
    具体来说,如果重复出现了偶数次,则异或后会全部消去,如果重复出现了奇数次,则异或后会保留一个。

1、1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?

当然,这道题,可以用最直观的方法来做,将所有的数加起来,减去1+2+3+…+1000的和,得到的即是重复的那个数,该方法很容易理解,而且效率很高,也不需要辅助空间,唯一的不足时,如果范围不是1000,而是更大的数字,可能会发生溢出。

我们考虑用异或操作来解决该问题。现在问题是要求重复的那个数字,我们姑且假设该数字式n吧,如果我们能想办法把1-1000中除n以外的数字全部异或两次,而数字n只异或一次,就可以把1-1000中出n以外的所有数字消去,这样就只剩下n了。我们首先把所有的数字异或,记为T,可以得到如下:

1^2^...^n^...^n^...^1000 = 1^2^...^1000^(n^n)= 1^2^...^1000^0 = 1^2^...^1000(结果已经不包含n)

而后我们再让T与1-1000之间的所有数字(仅包含一个n)异或,便可得到该重复数字n。如下所示:

T^(1^2^3^4…^n…^1000) = T^(T^n) = 0^n = n

2、一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

样例输入:2 4 3 6 3 2 5 5
样例输出:4 6
1
2

思路:异或去重是知道如果只有一个只出现一次的数字的求法,但这里是有两个只出现一次的数字,我们便要想办法把他分为两个子数组,每个子数组中包含一个只出现一次的数字,其他的数字都出现了两次。

首先依然从头到尾异或所有的数字,如ABCCDDEEFF,这样得到的结果就是AB异或的结果,那么在异或后的结果中找出其二进制中最右边为1的位,该位既然为1,说明AB对应的该位肯定不同,必定一个为1,一个为0,因此我们可以考虑根据此位是否为1来划分这两个子数组,这样两个只出现一次的数字就分开了

但我们还要保证出现两次的数字都分到同一个子数组中,很明显,相同的数字相同的位上的值是相同的,要么都为1,要么都为0,因此我们同样可以通过判断该位是否为1来将这些出现两次的数字划分到同一个子数组中,该位如果为1,就分到一个子数组中,如果为0,就分到另一个子数组中。

这样就能保证每个子数组中只有一个出现一次的数字,其他的数字都出现两次,分别全部异或即可得到这两个只出现一次的数字。时间复杂度为O(n)。
另外,所有元素异或后,在找出最右边为1的时,X&(-X)之后得到的数字,是把X中最右边的1保留下来


3、如果不使用 Java 语言自带的 BigInteger 类,我们还有什么方法来实现十进制到二进制的转换呢?(提示:可以使用二进制的移位和按位逻辑操作来实现。)

answer:
数字与1做与操作,结果为1说明低位是1,否则为0,然后数字右移,重复以上操作,直到数字为0结束,倒序输出所有结果。就是讲二进制的每一位都取出来然后自己拼装

猜你喜欢

转载自blog.csdn.net/qq_29785317/article/details/85230916