---
# System prepended metadata

title: 2025 年「資訊科技產業專案設計」課程作業 2

---

# 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: : 你這方法有符合需求又有效率，感覺可以順利解決目前遇到這問題。
那今天面試也到一個段落，感謝你的參與，有甚麼後續消息，會再通知你的，再見。