LeetCode 2134. 最少交換次數來組合所有的 1 II(環形陣列+定長滑動視窗圖解)
2134. 最少交換次數來組合所有的 1 II
題目描述
交換定義為選中一個陣列中的兩個互不相同的位置並交換二者的值。
環形陣列是一個陣列,可以認為第一個元素和最後一個元素相鄰。
給你一個二進位環形陣列 nums,回傳在任意位置將陣列中的所有 1 聚集在一起需要的最少交換次數。
範例 1
1 | |
範例 2
1 | |
範例 3
1 | |
限制
1 <= nums.length <= 10^5nums[i]為0或者1
本文題解思路與程式碼整理自 LeetCode 題解作者「灵茶山艾府」,題解連結。圖解與步驟說明為自行補充。
前置知識:定長滑動視窗
定長視窗每次右移一格,少一個最左邊的元素、多一個新元素,視窗內的和可以用「加新的、減舊的」在 O(1) 內更新。前面幾篇已經用過,例如 2841、1423。這題多了一個「環形」,要處理視窗跨過尾巴的情況。
思路
這題的操作是「交換」,看起來很難算。但可以用一個簡單的等價問題取代:最後所有的 1 會擠在一段連續的位置裡,那一段的長度一定是 1 的總個數。
第一步:目標是一個長度為 k 的全 1 視窗
設 nums 裡有 k 個 1。把所有 1 聚在一起,最後就是某一段長度為 k 的連續位置全是 1(環形陣列裡,這一段可以從尾巴繞回開頭)。
所以問題變成:選一段長度為 k 的視窗,把它變成全 1,最少要交換幾次?
第二步:交換次數 = 視窗內 0 的個數
- 視窗內本來就是 1 的位置,不用動。
- 視窗內是 0 的位置,把它和視窗外的某個 1 交換,就能把一個 1 換進視窗。
視窗內 0 的個數,一定等於視窗外 1 的個數,因為全部 1 剛好 k 個、視窗長度也是 k:
視窗內的 1 + 視窗外的 1 = k = 視窗內的 1 + 視窗內的 0
⇒ 視窗外的 1 = 視窗內的 0
所以視窗內的 0 和視窗外的 1 可以一一配對,每交換一對,就多一個 1 進視窗,共需要「視窗內 0 的個數」次,而且沒有更省的做法。
用範例 1 看看。k = 3,每個視窗(含繞回的)內有幾個 0,就要交換幾次:
七個視窗內 0 的個數依序是 2、1、1、1、2、3、2,最小值是 1,就是範例 1 的答案。
於是問題變成:
環形陣列中,長度為
k的視窗裡 0 的個數的最小值。
這是標準的定長滑動視窗。
第三步:處理環形陣列
視窗要從尾巴繞回開頭怎麼辦?把陣列想成攤平的序列,接在後面再複製一段,下標超過 n − 1 就取模 n 映射回原本的位置:
- 第一個視窗是
[0, k − 1],最後一個視窗是[n − 1, n + k − 2]。 [n − 1, n + k − 2]的下一個視窗是[n, n + k − 1],在環形陣列裡這和[0, k − 1]完全一樣,所以不需要再繼續枚舉。- 因此
i只需要從0走到n + k − 2,進入視窗的元素用nums[i % n]。 - 離開視窗的元素
nums[i − k + 1],因為i < n + k − 1,所以i − k + 1 < n,不用取模。
第四步:統計 1 比統計 0 方便
視窗內 0 的個數 = k − 視窗內 1 的個數。與其數 0,不如數 1(直接把元素加起來,因為元素就是 0 或 1),先求視窗內 1 的個數的最大值 max1,最後答案就是 k − max1。
特別情況:陣列裡完全沒有 1(k = 0),不需要交換,直接回傳 0。
圖解(可互動)
用範例 1:nums = [0,1,0,1,1,0,0],k = 3,n = 7。陣列被攤平成長度 n + k − 1 = 9 的序列,虛線格是接在後面複製的那兩格(下標 7、8,對應原本的 0、1)。重點看:
- 前
k − 1 = 2個元素只累加、不更新答案(窗口還沒滿)。 - 窗口
[5, 7]、[6, 8]是跨過尾巴的窗口,用的是nums[i % n]。 max1最後是 2,答案 =3 − 2 = 1。
把過程寫成表格(n = 7,k = 3):
| 窗口 | 內容 | 窗口內 1 的個數 | max1 |
窗口內 0 的個數 |
|---|---|---|---|---|
[0,2] |
0,1,0 | 1 | 1 | 2 |
[1,3] |
1,0,1 | 2 | 2 | 1 |
[2,4] |
0,1,1 | 2 | 2 | 1 |
[3,5] |
1,1,0 | 2 | 2 | 1 |
[4,6] |
1,0,0 | 1 | 2 | 2 |
[5,7] |
0,0,0(nums[7%7]=nums[0]) |
0 | 2 | 3 |
[6,8] |
0,0,1(nums[0]、nums[1]) |
1 | 2 | 2 |
答案 = k − max1 = 3 − 2 = 1。
解法(灵茶山艾府)
1 | |
幾個細節:
i的範圍是0 ~ n + k − 2,這樣剛好窗口起點0 ~ n − 1各一次,不會重複。- 進入視窗要取
i % n,離開視窗不用,因為i − k + 1 < n。 - 整個陣列都是 1(
k = n)時,max1 = k,答案是 0,不需要特別處理。
複雜度分析
- 時間複雜度:O(n),其中 n 是
nums的長度。統計 1 的個數 O(n),滑動視窗最多走n + k − 1 ≤ 2n − 1步。 - 空間複雜度:O(1)。