C 實現漫畫演算法系列 判斷 2 的乘方

2022-02-08 06:01:26 字數 4771 閱讀 5706

下面我來引述一下-判斷 2 的乘方這個演算法。

作為一枚工科生,我們首先想到的是從數學角度來解決這個問題,那麼稍微想一下,就有思路了,數學解法的套路是:

解法1:設定乙個中間變數temp,來存放2的n次方。方法中建立乙個迴圈,在迴圈中將temp與該正整數x比較,若相等則說明x為2的乘方,否則temp數值乘2,在迴圈中繼續與x比較。當temp大於x時,說明在小於x的2的乘方的數中沒有與x相等的,x不為2的乘方,退出迴圈。實現**為:

using

system;

namespace

powerof

", result);

}catch

console.readkey();

}public

static

bool ispoweroftwo(int

number)

temp = temp * 2

; }

return

false

; }

}}

這就是典型的用數學方法來解決問題的方式,這樣做得到的結果絕對是正確的,但是效率並不高,需要多次在迴圈中讓中間變數temp比較並乘2,演算法的時間複雜度為o(logn),空間複雜度為o(1)。

那麼如何用程式設計師的思維、更加高效的解決這個問題呢?我們知道在c#中有一種運算叫做位運算,位運算的特點是:算術左移即運算元乘2,算數右移即運算元除2(快速記憶:左乘右除),按位與0可將運算元按位清0,按位與1得到原運算元,按位或1可將運算元按位置1,按位異或0即得到原運算元,按位異或1即實現原運算元按位取反。還有等等一系列的用處就不一一枚舉了。

這裡我們可以用位運算子來高效的解決有關二進位制的問題。

解法2:實現方法大部分與上述方法一樣,不同的是這裡不需要讓temp乘2,而是在迴圈中讓temp左移一位實現乘2的效果。實現**為:

public

static

bool ispoweroftwo(int

number)

temp = temp << 1

; }

return

false

; }

其實這裡雖然用了位運算,但是解題的思路仍然和第乙個解法是一樣的,本質上講只是換了乙個運算temp的方式而已。時間複雜度和空間複雜度沒有變。

1: 00000001

2: 00000010

4: 00000100

8: 00001000

16: 00010000

18: 00010010

我們可以看到,它們的二進位制數只有一位1。這條規律將會幫助我們實現乙個時間複雜度為o(1)的演算法,那麼知道這個之後還要怎麼辦呢?其實由這條規律我們可以推出,當這些數減1之後:

1-1: 00000000

2-1: 00000001

4-1: 00000011

8-1: 00000111

16-1: 00001111

18-1:00010001

即原數值最高位為0,低位全為1,這個時候我們讓這個正整數和它減1後的結果按位相與,即x&x-1,得到的結果是:

1&1-1: 00000000

2&2-1: 00000000

4&4-1:00000000

8& 8-1: 00000000

16&16-1: 00000000

18&18-1: 00010000

也就是說,在執行完上述操作後,若正整數為2的乘方,結果為0,否則結果非0。

解法3:當x&x-1後的結果為0時,該數為2的乘方。否則若結果不為0,該數不是2的乘方。我們用**描述:

public

static

bool ispoweroftwo(int

number)

時間複雜度為o(1),不需要額外空間。由於計算機中數的形式都為二進位制,用二進位制的方法來解決二進位制問題往往比用十進位制的數學方法要高效。

同題目1一樣,我們首先會想到數學方法解決這道題,talk is cheap ,show you the code !

解法1:設定乙個變數count存放正整數x的二進位制中數字「1」的個數,將x對2取餘數。若餘數為1,則可得到x二進位制的第乙個位為1,count加1;若餘數為0,x二進位制第乙個的個位為0,count不變。然後將x除2,相當於將二進位制數右移一位。在迴圈中重複上述步驟,當x不大於0時結束迴圈。此時count值為x二進位制中1的個數。

using

system;

namespace

powerof

轉換成二進位制後的數字「1」的個數為

", entry,result);

}catch

console.readkey();

}public

static

int counterofone(int

number)

return

count;}}

}

解法2:設定乙個變數count存放正整數x的二進位制中數字「1」的個數,判斷x二進位制最低位是否為「1」,若為1,count加1,否則count不變。將x向右移一位。在迴圈中重複上述步驟,當x不大於0時結束迴圈。此時count值為x二進位制中1的個數。

using

system;

namespace

powerof

轉換成二進位制後的數字「1」的個數為

