?拿到二進(jìn)制的每一位,看它是否等于
1
1
1,再定義一個計數(shù)器變量,如果等于
1
1
1,計數(shù)器變量就加
1
1
1。最終計數(shù)器的值就是
1
1
1 的個數(shù)。
?現(xiàn)在的問題就變成了—— 如何得到二進(jìn)制的每一位? 以十進(jìn)制數(shù)字
123
123
123 為例,通過123%10=3
就能得到
3
3
3,不難發(fā)現(xiàn):只要用一個數(shù)除以它的進(jìn)制數(shù),最終的余數(shù)就是這個數(shù)最低位上的數(shù)字,因此如果要得到
2
2
2 首先要讓
2
2
2 來到最低位,只要去掉當(dāng)前最低位上的
3
3
3 ,
2
2
2 就能來到最低位上。如何去掉最低位上的數(shù)字呢? 通過123/10=13
就能把最低位上的
3
3
3 去掉??梢姡褐灰靡粋€數(shù)除以它的進(jìn)制數(shù),就能去掉該數(shù)當(dāng)前的最后一位數(shù)字。(這里指的是整數(shù)除法)因此我們只要不斷地重復(fù)上面的兩個步驟,就能都得到一個數(shù)上的每一位數(shù)字。二進(jìn)制數(shù)也同理。思路整理的差不多接下來就該通過代碼來實現(xiàn)了。
代碼實現(xiàn):
int NumberOf1(int a)
{int count = 0;//計數(shù)器
while (a)
{if (a % 2 == 1)//通過取模得到最低位上的數(shù)字
{ count++;
}
a /= 2;//通過整數(shù)除法取出掉最低位上的數(shù)字
}
return count;
}
int main()
{int a = 0;
scanf("%d", &a);
int num = NumberOf1(a);
printf("%d\n", num);
return 0;
}
上述代碼缺陷:
?經(jīng)過測試發(fā)現(xiàn),上述代碼可以準(zhǔn)確計算出一個正整數(shù)二進(jìn)制位中
1
1
1 的個數(shù),當(dāng)參數(shù)是負(fù)數(shù)時,計算出來的結(jié)果,與我們希望的結(jié)果會有很大的差距。以
?
1
-1
?1 為例,理論上
?
1
-1
?1 的補碼是32個全
1
1
1 ,因此計算出來的結(jié)果應(yīng)該是32才對,但上面這段代碼計算出的結(jié)果是
0
0
0 ,為什么呢?結(jié)果是
0
0
0 說明if (-1 % 2 == 1)
沒成立過,我們可以通過內(nèi)存窗口來看看-1 % 2
的結(jié)果到底是什么,
?結(jié)果是ff ff ff ff
。內(nèi)存中存的是補碼,所以對應(yīng)的二進(jìn)制補碼就是
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111,int b
說明b
是一個有符號的的整型,因此前面二進(jìn)制的最高位是符號位,并且是
1
1
1 ,說明-1%2
的結(jié)果是一個負(fù)數(shù),自然就不可能等于
1
1
1 了。我們發(fā)現(xiàn):只要是一個負(fù)數(shù),對
2
2
2 取模得到的結(jié)果就一定是一個負(fù)數(shù),就永遠(yuǎn)也不可能等于
1
1
1,如何解決這個問題呢?
?這里我們可以把形參設(shè)置成一個無符號的整型變量,此時問題就迎刃而解了。還是以
?
1
-1
?1 為例:
?
1
-1
?1 在內(nèi)存中的補碼是
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111 傳參的時候用一個無符號的整型變量a
去接收 ,在這個無符號的整型變量a
的眼里
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111 就不再是什么
?
1
-1
?1 的補碼了,a
認(rèn)為這就是一串普普通通的二進(jìn)制代碼,沒有什么所謂的符號位這些亂七八糟的東西。因此再用a%2
得到的結(jié)果就不再可能是負(fù)數(shù)了,我們還是可以通過調(diào)試來一探究竟。
?上圖中,我們把
?
1
-1
?1 賦值給一個無符號的的整型變量a
,然后用a%2
得到的結(jié)果是
1
1
1 ,并且也是無符號整型;用a/2
得到
2147483647
2147483647
2147483647 ,這個很容易理解,因為
?
1
-1
?1 的補碼
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111 在a
的眼里就是一個平平無奇的二進(jìn)制序列,沒有符號位,這個二進(jìn)制序列轉(zhuǎn)換成十進(jìn)制就是:
4
,
294
,
967
,
295
4,294,967,295
4,294,967,295,除以
2
2
2 就得到
2147483647
2147483647
2147483647
代碼修改:
int NumberOf1(unsigned int a)//把形參改成無符號整型,就對負(fù)數(shù)也適用了
{int count = 0;
while (a)
{if (a % 2 == 1)
{ count++;
}
a /= 2;
}
return count;
}
int main()
{int a = 0;
scanf("%d", &a);
int num = NumberOf1(a);
printf("%d\n", num);
return 0;
}
?上面的代碼,在傳 ? 1 -1 ?1 的時候,計算出來的結(jié)果就是我們所期待的 32 32 32 了。到這里,思路一就結(jié)束了。接下來看另一種思路。
思路二:?思路二的大體方向和思路一 一樣,得到二進(jìn)制序列的每一位,然后看其是否等于
1
1
1 。只不過思路二的實現(xiàn)方法與思路一不同,會比思路一容易一些,都是初學(xué)者不容易想出來。接下來就來整理一下思路二吧,首先我們還是希望得到二進(jìn)制的每一位,此時我們可以借助按位與(&
)這個操作符來實現(xiàn),還記得按位與的計算過程嘛?對應(yīng)的兩個二進(jìn)制位都為
1
1
1 的時候出
1
1
1 ,其他全部出
0
0
0 ,因此,可以讓待求得二進(jìn)制序列按位與上
1
1
1 ,
1
1
1 的二進(jìn)制序列為
00000000000000000000000000000001
00000000000000000000000000000001
00000000000000000000000000000001 ,此時待求的二進(jìn)制序列的最低位如果是
1
1
1 ,那1&1
得到的結(jié)果就是
1
1
1 ,如果待求的二進(jìn)制序列的最低位是
0
0
0 ,那0&1
得到的結(jié)果就是
0
0
0,我們發(fā)現(xiàn)任何一個二進(jìn)制只要與上
1
1
1 都會得到它自身。此時我們得到了二進(jìn)制序列的最低位,那它的第二位、第三位、第四位……呢?別忘了,我們還有一個移位操作符,我們可以利用移位操作符,讓二進(jìn)制序列往右移動,使得每一個二進(jìn)制位都能與上
1
1
1 ,這樣我們就能拿到二進(jìn)制序列的每一位了。大體思路捋的差不多了,接下來該上代碼了。
int NumberOf1(int a)
{int i = 0;
int count = 0;
for (i = 0; i< 32; i++)//最多只需右移31個二進(jìn)制位
{if (((a >>i) & 1) == 1)//只有當(dāng)1 & 1結(jié)果才是1,讓a的二進(jìn)制位一直右移
{ count++;
}
}
return count;
}
int main()
{int a = 0;
scanf("%d", &a);
int num = NumberOf1(a);
printf("%d\n", num);
return 0;
}
?思路二也無需考慮什么正負(fù)數(shù),因為移位操作符和位操作符都是直接針對二進(jìn)制位來計算的,看完思路二我就只想說妙。但是別急,接下來的思路三才算得上是“王炸”
思路三:?這里直接上方法,記住下面這個式子:n=n&(n-1)
。舉個例子,以十進(jìn)制的
11
11
11 為例,
11
11
11 的二進(jìn)制是
1011
1011
1011 ,此時n=1011;
,n-1=1010;
,n&(n-1)=1010
此時n
就變成了:
1010
1010
1010 ,對比前面的n
我們發(fā)現(xiàn)二進(jìn)制序列最右邊的
1
1
1 變成了
0
0
0 ,接著往下,n=1010;
,n-1=1001;
,n&(n-1)=1000
此時n
就變成了
1000
1000
1000 對比第二個n
二進(jìn)制序列最右邊的
1
1
1 又變成了
0
0
0,再往下看,n=1000;
,n-1=0111;
,n&(n-1)=0000
此時n
就變成了
0000
0000
0000 。不難發(fā)現(xiàn):n
從最初的
1011
1011
1011 ,變成了現(xiàn)在的
0000
0000
0000 是把n=n&(n-1)
這個式子執(zhí)行了
3
3
3 次,什么?3??這不就是
1011
1011
1011 這個二進(jìn)制序列里面
1
1
1 的個數(shù)嘛,其實這并非偶然,因為n=n&(n-1)
這個式子每執(zhí)行一次,就會把當(dāng)前n
這個二進(jìn)制序列里面最右邊的
1
1
1 去除掉,直到整個二進(jìn)制序列變成
0
0
0 ,分析的差不多了,接下來上代碼!
int NumberOf1(int n)
{int count = 0;
while (n)//能變成全0的時候,就不用再執(zhí)行下面的式子了
{n = n & (n - 1);
count++;//記錄上面這個式子一共執(zhí)行了多少次
}
return count;
}
int main()
{int n = 0;
scanf("%d", &n);
int num = NumberOf1(n);
printf("%d\n", num);
return 0;
}
?哈哈,沒騙你吧,思路三才是最終的“王炸”
?好了,這道題的分享就到這里啦,如果你有更好的思路或者方法,歡迎在評論區(qū)或者私信,給我留言,拜拜咯!
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