拜託,面試別再讓我數1了!!!

2021-08-28 12:43:08 字數 2984 閱讀 9472

面試中,除了topk,是否被問過:求乙個正整數的二進位制表示包含多少個1?

畫外音:姊妹篇《拜託,面試別再問我topk了!!!》。

例如:uint32_t i=58585858;

i的二進位制表示

是: 0000 00110

111 11

01 1111

0011

0000 0010

於是,i的二進位制表示包含15個1。

到底有幾種方法,這些思路裡蘊含的優化思路究竟是怎麼樣的,今天和大家聊一聊。

一、位移法

思路:既然輸入n是uint32,每次取n的最低位,判斷是不是1,位移32次,迴圈判斷即可。

偽**

do n>>= 1;

i++;

} while(i<32);

分析:不管n的二進位制表示裡包含多少個1,都需要迴圈

計算32次

,比較耗時。有沒有可能,每次消除掉乙個1,這樣來降低計算次數呢?

二、求與法

觀察一下n與

n-1這兩個數的二進位制表示:

栗子

x =1011 0000

x-1=1010 1111

x & (x-1) =1010 0000

於是,n&(n-1)這個操作,可以起到「

消除最後乙個1

」的功效。

思路:逐步通過n&(n-1),來消除n末尾的1,消除了多少次,就有多少個1。

偽**

while(n)

分析:這個方法,n的二進位制表示

有多少個1,就會計算多少次

。總的來說,n的長度是32bit,如果n的值選取完全隨機,平均期望由16個1構成,

平均下來16次

,節省一半的計算量。

畫外音:校招時,我問過這樣的面試題,「如何快速判斷乙個正整數是不是2的x次冪」,巧妙解法是

return !(n&(n-1));

即,如果n是2的x次冪,二進位制表示只有乙個1。

三、查表法

空間換時間,是

演算法優化中最常見的手段

,如果有相對充裕的記憶體,可以有更快的演算法。

思路:乙個uint32的正整數n,

一旦n的值確定,n的二進位制表示中包含多少個1也就確定了

,理論上無需重新計算:

1的二進位制表示中包含1個1

2的二進位制表示中包含1個1

3的二進位制表示中包含2個1

… 58585858的二進位制表示中包含15個1

...提前計算好結果陣列:

result[1]=1;

result[2]=1;

result[3]=2;

… result[58585858]=15; …

偽**

return result[n];

查表法的好處是,

時間複雜度為o(1)

,潛在的問題是,需要很大的記憶體。

記憶體分析

假如被分析的整數是uint32,打表陣列需要記錄2^32個正整數的結果。

n的二進位制表示最多包含32個1,儲存結果的計數,使用5個bit即可。

故,共需要記憶體2^32 * 5bit = 2.5gb。

四、二次查表法

查表法,非常快,只查詢一次,但消耗記憶體太大,在工程中幾乎不被使用。

演算法設計,本身是乙個時間複雜度與空間複雜度的折衷,增加計算次數,往往能夠減少儲存空間。

思路

(1)把uint32的正整數n,分解為低16位正整數n1,和高16正整數n2;

(2)n1查一次表,其二進位制表示包含a個1;

(3)n2查一次表,其二進位制表示包含b個1;

(4)則,n的二進位制表示包含a+b個1;

偽**

uint16 n1 = n & 0xffff;

uint16 n2 = (n>>16) & 0xffff;

return  result[n1]+result[n2];

問題來了:增加了一倍的計算量(1次查表變2次查表),記憶體空間是不是對應減少一半呢?

記憶體分析

被分析的整數變成uint16,打表陣列需要記錄2^16個正整數的結果。

n1和n2的二進位制表示最多包含16個1,儲存結果的計數,使用4個bit即可。

故,共需要記憶體2^16 * 4bit = 32kb。

畫外音:幫忙看下,算錯了沒有。

好神奇!!!

計算量多了1次(1倍),記憶體佔用量卻由2.5g降到了32k(1萬多倍),是不是很有意思?

五、總結

數1,不難;但其思路有優化過程,並不簡單:

(1)位移法,32次計算;

(2)n&(n-1),能消除乙個1,平均16次計算;

(3)查表法,1次查表,2.5g記憶體;

(4)二次查表法,2次查表,32k記憶體;

知其然,知其所以然

思路比結論重要。

希望大家對「數1」有新的認識,謝轉。

作業題:公升級為4次查表,需要使用多少記憶體呢?

-分享可落地

的架構文章

拜託,面試別再問我跳表了!

跳表是乙個隨機化的資料結構,實質就是一種可以進行二分查詢的有序鍊錶。跳表在原有的有序鍊錶上面增加了多級索引,通過索引來實現快速查詢。跳表不僅能提高搜尋效能,同時也可以提高插入和刪除操作的效能。考慮乙個有序鍊錶,我們要查詢3 7 17這幾個元素,我們只能從頭開始遍歷鍊錶,直到查詢到元素為止。上述這個鍊...

拜託,面試別再問我TopK了!!!

除非校招,我在面試過程中從不問topk這個問題,預設大家都知道。將n個數排序之後,取出最大的k個 即為所得。sort arr,1,n return arr 1,k o n lg n 明明只需要topk,卻將全域性都排序了,這也是這個方法複雜度非常高的原因。那能不能不全域性排序,而只區域性排序呢?這就...

拜託,面試別再問我計數排序了!!!

radix sort 還有計數排序 counting sort 今天,1分鐘,通過幾幅圖,爭取讓大家搞懂計數排序。空間大小為o max min 用來儲存所有元素出現次數 計數 掃瞄待排序資料 arr n 使用計數陣列 counting max min 對每乙個 arr n 現的元素進行計數 掃瞄計數...