🔗 🟢 1122. Relative Sort Array 1189

tags: Weekly Contest 145 陣列(Array) 排序(Sorting) 計數排序(Counting Sort) 雜湊表(Hash Table)

題意

給定兩個整數陣列 arr1arr1arr2arr2,其中 arr2arr2 中的元素是唯一的,且 arr2arr2 中的每個元素都出現在 arr1arr1 中。

arr1arr1 中的元素進行排序,使得 arr1arr1 中的元素相對順序與 arr2arr2 中的相對順序相同。不在 arr2arr2 中的元素需要按升序排列並放在 arr1arr1 的末尾。

約束條件

  • 1arr1.length,arr2.length10001 \leq \text{arr1.length}, \text{arr2.length} \leq 1000
  • 0arr1[i],arr2[i]10000 \leq \text{arr1}[i], \text{arr2}[i] \leq 1000
  • arr2arr2 中的所有元素都是 唯一 的。
  • 每個 arr2[i]arr2[i] 都在 arr1arr1 中。

思路

方法一:自訂排序

由於需要按照特定的順序對 arr1arr1 進行排序,因此我們可以通過 自訂排序 來實現這個需求。

為了按照 arr2arr2 中的相對順序對 arr1arr1 進行排序,因此我們需要建立一個 雜湊表(Hash Table) ,將 arr2arr2 中的每個元素映射到其在 arr2arr2 中的索引。來記錄 arr2arr2 中每個元素的排名。這樣,我們可以很方便地知道 arr1arr1 中的元素在 arr2arr2 中的相對順序。

而在排序時,我們可以根據這個映射來對 arr1arr1 進行排序,具體步驟如下:

  • 在比較 aabb 時,如果 aabb 都在 arr2arr2 中,我們根據它們在 arr2arr2 中的排名來比較。
  • 如果 aabb 都不在 arr2arr2 中,我們按照自然順序來比較。
  • 如果 aaarr2arr2 中而 bb 不在,我們將 aa 放在 bb 前面。

除了上述方式外,也能將不在 arr2arr2 中的元素 xx 的排名視為 1000+x1000 + x,這樣就能夠保證不在 arr2arr2 中的元素會按照升序排列。

複雜度分析

  • 時間複雜度:O(n+mlogm)\mathcal{O}(n + m \log m),其中 nnarr2arr2 的長度,mmarr1arr1 的長度。
    • 建立排名映射需要 O(n)\mathcal{O}(n) 的時間。
    • 排序的時間複雜度是 O(mlogm)\mathcal{O}(m \log m)
  • 空間複雜度:O(n)\mathcal{O}(n),使用了額外的映射來存儲 arr2arr2 的排名。
1
2
3
4
class Solution:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
rank = {x: i for i, x in enumerate(arr2)}
return sorted(arr1, key=lambda x: rank.get(x, 1000 + x))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
unordered_map<int, int> rank;
for (int i = 0; i < arr2.size(); i++) rank[arr2[i]] = i;
sort(arr1.begin(), arr1.end(), [&](int x, int y) {
if (rank.count(x)) {
return rank.count(y) ? rank[x] < rank[y] : true;
} else {
return rank.count(y) ? false : x < y;
}
});
return arr1;
}
};

方法二:計數排序(Counting Sort)

注意到約束條件中的數字範圍是 0arr1[i],arr2[i]10000 \leq \text{arr1}[i], \text{arr2}[i] \leq 1000,因此我們可以基於值域,使用 計數排序(Counting Sort) 的思想來解決問題。

首先紀錄 arr1arr1 中每個元素的出現次數,然後根據 arr2arr2 的順序重建陣列,最後將 arr2arr2 中未出現的元素按升序排列加入結果陣列即可。此外,也能透過直接修改 arr1arr1 來達到相同的效果。

具體步驟如下:

  1. 遍歷 arr1arr1,並使用一個長度為 10011001 的計數陣列 $cnt 來計算 arr1arr1 中每個元素的出現次數。
  2. 遍歷 arr2arr2,根據 cntcnt 中的計數,將 arr2arr2 中的元素按照其在 arr2arr2 中的順序加入到結果陣列 ansans 中,並將這些元素的計數歸零。
  3. 最後,對於那些不在 arr2arr2 中但在 arr1arr1 中出現的元素,按照自然數升序的順序,將它們加入結果陣列 ansans 中。

複雜度分析

  • 時間複雜度:O(n+m+k)\mathcal{O}(n + m + k),其中 nnarr1arr1 的長度,mmarr2arr2 的長度,kk 是數字範圍的大小,本題中為 1001。
    • arr1arr1 中的元素進行計數需要 O(n)\mathcal{O}(n) 時間。
    • arr2arr2 中的元素進行處理需要 O(m)\mathcal{O}(m) 時間。
    • 最後處理剩餘元素的步驟需要 O(k)\mathcal{O}(k) 時間。
  • 空間複雜度:O(k)\mathcal{O}(k),需要一個大小為 10011001 的計數陣列 cntcnt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
cnt = [0] * (1001)
for x in arr1:
cnt[x] += 1

ans = []
for x in arr2:
ans.extend([x] * cnt[x])
cnt[x] = 0
for x in range(1001):
if cnt[x] > 0:
ans.extend([x] * cnt[x])
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
vector<int> cnt(1001);
for (int x : arr1) cnt[x]++;
int i = 0;
for (int x : arr2)
while (cnt[x]-- > 0)
arr1[i++] = x;
for (int x = 0; x < 1001; x++)
while (cnt[x]-- > 0)
arr1[i++] = x;
return arr1;
}
};

寫在最後

Cover photo is generated by @たろたろ, thanks for their work!