# 2025 年「[資訊科技產業專案設計](https://hackmd.io/@sysprog/info2025)」課程作業 2 >Partner >鱸梭-Matthew >:fire:: Interviewer >Me >包拯-Moon >:ice_cube: : Interviewee ## [191. Number of 1 Bits](https://leetcode.com/problems/number-of-1-bits/description/?envType=problem-list-v2&envId=oizxjoit) >[影片連結](https://youtu.be/SSSOuE8ylDQ) :fire: : 你好,我是你今天的面試官,我姓鱸。 那麼今天的題目給你一個32-bit的unsigned integer。 你要寫出一個function,可以回傳它的二進位表示中有多少個1。 :ice_cube: : 好,那我想先確認一下我對題目的理解是正確的。 我現在輸入一個32-bit的unsigned integer n,然後我需要寫出一個函式去回傳它在二進位表示中有幾個1。 那舉例來說如果n等於17,那麼它的二進位就是10001,所以輸出就會是2,因為有兩個1。 請問我有理解正確嗎? :fire: : 沒錯,你要分享一下你要怎麼解這題嗎? :ice_cube: : 好的,那我想到最直覺的方法就是利用bitwise operation,每次檢查最低位元是不是1。 先將輸入n和1做and operation,再利用一個初始化為0的計數器count,來計算有幾個1,如果and operation的結果是1,代表當前的最低位元是1,就讓count加一,接著將n右移一個bit,重複上面的動作直到n變成0為止。 :fire: : Ok. 這個方法確實很好理解。那麼請你用程式來實現這個Solution。 :ice_cube: : 那首先我就是定義一個function called countones,他的輸入是一個整數n,再來是前面提到用來計算有幾個1的計數器,我將它命名為count然後初始化為0。 接下來我會用到一個while迴圈,迴圈內的動作會持續執行,直到n變成0才結束。 那麼迴圈內在做的事情就是依序檢查最右邊的bit,是1的話count就會加1,是0,count就加0等於不會變,接著將n往右移動一個bit。 當迴圈結束後回傳count就是代表輸入的整數n的二進位表示有幾個1,這樣就完成了這個題目。 ```c int countones(int n) { int count = 0; while(n != 0){ count += n & 1; n >>= 1; } return count; } ``` :fire: : 看起來你的方法是正確的沒錯。 但我發現他有個問題是,如果這一串bit的1幾乎都出現在高位元,這樣最糟的情況要跑32次迴圈,效率太差了。 你覺得還有甚麼方法可以讓他更快? :ice_cube: : 對這確實是個很嚴重的問題。 如果有很多0的話前面就都是在做無效的檢查。 所以意思是應該要跳過這些0,直接把最右邊的1消除,就可以少做幾次迴圈。 我思考一下,要怎麼把1消除。 :fire: : 好,沒關係。因為我們今天面試時間比較緊迫,如果你一時想不到也不要緊。 :ice_cube: : 嗯不好意思,我想再確認一下我的想法。 假設n是24,換成二進位是11000,那我如果要把最右邊的1消除,我將那個1和0做and應該就可以消除。 喔我想到了,我把n減1之後會得到10111,再去和原本的n做and operation,這樣就可以把右邊的1消除,不用去檢查每個bit,反而是去計算我消除了幾次1,就可以得到題目要求的結果了。 :fire: : 聽起來是個非常不錯的方法。我們只剩下幾分鐘的時間,請你快速的用程式實現,然後告訴我他的時間複雜度和空間複雜度。 :ice_cube: : 好的,這個方法只需要改動while迴圈裡面的部份,迴圈裡第一行我們要將n和n-1做and operation,然後賦值給n。 這樣n最右邊的1就會被消除,然後每消除一個1,count就加1,直到n的1全被消除,變成0跳出迴圈然後回傳count的值,就完成了這個方法。 而且他時間複雜度是根據二進位表示中1的數量,所以當1很少的時候,雖然兩個方法的時間複雜度都是O(1),但就會比第一個方法快。 那空間複雜度的部分就和前一個方法一樣是O(1)。 ```c int countones(int n) { int count = 0; while(n != 0){ n = n & (n - 1); count++; } return count; } ``` :fire: : 很好,這個方法有達到我想看到的結果。 那麼恭喜你完成了今天的面試,我們會根據今天的整體表現進行評估,之後會再透過mail通知你結果,祝你後續一切順利 ! --- >Me >包拯-Moon >:hammer: : Interviewer >Partner >鱸梭-Matthew >:fish:: Interviewee ## [283. Move Zeroes](https://leetcode.com/problems/move-zeroes/description/) >[影片連結](https://youtu.be/bw29pLj9Wc0) :hammer: : 你好,我是北宋公司的包拯,今天就由我來面試你,剛好一直有一個問題,擺在旁邊都沒去解決。 我們公司倉儲的貨物箱狀態會用一個數字表示,如果是空箱,就顯示 0;如果有貨,就顯示箱子編號,但是他是都是穿插的,倉儲人員很難一眼看出哪些箱子有貨、哪些是空的,有沒有一個方法讓她空箱都排去後面,還有就是不能改變貨物原本的順序,因為他們的排列代表實際出貨順序,打亂會搞錯發貨順序,然後因為我們倉儲光是存這些箱子的狀態就很吃緊了,所以你要直接對那個貨物箱狀態最修改。 :fish: : 我先確認一下你現在的問題,就是要在不改變箱子編號的順序下,把空箱 也就是顯示的 0 移到最後,然後不能使用額外的空間去複製目前的狀態。 如果現在 倉儲箱子狀態是: [0,1,0,3,12] 那希望能顯示成 Output: [1,3,12,0,0] 或者現在是 Input: [0,0,1,3,12] 希望能顯示成 Output: [1,3,12,0,0] 或者是 Input: [1,3,12,0,0] 就要顯示成 Output: [1,3,12,0,0] 那如果現在只有空箱 Input: [0] 就顯示 Output: [0] 最後如果都沒有空箱 Input: [1,2,4,12,5,8] 就 Output: [1,2,4,12,5,8] 這樣理解應該沒有錯吧? :hammer: : 沒錯,我就是想要這樣 :fish: : 那箱子編號有範圍嗎?還有箱子大概有幾個? :hammer: : 編號從(-2)的31次方到 2 的 31次方,然後箱子最多不超過10000個 :fish: : 那這個問題最直覺的想法就是我要把 0 跟他之後遇到的第一個 非 0 做交換,這樣就可以再不改變非 0 數字的順序下把 0 往後丟。 例如輸入 [0, -1, 0, 3, 12]: 一開始第一個是 0,我就往後找,找到 -1,交換 → [-1, 0, 0, 3, 12] 接著再繼續掃,遇到第二個 0,往後找 3,交換 → [-1, 3, 0, 0, 12] 最後遇到第三個 0,往後找 12,交換 → [-1, 3, 12, 0, 0] 結果就完成了。 但是這樣其實沒什麼效率,他的時間複雜度會是O(n^2) :hammer: : 沒關係,你先把它實做出來,再看看如何。 :fish: : 那我會使用兩個for迴圈,第一個for去跑每個input數字,也就是0 -1 0 3 12, 然後第二個for迴圈就是去檢查第一個for當下後面的數字,如果第一個for是 0 就是 -1 0 3 12。 但只有當我找到 0 的時候才要跑,然後當我現在找到一個 非0 的箱子才要換,換完就可以直接找下一個箱子。 對,這樣就完成我剛剛說的方法了。 ```cpp void moveZeroes(int* nums, int numsSize) { for (int i=0; i < numsSize; i++) { if (nums[i] == 0) { for ( int x = i + 1; x < numsSize; x++) { if ( nums[x] != 0) { int temp = nums[i]; nums[i] = nums[x]; nums[x] = temp; break; } } } } } ``` :hammer: : 看起來沒有錯誤,但是就像你說的,這效率太慢了。時間就是金錢,如果我貨有1000個,你光排好就不知道多久了,有沒有更快的方式? :fish: : 有,剛有想到,這其實是一個two pointer的問題。 就是說,我只要紀錄目前有貨物箱子要放在哪,跟我目前要處理的箱子是哪個,就可以完成這件事。 當我在掃描整個陣列時, 如果遇到的是有貨的箱子, 就把它放到「應該放的位置」上,然後更新下一次有貨箱放哪。 如果遇到的是空箱(0),那就先不理它,繼續往後掃。 例如輸入 [0, -1, 0, 3, 12]: 一開始第 1 個是 0,不動。 掃到 -1,把它跟前面的 0 交換,變成 [-1, 0, 0, 3, 12]。 繼續往後掃,遇到第二個 0 不動。 掃到 3,跟前面的 0 交換,變成 [-1, 3, 0, 0, 12]。 掃到 12,跟前面的 0 交換,變成 [-1, 3, 12, 0, 0]。 這樣就成功了 :hammer: : 感覺會比你原本的方法快很多,那你會如何實作? :fish: : 首先我需要一個變數去紀錄我下次有貨物的箱子要放在哪 然後用一個for迴圈去檢查每個箱子要排在哪 當我檢查到有貨物的箱子,我就把箱子所有東西跟號碼都換到新位置 舊的箱子就會變成空箱子 然後更新下一次有貨箱要去的位置。 這樣就實作完成。 那這樣他的時間複雜度就會下降為O(n) ```cpp void moveZeroes(int* nums, int numsSize) { int j = 0; for (int i = 0; i < numsSize; i++) { if (nums[i] != 0) { if (i != j) { nums[j] = nums[i]; nums[i] = 0; } j++; } } } ``` 另外,如果是「全部都是空箱」或「完全沒有空箱」的情況, 也會正確處理: Input: [0] 只有一個空箱,不動 Output: [0] 或者 Input: [1, 2, 4, 12, 5, 8] 箱子1要去第一個位置 [1,2,4,12,5,8], 更新下一次箱子要去的位置,也就是排在下一個 現在換箱子2,箱子2要去第二位置,再更新下一次箱子要去的位置 依此類推,就像拿起來再放下去。 最後就會是 Output: [1, 2, 4, 12, 5, 8] :hammer: : 你這方法有符合需求又有效率,感覺可以順利解決目前遇到這問題。 那今天面試也到一個段落,感謝你的參與,有甚麼後續消息,會再通知你的,再見。