以下代码将检查字符串中是否有任何重复的字符,但我不理解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;声明也是如此。
答案 0 :(得分:16)
我也正在阅读这本书Cracking the Code Interview并最终搜索了一个明确的解释。最后我理解了这个概念。
这是方法。
注意:
我们将在下面的代码中假设字符串只是小写'a'到'z'。这将允许我们只使用一个int。
Java 整数大小 32
小写字母的数量 26
因此我们可以清楚地在一个整数内设置0/1(真或假)值 十进制表示法。
类似于 bool访问[32] 。 bool 使用1个字节。因此,您需要32个字节来存储已访问的bool [32]。
位屏蔽是对此的空间优化。
让我们开始:
int val = str.charAt(i) - 'a';
对于'b',它是1 。即66-65。
(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;
(此部分称为位屏蔽。)
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`
我们完成了设置。现在我们做两件事:
首先假设所有字符都不同。对于我们遇到的每个char,我们希望我们的数据结构,即检查器为该char设置为1。因此,我们使用上面描述的OR属性在我们的数据结构中生成1,因此我们执行:
checker = checker | (1 << val);
因此,检查器会为我们遇到的每个角色存储1个。
现在我们来到角色可以重复的部分。因此,在我们执行第1步之前,我们要确保检查器在与当前字符对应的位置处已经没有1。所以我们检查
的值checker & (1 << val)
所以在上面描述的AND属性的帮助下,如果我们从这个操作得到1,那么checker在那个位置已经有1,这意味着我们之前必须遇到过这个字符。所以我们立即回复假。
就是这样。如果我们所有的&amp;检查返回0,我们最终返回true,这意味着没有字符重复。
答案 2 :(得分:2)
1 << val
与2 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
表示剩下1
个val
位移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个位置,其中相应的字符已经出现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
,则保留它。
执行操作(在这种情况下按位OR)并将值赋给左手变量。这适用于许多运营商,例如: -
a += b
,将a
添加到b
并将新值保存在a
中。 a *= b
,将a
乘以b
并将新值保存在a
中。 答案 7 :(得分:0)
按位移动的工作原理如下:
示例:a = 15(位表示:0000 1111)
操作:a<<2
它将在左方向旋转位表示2个位置。
所以a<<2
是0011 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。将结果分配给检查器。