![](/img/trans.png)
[英]Java - Trying to understand Threading and new Thread(this).start();
[英]new to java - trying to understand: checker |= (1 << val)
以下代碼將檢查字符串中是否有任何重復字符,但我不明白 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;
}
我試圖查找一些參考資料,我是位移位的新手,我所了解的是 << 向左或向右移動二進制數。 你能向我解釋檢查器 |= (1 << val) 是如何工作的嗎? 還有那個“if”語句。
我也在閱讀《 破解代碼面試》這本書,最終在谷歌上搜索了清晰的解釋。 終於明白了這個概念。
這是方法。
筆記 :
我們將假設,在下面的代碼中,字符串只是小寫的“a”到“z”。 這將允許我們只使用一個 int。
Java整數的大小為32
小寫字母的數量是26
所以我們可以清楚地在一個整數內以十進制表示法設置 0/1(真或假)值。
它類似於boolvisited[32] 。 bool使用 1 個字節。 因此,您需要 32 個字節來存儲 bool 訪問 [32]。
位掩碼是對此的空間優化。
開始吧 :
int val = str.charAt(i) - 'a';
對於'b' 它是 1 。 即 98-97 。
(1 << val) // 1<<1 => 10(binary)
現在讓我們看看按位&是如何工作的
0 & 0 -> 0
0 & 1 -> 0
1 & 0 -> 0
1 & 1 -> 1
所以通過下面的代碼:
(checker & (1 << val))
我們檢查 checker[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);
我希望這對某人有所幫助!
好像我參加聚會遲到了,但讓我來解釋一下。
首先是 AND 即 & 操作:
0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1
所以基本上,如果你得到一點,並且你想知道它是 1 還是 0,你只需將它與 1 相加。如果結果是 1,那么你有一個 1,否則你有 0。我們將使用這個& 下面的屬性。
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
每個字符left shift
1,即 (1 << val),因此每個字符都有一個1 位和 31 個零位:
a = 1 i.e. `0000000000000000000001`
b = 2 i.e. `0000000000000000000010`
c = 4 i.e. `0000000000000000000100`
d = 8 i.e. `0000000000000000001000`
我們完成了設置。 現在我們做兩件事:
首先假設所有字符都不同。 對於我們遇到的每個字符,我們希望我們的數據結構即檢查器為該字符設置 1。 所以我們使用上面描述的 OR 屬性在我們的數據結構中生成一個 1,因此我們這樣做:
checker = checker | (1 << val);
因此,檢查器為我們遇到的每個字符存儲 1。
現在我們來到字符可以重復的部分。 所以在我們做第 1 步之前,我們要確保檢查器在當前字符對應的位置已經沒有 1。 所以我們檢查的值
checker & (1 << val)
所以借助上面描述的 AND 屬性,如果我們從這個操作中得到一個 1,那么 checker 在那個位置已經有一個 1,這意味着我們之前一定遇到過這個字符。 所以我們立即返回false。
就是這樣。 如果我們所有的 & 檢查返回 0,我們最終返回 true,這意味着沒有字符重復。
1 << val
與2 to the degree of val
相同。 所以這是一個數字
它的二進制表示中只有一個一(一個在位置val+1
,如果你從
數字的右邊到左邊一個)。
a |= b
基本上意味着:在a
設置所有二進制標志/來自
的二進制表示b
(並保持在那些a
其中已經設置)。
這會將右側的第 'val' 位設置為 1。
1 << val
是 1 左移val
次。 其余值為 0。
該行相當於checker = checker | (1 << val)
checker = checker | (1 << val)
。 0 位的或運算什么都不做,因為x | 0 == x
x | 0 == x
。 但是 1 的 or-ing 總是導致 1。所以這會(僅)打開那個位。
if
語句類似,因為它正在檢查該位是否已經打開。 掩碼值1 << val
除了單個 1 外都是 0。與 0 總是產生 0,因此結果中的大多數位都是 0。x & 1 == x,因此只有在以下情況下它才會為非零val
那個位不是 0。
checker |= (1 << val)
與checker = checker | (1 << val)
相同 checker = checker | (1 << val)
。 <<
是你說的左移。 1 << val
表示它是左移1
val
數字。 示例: 1 << 4
是 1000。左移與乘以 2 相同。4 次左移是 4 乘以 1 乘以 2。
1 * 2 = 2 (1)
2 * 2 = 4 (2)
4 * 2 = 8 (3)
8 * 2 = 16 = (4)
|
運算符是按位或。 這就像正常或一點點。 如果我們有多個位,則對每一位執行 or 操作。 例子:
110 | 011 = 111
您可以使用它來設置標志(使位 1)。
if 條件與此類似,但具有&
運算符,它是按位與。 它主要用於屏蔽二進制數。
例子:
110 | 100 = 100
因此,您的代碼僅檢查val
位置的位是否為 1,然后return false
,否則將val
位置的位設置為 1。
其他答案解釋了編碼運算符的用法,但我認為它們沒有觸及此代碼背后的邏輯。
基本上,代碼1 << val
將二進制數中的 1 移到每個字符的唯一位置,例如
a-0001
b-0010
c-0100
d-1000
正如您所注意到的,對於不同的字符,1 的位置是不同的
checker = checker | (1 << val)
這里的檢查器是 Oring(基本上將 1 存儲在與1<<val
相同的位置)所以檢查器知道已經出現了哪些字符讓我們說在出現 a,b,c,d 后checker
看起來像這樣0000 1111
最后
if ((checker & (1 << val)) > 0)
檢查該字符之前是否已經出現過,如果是,則返回false。解釋一下,您應該對AND(&) 操作有所了解。
因此,檢查器當前在相應字符已經出現的地方有 1,如果一個字符出現兩次導致 1&1->1 > 0,則 if 語句中的表達式為真
按位移位的工作原理如下:
示例: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 已經存在於1<<val
位置,則總是大於 0。
因此我們可以返回false
。
否則,我們將在 1 處分配檢查器值 1
我一直在研究算法,這就是我注意到的也可以工作。 當您手動練習時,它使算法更容易理解:
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) 的值時,我們可以將 1 右移val
的值,或者我們可以使用 2 的冪。
此外,只要((checker & newValue) > 0)
為假,當我們將舊的檢查器值和newValue
相加時,就會生成新的檢查器值。
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。az 是 97-122 = 25。如果我們乘以 1*(2)^25 = 33554432。如果檢查器的第 26 位語句為 1,則二進制為 100000000000000000000000000000000000 1 << val)) > 0) 為真,isUniqueChar 將返回假。 否則檢查器會打開它的第 26 位。 |= 運算符(按位或和賦值運算符)執行檢查器按位 OR 10000000000000000000000000。將結果分配給檢查器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.