我与二进制的微妙关系

Hello world!很久没写博客了。家庭与生活,工作与力扣,难舍难分,总之一言难尽。今天写一下我对二进制的一知半解,分享给各位,共勉!

一、介绍二进制

说起二进制,我们不得不从 0 开始。假设现在让我们数数,一般我们数到 9 的下一个数就会进十,变成 10。而二进制,就是数到 2 变成 10。任何进制包括  8 或 16 进制的进制原理都和这个类似。

我们用计算机存储的数据,都会使用二进制进行存储,也就是 n 个 0 与 n 个 1 进行组合。主要因为二进制的数制系统相较于其他进制只有 0 和 1 两个标识,简单且方便计算机识别与处理。

二、二进制的运算符

下面简单介绍这些运算符。

按位与(And)( & )

按位与的运算原理,若把 1 看作 true 的话与逻辑运算符 && 很像。当两个数的二进制同位都为 1 时,运算为 1,否则为 0。

(1 & 1) = (01 & 01) = 01

(1 & 0) = (01 & 00) = 00

(2 & 1) = (10 & 01) = 00

按位或(Or)( | ) 

按位或与逻辑运算的 || 很像。当两个数的二进制同位只要存在1个或2个 1 时,运算都为 1,否则为 0。

(1 | 1) = (01 | 01) = 01

(1 | 0) = (01 | 00) = 01

(2 | 1) = (10 | 01) = 11

按位异或(Xor)( ^ ) 

按位异或就是当两个数的二进制同位进行差值取绝对值。

(1 ^ 1) = (01 ^ 01) = 00

(1 ^ 0) = (01 ^ 00) = 01

(2 ^ 1) = (10 ^ 01) = 11

取反( ~ ) 

取反,顾名思义,就是将二进制每位上的 0 变为 1,1 变为 0。

(~2) = (~10) = 11111111111111111111111111111101

(~3) = (~11) = 11111111111111111111111111111100

左移( << ) 

左移,顾名思义,就是将二进制中右侧的第一个 1 到左侧的部分向左移动几个单元,也可以说是在二进制后补几个 0

(1 << 1) = (01 << 1) = 10

(9 << 3) = (1001 << 3) = 1001000

(2 << 3) = (10 << 3) = 10000

右移( >> ) 

右移,与左移相反,右移若没有位数可以移动,就是 0

(1 >> 1) = (01 >> 1) = 00

(9 >> 3) = (1001 >> 3) = 01

(2 >> 3) = (10 >> 3) = 00

 三、二进制的实际应用

为了更充分的利用刚刚学到的二进制运算符原理,下面我们直接进入今天的主题,用二进制都能干啥?

二进制如果利用得当,可以很大程度的提高程序的运行效率。

1、字符串系列

  • 字符串与数字进行转换。

对于阿斯克码值中,数字范围与种类相对较小的就是英文的大小写字符。大小写分别为 26 种,也就是 A ~ Z 与 a ~ z。那么对于一个字符串来说,如果纪录这串字符中都使用了哪些字母,我们可以使用二进制来解决。

字符串:abcdefg - 0123456 - 1111111 - 127

解释:对于小写字符,a 可以看为 0,z 可以看作 25,最高不超过 int 类型的二进制最大位的31位。因此,这里不考虑原字符串的字母排列顺序以及每个字母的使用频次,那么使用 int 来存储所有使用过的字符完全足够。也就是说,127 这个数字告诉我们,它的二进制 0 到 6 位都是 1,那么通过阿斯克码转换,可以知道这个字符串使用了 a ~ g 这 7 个字符

代码:

    public int charToNum(String str) {
        int mask = 0;
        for (int c : str.toCharArray()) {
            mask |= (1 << c - 'a');
        }
        return mask;
    }

 

  • 两个字符串比较是否存在使用了相同的字符

基于前面的字符串转数字的技巧,我们可以更进一步的使用这种方式,来比较两个英文字符串是否存在相同字符。

字符串:hello vs world

              hello -  00000000100100010010000 - 18576

              world - 10000100100100000001000 - 4343816

解释:直观可以看到有 2 处的 1 处在同位,意味着存在 2 种冲突的字符

代码:

    public boolean conflict(String s1, String s2) {
        int a = charToNum(s1), b = charToNum(s2);
        return (a & b) != 0;
    }

 这里我们使用按位与(&)可以巧妙的解决两个数字的相同二进制位是否存在都为 1 的情况。

  • 求得两个字符中存在几种冲突的字符

知道了是否冲突,如果更进一步需要知道存在几种冲突,我们可以考虑枚举二进制串中存在几个 1 即可。这里需要思考:使用哪些二进制运算符,以及如果一次性无法计算出某些结果,我们该如何组合使用二进制运算符,并设计一个程序来帮我们求得结果。单个二进制运算符的运算能力有限,因此当我们对二进制运算符掌握的足够牢靠,以及有相当够的经验,很多新的问题也可以尝试去使用二进制运算符解决。就好比当我们只知道加法时,对于 6 个 6 相加,我们需要一个一个的累加求得结果,而不是 6 乘以 6。

代码:

    public int countConflicts(String s1, String s2) {
        int binary = charToNum(s1) & charToNum(s2);
        int mask = 1, cnt = 0;
        // 31 是二进制 int 最大长度
        for (int i = 0; i < 31; ++i) {
            if ((binary & mask) != 0) { ++cnt; }
            mask <<= 1;
        }
        return cnt;
    }
  • 字符串与二进制的一些思考 

当然,int 类型的二进制位最大支持 31 位,更大的可以选择 long 类型的 63 位了。对于字符串转数字,只要计算的维度的取值范围不超过 63,我们都可以采取二进制的方式去进行程序设计。汉字不像英文字符仅有 26 个,但汉字的拼音确是由 26 个英文字母组成,如果你想象力足够丰富,是可以设计出意想不到的程序的。

2、待定

你可能感兴趣的:(算法)