java的新手 - 试图理解:checker | =(1<< val)

时间:2014-09-15 11:36:54

标签: java

以下代码将检查字符串中是否有任何重复的字符,但我不理解if子句:

public static boolean isUniqueChars(String str) {
        int checker = 0;
        for (int i = 0; i < str.length(); ++i) {
            int val = str.charAt(i) - 'a';
            if ((checker & (1 << val)) > 0) 
                return false;
            checker |= (1 << val);
        }
        return true;
    }

我试图查找一些参考文献,我是新移位的,我所理解的是&lt;&lt;左移或右移二进制数。你能解释一下checker | =(1&lt;&lt; val)是如何工作的吗?那个&#39;如果&#39;声明也是如此。

10 个答案:

答案 0 :(得分:16)

我也正在阅读这本书Cracking the Code Interview并最终搜索了一个明确的解释。最后我理解了这个概念。

这是方法。

注意:

  1. 我们将在下面的代码中假设字符串只是小写'a'到'z'。这将允许我们只使用一个int。

  2. Java 整数大小 32

  3. 小写字母的数量 26

  4.   

    因此我们可以清楚地在一个整数内设置0/1(真或假)值   十进制表示法。

    1. 类似于 bool访问[32] bool 使用1个字节。因此,您需要32个字节来存储已访问的bool [32]。

    2. 位屏蔽是对此的空间优化。

    3. 让我们开始:

      1. 您正在遍历字符串中的所有字符。
      2. 假设在第i次迭代时你发现了字符'b'。 您计算其 0基于索引
      3. int val = str.charAt(i) - 'a';

        对于'b',它是1 。即66-65。

        1. 现在使用左移运算符,我们找到值2 ^ 1 =&gt; 2。
        2. (1 << val) // 1<<1 => 10(binary)

          现在让我们看看按位&amp; 的工作方式

          0 & 0 -> 0
          0 & 1 -> 0
          1 & 0 -> 0
          1 & 1 -> 1
          

          所以通过以下代码:

          (checker & (1 << val))
          

          我们检查检查器[val] == 0。 假设我们已经遇到'b'。

          check = 0000 0000 0000 0000 0000 1000 1000 0010   &  
          'b'   = 0000 0000 0000 0000 0000 0000 0000 0010 
           ----------------------------------------------
          result= 0000 0000 0000 0000 0000 0000 0000 0010
          

          即十进制值= 2,其> 0

          所以你最后我们理解了这一部分。

           if ((checker & (1 << val)) > 0) 
                          return false;
          
          1. 现在如果没有遇到'b',那么我们使用按位OR设置第二位检查器
          2. (此部分称为位屏蔽。)

            OR的真相表

            0 | 0 -> 0
            0 | 1 -> 1
            1 | 0 -> 1
            1 | 1 -> 1
            

            所以

            check = 0000 0000 0000 0000 0000 1000 1000 0000   |  
            'b'   = 0000 0000 0000 0000 0000 0000 0000 0010 
             ----------------------------------------------
            result= 0000 0000 0000 0000 0000 1000 1000 0010
            

            这样就简化了这一部分:

            checker |= (1 << val);  // checker = checker |  (1 << val);
            

            我希望这有助于某人!

答案 1 :(得分:4)

好像我迟到了,但让我来解释一下。

首先是AND ie&amp;操作:

0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1

所以基本上,如果给你一点,你想知道它的1或0,你只是&amp;如果结果是1,那么你得到1,否则你得到0.我们将使用&amp;的这个属性。下方。

OR即|操作

0 & 0 = 0
1 & 0 = 1
0 & 1 = 1
1 & 1 = 1  

所以基本上,如果给你一点,并且你想对它做一些事情,以便输出总是1,那么你做一个| 1用它。

现在,在Java中,类型int是4个字节,即32位。因此,我们可以使用int本身作为数据结构以更简单的方式存储32个状态或布尔值,因为一个比特可以是0或1,即假或真。由于我们假设我们的字符串仅由小写字符组成,因此我们在int中有足够的空间来为26个字符中的每一个存储一个布尔值!

首先,我们将我们称为checker的数据结构初始化为0,这只是32个零:0000000000000000000000

到目前为止一直很好?

现在我们通过我们的字符串,对于每个字符,首先我们得到字符的整数表示。

int val = str.charAt(i) - 'a';

我们从中减去a,因为我们希望整数基于0。所以如果vals:

a = 0 i.e. `0000000000000000000000`
b = 1 i.e. `0000000000000000000001`
c = 2 i.e. `0000000000000000000010`
d = 4 i.e. `0000000000000000000100`

现在如上所示,a是32个零,但其余字符有1个和31个零。因此,当我们使用这些字符时,我们left shift每个都为1,即(1 <&lt;&lt; val),因此每个字符都有单个1位,31个零位

a = 1 i.e. `0000000000000000000001`
b = 2 i.e. `0000000000000000000010`
c = 4 i.e. `0000000000000000000100`
d = 8 i.e. `0000000000000000001000`

我们完成了设置。现在我们做两件事:

  1. 首先假设所有字符都不同。对于我们遇到的每个char,我们希望我们的数据结构,即检查器为该char设置为1。因此,我们使用上面描述的OR属性在我们的数据结构中生成1,因此我们执行:

    checker = checker | (1 << val);
    
  2. 因此,检查器会为我们遇到的每个角色存储1个。

    1. 现在我们来到角色可以重复的部分。因此,在我们执行第1步之前,我们要确保检查器在与当前字符对应的位置处已经没有1。所以我们检查

      的值
      checker & (1 << val)
      
    2. 所以在上面描述的AND属性的帮助下,如果我们从这个操作得到1,那么checker在那个位置已经有1,这意味着我们之前必须遇到过这个字符。所以我们立即回复假。

      就是这样。如果我们所有的&amp;检查返回0,我们最终返回true,这意味着没有字符重复。