", entry,result);

}catch

console.readkey();

}public

static

int counterofone(int

number)

return

count;}}

}

時間複雜度為o(logn)。

事實上這段**的思想和上乙個解法的思想是相同的,但是上乙個解法用了數學方式解決,而這種解法用了位運算來解決。在這裡,同樣只有乙個判斷條件,就是迴圈條件。在迴圈中,count不停的加上x同1按位與的值,即加上x二進位制最低位的值。最低位是1就加1,不是1就加0,相當於不變。然後,將x右移一位。在計算機中,運算速度:移位》乘法》除法。因此這段**的執行時間要比上乙個解法還要短。

解法3:

看起來似乎已經是最快的解法了,沒錯,但這僅僅是這個解題思路中的最快解法。我們總結一下,我們從開始看這個問題的開始,就一直用這一種思路:建立迴圈來將二進位制移位,每次迴圈對當前最低位是否為1進行判斷並計數,每次迴圈移一位。那麼如果我們每次迴圈不止移一位呢?我們看上面描述的所有演算法,無論二進位制數中有幾個1,都要一位一位的來判斷。我們只有一次就跳到存有1的位置,才可以讓演算法的效率有質的飛躍。

可是我們每次移多位的話就會漏掉某一位,這樣得到的結果是不正確的。怎麼辦?還記得我們的第乙個題目嗎?判斷乙個數是否為2的乘方,將其二進位制x和x-1按位與。如果是2的乘方,結果為0,那麼如果不是呢?舉個例子:

18:00010010

18-1: 00010001

18&18-1: 00010000=16(十進位制)

與運算後的結果為16,那麼:

16: 00010000

16-1: 00001111

16&16-1:00000000

與運算後的結果為0,我們這時候可以發現,18的二進位制數中有2個1,進行了兩次x&x-1的與運算後為0。

也就是說,每執行一次x&x-1,實際上消除了乙個二進位制數中從最低位開始數的第乙個的1。這樣的話,我們就可以將演算法改為:

using

system;

namespace

powerof

轉換成二進位制後的數字「1」的個數為

", entry,result);

}catch

console.readkey();

}public

static

int counterofone(int

number)

return

count;}}

}

由於我們規定輸入的是正整數,所以一開始就可以++count(如果輸入的是0或負數,那麼結果就不正確了,小心這個bug,看清題目要求)。執行x&x-1,將結果放入x。迴圈條件判斷逗號後邊的表示式,也就是x的值是否為0,若不是0繼續迴圈,否則停止。

這種演算法不需要二進位制數一位一位的挪,可以一步就將低位開始的第乙個1找到,時間複雜度大部分情況下遠小於o(logn)。只有當二進位制數為全1時,時間複雜度為o(logn)。

那麼再說乙個二進位制中老生常談的問題吧。

其實c#裡面有乙個方法直接轉化, 詳見點選這個部落格

但是我用另乙個方法來實現:

using

system;

namespace

powerof

轉換成二進位制後的數字為

", entry,result);

}catch

console.readkey();

}public

static

string dectobin(string

x)

z =convert.tostring(b);

return

z; }

}}

資料結構和演算法系列 FP Tree演算法的實現

在關聯規則挖掘領域最經典的演算法法是apriori,其致命的缺點是需要多次掃瞄事務資料庫。於是人們提出了各種裁剪 prune 資料集的方法以減少i o開支,韓嘉煒老師的fp tree演算法就是其中非常高效的一種。嚴格地說apriori和fp tree都是尋找頻繁項集的演算法,頻繁項集就是所謂的 支援...

白話經典演算法系列一 冒泡演算法的實現 優化

設陣列長度為n。1 比較相鄰的前後二個資料,如果前面資料大於後面的資料,就將二個資料交換。2 這樣對陣列的第0個資料到n 1個資料進行一次遍歷後,最大的乙個資料就 沉 到陣列第n 1個位置。3 n n 1,如果n不為0就重複前面二步,否則排序完成。氣泡排序 公升序排列 target排序陣列 n個數需...

演算法系列 演算法入門之遞迴分而治之思想的實現

別想太多,肯定要!你以為的演算法是各種排序,選擇排序 快速排序 歸併排序,廣深搜尋 動態規劃.然而,演算法實際上指的是解決某個實際問題的方法。解決同乙個問題的方法有很多,比如迴圈輸出某個陣列,可以有for for in for of map foreach等,不同的實現方法會反映不同的效能,這些效能...