🔗 🟢 2965. Find Missing and Repeated Values 1245

tags: Weekly Contest 376 計數(Counter) 雜湊表(Hash Table) 位運算(Bit Manipulation) 數學(Math)

題意

給定一個下標從 00 開始的二維整數矩陣 gridgrid,大小為 n×nn \times n,其中的值皆在 [1,n2][1, n^2] 範圍內。

gridgrid 中除了 aa 出現 兩次bb 缺失 之外,每個整數都 恰好出現一次。以長度為 22 的列表形式返回重複的數字 aa 和缺失的數字 bb ,需按照 [a,b][a, b] 的順序返回。

思路

方法一:計數(Counter) / 雜湊表(Hash Table)

根據題意 grid[i][j]grid[i][j] 的值域為 [1,n2][1, n^2],因此可以使用一個長度為 n2+1n^2+1 的陣列或雜湊表 cntcnt 來計算每個數字出現的次數,最後找出出現兩次的數字和沒有出現的數字即可。

複雜度分析

  • 時間複雜度 O(n2)\mathcal{O}(n^2)
  • 空間複雜度 O(n2)\mathcal{O}(n^2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]:
n = len(grid)
cnt = [0] * (n * n + 1)
for row in grid:
for x in row:
cnt[x] += 1
ans = [-1, -1]
for i in range(1, n * n + 1):
if cnt[i] == 2: # repeated, a
ans[0] = i
elif cnt[i] == 0: # missing, b
ans[1] = i
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<int> findMissingAndRepeatedValues(vector<vector<int>>& grid) {
int n = grid.size();
unordered_map<int, int> cnt;
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
cnt[grid[i][j]]++;
vector<int> ans(2);
for (int i=1; i<=n*n; i++){
if (cnt[i] == 2) ans[0] = i; // repeated, a
else if (cnt[i] == 0) ans[1] = i; // missing, b
}
return ans;
}
};

方法二:位運算(Bit Manipulation)

gridgrid 視為長度為 n2n^2 的一維陣列,若在 gridgrid 中額外添加 [1,n2][1, n^2] 的所有數字,則在 [1,n2][1, n^2] 中重複的數字 aa 會出現三次、缺失的數字 bb 會出現一次,其餘數字皆會出現兩次。

如此便可以將此問題轉換為 260. Single Number III 的問題,使用位運算來找出 aabb。過程可以參考該題的 解題紀錄 ,這裡不再贅述。

但需注意,在該題中返回的順序是任意的,而這題要求返回的順序是 [a,b][a, b],因此在最後需要檢查 aa 是否在 gridgrid 中,若不在則將 aabb 交換。

複雜度分析

  • 時間複雜度 O(n2)\mathcal{O}(n^2)
  • 空間複雜度 O(1)\mathcal{O}(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]:
n = len(grid)
# 異或所有數字
xor = 0 # xor of all numbers
for row in grid:
for x in row:
xor ^= x
for x in range(1, n * n + 1):
xor ^= x
# 分組異或
lb = xor & -xor # 最低位的 1
ans = [0, 0]
for row in grid:
for x in row:
ans[x & lb == 0] ^= x # 分組異或
for x in range(1, n * n + 1):
ans[x & lb == 0] ^= x # 分組異或
return ans if ans[0] in [x for row in grid for x in row] else ans[::-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
vector<int> findMissingAndRepeatedValues(vector<vector<int>>& grid) {
int n = grid.size();
// xor all numbers
int s = 0; // xor of all numbers
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
s ^= grid[i][j];
for (int i=1; i<=n*n; i++)
s ^= i;
// group xor
int lb = s & -s; // lowest bit of 1
vector<int> ans(2);
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
ans[(grid[i][j] & lb) == 0] ^= grid[i][j];
for (int i=1; i<=n*n; i++)
ans[(i & lb) == 0] ^= i;
// check if ans[0] is in grid
for (auto& row : grid)
if (find(row.begin(), row.end(), ans[0]) != row.end())
return ans;
return {ans[1], ans[0]};
}
};

方法三:數學(Math)

m=n2m = n^2gridgrid 中的所有元素和為 s1s_1 ,所有元素的平方和為 s2s_2 。若將 [1,m][1, m] 的所有數字相加,則總和為 i=1n2i=m(m+1)2\sum_{i=1}^{n^2} i = \frac{m(m+1)}{2};將 [1,m][1, m] 的所有數字的平方相加,則總和為 i=1n2i2=m(m+1)(2m+1)6\sum_{i=1}^{n^2} i^2 = \frac{m(m+1)(2m+1)}{6}

由於在 gridgridaa 出現兩次,bb 缺失,因此 s1s1s2s2 會比 m(m+1)2\frac{m(m+1)}{2}m(m+1)(2m+1)6\frac{m(m+1)(2m+1)}{6}aba - ba2b2a^2 - b^2 ,即 ab=s1m(m+1)2a - b = s_1 - \frac{m(m+1)}{2}a2b2=s2m(m+1)(2m+1)6a^2 - b^2 = s_2 - \frac{m(m+1)(2m+1)}{6} ,分別令為 d1d1d2d2

聯立 d1=abd1 = a - bd2=a2b2=(a+b)(ab)d2 = a^2 - b^2 = (a + b)(a - b) ,即可解出 aabb

  • 兩式相除得到 a+b=d2d1a + b = \frac{d2}{d1}
  • d2d1+d1=2aa=d2d1+d12\frac{d2}{d1} + d1 = 2a \Rightarrow a = \frac{\frac{d2}{d1} + d1}{2}
  • d2d1d1=2bb=d2d1d12\frac{d2}{d1} - d1 = 2b \Rightarrow b = \frac{\frac{d2}{d1} - d1}{2}

複雜度分析

  • 時間複雜度 O(n2)\mathcal{O}(n^2),計算 s1s1s2s2 需要遍歷整個 gridgrid
  • 空間複雜度 O(1)\mathcal{O}(1)
1
2
3
4
5
6
7
8
class Solution:
def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]:
n, m = len(grid), len(grid) ** 2
s1 = sum(x for row in grid for x in row) # sum of all numbers
s2 = sum(x * x for row in grid for x in row) # sum of all squares
d1 = s1 - m * (m + 1) // 2 # a - b
d2 = s2 - m * (m + 1) * (m * 2 + 1) // 6 # a^2 - b^2 = (a + b)(a - b)
return [(d2 // d1 + d1) // 2, (d2 // d1 - d1) // 2] # (a, b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using LL = long long;
class Solution {
public:
vector<int> findMissingAndRepeatedValues(vector<vector<int>>& grid) {
int n = grid.size();
LL m = n * n, d1 = 0, d2 = 0;
for (int i=0; i<n; i++)
for (int j=0; j<n; j++){
d1 += grid[i][j]; // sum of all numbers
d2 += grid[i][j] * grid[i][j]; // sum of all squares
}
d1 -= m * (m + 1) / 2; // a - b
d2 -= m * (m + 1) * (m * 2 + 1) / 6; // a^2 - b^2 = (a + b)(a - b)
return {(int) (d2 / d1 + d1) / 2, (int) (d2 / d1 - d1) / 2}; // (a, b)
}
};

參考資料


寫在最後

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

計數(Counter) 或 雜湊表(Hash Table) 的作法是最直覺的,這也是這題只有 1245 分的原因;但 位運算(Bit Manipulation) 和 數學(Math) 解法應該都有 Medium 的難度,因此在通過之後,還是可以去題解區看看其他人的解法,會大有收穫。