答案 2 :(得分:2)

1 << val2 to the degree of val相同。所以它是一个有数字的数字 只有一个在其二进制表示中(一个位于val+1位置,如果从中计数 数字的右侧是左侧的数字。)

a |= b基本上就是这样:在a中设置所有二进制标志/来自中的二进制标志 b的二进制表示(并保留a中已设置的那些)。

答案 3 :(得分:1)

这会将右边的第#位设置为1。

1 << val向左移动val次。其余值为0.

这一行相当于checker = checker | (1 << val)。由于x | 0 == x,因此使用0位进行操作无效。但是,或者输入1会导致1.因此,这会转动(仅)该位。

if语句类似,因为它正在检查该位是否已经打开。除了单个1之外,掩码值1 << val都是0。并且0和0总是产生0,因此结果中的大多数位都是0. x&amp; 1 == x,所以只有val的位不为0时才会为非零。

答案 4 :(得分:1)

checker |= (1 << val)checker = checker | (1 << val)相同。 正如你所说的,<<有点移位。 1 << val表示剩下1val位移1 << 4。 示例:1 * 2 = 2 (1) 2 * 2 = 4 (2) 4 * 2 = 8 (3) 8 * 2 = 16 = (4) 为1000.左移位与乘以2相同.4左移位为4乘以1乘以2.

|

110 | 011 = 111 运算符是按位或。它像普通或一点一样。如果我们有多个位,则为每个位执行或操作。 例如:

&

您可以使用它来设置标志(使位1)。

if条件与此类似,但具有110 | 100 = 100 运算符,它是按位和。它主要用于掩盖二进制数。

示例:

val

因此,您的代码只检查位return false的位是1,然后是val,否则将位{{1}}的位设置为1。

答案 5 :(得分:1)

其他答案解释了编码运算符的用法,但我认为他们没有触及此代码背后的逻辑。

基本上,代码1 << val将二进制数中的1转换为每个字符的唯一位置,例如

a-0001 b-0010 c-0100 d-1000

正如您可以注意到不同的角色,1的位置是不同的

checker = checker | (1 << val)

这里的检查是Oring(基本上在1<<val的同一个地方存储1) 因此检查员知道已经出现了什么字符 让我们说a,b,c,d checker出现之后会是这样的 0000 1111

最后

if ((checker & (1 << val)) > 0) 

如果是,则返回false,检查该字符是否已经出现。要解释一下你应该对AND(&amp;)操作有所了解。

  • 1安培; 1→1
  • 0安培; 0→0
  • 1安培; 0→0

因此检查器当前有1个位置,其中相应的字符已经出现if if语句中的表达式为true的唯一方式 如果一个字符出现两次,则导致1&amp; 1&gt; 1&gt; 0

答案 6 :(得分:0)

这意味着对值checker(1 << val)(即1,左移val次)执行二进制OR,并将新创建的值保存在checker中。

Left Shift<<

将所有二进制数字移位一个空格。将数字有效地提高到2 val或将数字乘以val次。

Bitwise或(|

在左右两个值的每个二进制字符中,如果两个数字中的任何一个都有1,则保留它。

Augmented Assignment|=

执行操作(在这种情况下按位OR)并将值赋给左手变量。这适用于许多运营商,例如: -

  • a += b,将a添加到b并将新值保存在a中。
  • a *= b,将a乘以b并将新值保存在a中。

答案 7 :(得分:0)

按位移动的工作原理如下:

示例:a = 15(位表示:0000 1111)

操作:a<<2

它将在左方向旋转位表示2个位置。

所以a<<20011 1100 = 0*2^7+0*2^6+1*2^5+1*2^4+1*2^3+1*2^2+0*2^1+0*2^0 = 1*2^5+1*2^4+1*2^3+1*2^2 = 32+18+8+4=60

因此a<<2 = 60

现在:

checker & (1<<val), 如果1<<val位置已经存在1,则总是大于0。

因此我们可以返回false

否则我们将1的检查值分配给1

答案 8 :(得分:0)

我一直在研究这个算法,这里我注意到它也会起作用。当您手动锻炼时,它使算法更容易理解:

public static boolean isUniqueChars(String str) {
    if (str.length() > 26) { // Only 26 characters
        return false;
    }
    int checker = 0;
    for (int i = 0; i < str.length(); i++) {
        int val = str.charAt(i) - 'a';
        int newValue = Math.pow(2, val) // int newValue = 1 << val
        if ((checker & newValue) > 0) return false;
        checker += newValue // checker |= newValue
    }
    return true;

当我们得到val(0-25)的值时,我们可以向右移动val的值,或者我们可以使用2的幂。

此外,只要((checker & newValue) > 0)为false,就会在我们总结旧的检查器值和newValue时生成新的检查器值。

答案 9 :(得分:0)

public static boolean isUniqueChars(String str) {
    int checker = 0;
    for (int i = 0; i < str.length(); ++i) {
        int val = str.charAt(i) - 'a';
        if ((checker & (1 << val)) > 0) 
            return false;
        checker |= (1 << val);
    }
    return true;
}

1 << val 使用右移运算符。假设我们有字符 z。 z 的 ASCII 码是 122。a-z 是 97-122 = 25。如果我们乘以 1*(2)^25 = 33554432。它的二进制是 10000000000000000000000000 如果检查器的第 26 位为 1,则此语句 if ((checker & (1 << val)) > 0) 为真,isUniqueChar 将返回假。 否则检查器会打开它的第 26 位。 |= 运算符(按位或和赋值运算符)执行检查器按位 OR 10000000000000000000000000。将结果分配给检查器。