【LetMeFly】3551.数位和排序需要的最小交换次数:计算连通块个数 力扣题目链接:https://leetcode.cn/problems/minimum-swaps-to-sort-by-digit-sum/
给你一个由 互不相同 的正整数组成的数组 nums,需要根据每个数字的数位和(即每一位数字相加求和)按 升序 对数组进行排序。如果两个数字的数位和相等,则较小的数字排在前面。
返回将 nums 排列为上述排序顺序所需的 最小 交换次数。
一次 交换 定义为交换数组中两个不同位置的值。
示例 1:
输入: nums = [37,100]
输出: 1
解释:
计算每个整数的数位和:[3 + 7 = 10, 1 + 0 + 0 = 1] → [10, 1]
根据数位和排序:[100, 37]。将 37 与 100 交换,得到排序后的数组。
因此,将 nums 排列为排序顺序所需的最小交换次数为 1。
示例 2:
输入: nums = [22,14,33,7]
输出: 0
解释:
计算每个整数的数位和:[2 + 2 = 4, 1 + 4 = 5, 3 + 3 = 6, 7 = 7] → [4, 5, 6, 7]
根据数位和排序:[22, 14, 33, 7]。数组已经是排序好的。
因此,将 nums 排列为排序顺序所需的最小交换次数为 0。
示例 3:
输入: nums = [18,43,34,16]
输出: 2
解释:
计算每个整数的数位和:[1 + 8 = 9, 4 + 3 = 7, 3 + 4 = 7, 1 + 6 = 7] → [9, 7, 7, 7]
根据数位和排序:[16, 34, 43, 18]。将 18 与 16 交换,再将 43 与 34 交换,得到排序后的数组。
因此,将 nums 排列为排序顺序所需的最小交换次数为 2。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
nums 由 互不相同 的正整数组成。
解题方法:计算连通块个数 我们可以创建一个下标数组idxs,其中idxs[i]表示排序后nums[i]的下标。
假设排序后idxs数组为[1, 0, 3, 4, 2],则说明下标需要由原来的[0, 1, 2, 3, 4]变为[1, 0, 3, 4, 2]。怎么变?
不难发现[0, 1]互相交换(共1次),[2, 3, 4]互相交换(共2次)即可。
我们可以把每个下标抽象为图中一个节点,idxs[i]表示节点i和节点idxs[i]之间有一条边。只需要求出整张图中有多少个连通块(上述例子中有两个连通块),则最小交换次数为n - 连通块个数。
具体方法一:并查集 从0到n-1遍历每个下标i,将i和idxs[i]合并。最后返回n - 连通块个数。
具体方法二:迭代 使用一个visited数组标记每个下标是否被访问过,从0到n-1遍历每个下标i,如果visited[i]为false,则说明找到了一个新的连通块,将连通块个数加一,并将该连通块中的所有 下标都标记为已访问。
时空复杂度
时间复杂度$O(n\log U + n\log n)$,其中$n=len(nums)$,$U=\max (nums)$
空间复杂度$O(n)$
AC代码 C++ —— 并查集 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class Solution {private : vector<int > parent; vector<int > sz; int getSum (int a) { int ans = 0 ; while (a) { ans += a % 10 ; a /= 10 ; } return ans; } void initUnion (int n) { parent.resize (n); ranges::iota (parent, 0 ); sz.assign (n, 1 ); } int find (int a) { if (parent[a] == a) { return a; } return parent[a] = find (parent[a]); } bool unite (int a, int b) { int pa = find (a); int pb = find (b); if (pa == pb) { return false ; } if (sz[pa] > sz[pb]) { swap (pa, pb); } parent[pa] = pb; sz[pb] += sz[pa]; return true ; }public : int minSwaps (vector<int >& nums) { int n = nums.size (); vector<int > idxs (n) ; ranges::iota (idxs, 0 ); sort (idxs.begin (), idxs.end (), [this , &nums](const int & a, const int & b) { int sa = getSum (nums[a]); int sb = getSum (nums[b]); return sa == sb ? nums[a] < nums[b] : sa < sb; }); int ans = nums.size (); initUnion (n); for (int i = 0 ; i < n; i++) { ans -= !unite (i, idxs[i]); } return ans; } };
C++ —— 迭代 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution {private : int getSum (int a) { int ans = 0 ; while (a) { ans += a % 10 ; a /= 10 ; } return ans; }public : int minSwaps (vector<int >& nums) { int n = nums.size (); vector<int > idxs (n) ; ranges::iota (idxs, 0 ); sort (idxs.begin (), idxs.end (), [this , &nums](const int & a, const int & b) { int sa = getSum (nums[a]); int sb = getSum (nums[b]); return sa == sb ? nums[a] < nums[b] : sa < sb; }); int ans = nums.size (); vector<bool > visited (n) ; for (int i = 0 ; i < n; i++) { if (!visited[i]) { ans--; } int x = i; while (!visited[x]) { visited[x] = true ; x = idxs[x]; } } return ans; } };
Python —— 并查集 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 ''' LastEditTime: 2026-10-17 19:03:51 ''' from typing import List class Union : def __init__ (self, n: int ) -> None : self .parent = [i for i in range (n)] self .sz = [1 ] * n self .count = n def merge (self, x: int , y: int ) -> None : px = self .find(x) py = self .find(y) if px == py: return if self .sz[px] > self .sz[py]: px, py = py, px self .parent[px] = py self .sz[py] += self .sz[px] self .count -= 1 def find (self, x: int ) -> int : if self .parent[x] == x: return x self .parent[x] = self .find(self .parent[x]) return self .parent[x]class Solution : def _getSum (self, n: int ) -> int : ans = 0 while n: ans += n % 10 n //= 10 return ans def minSwaps (self, nums: List [int ] ) -> int : n = len (nums) idxs = [i for i in range (n)] idxs.sort(key=lambda i: (self ._getSum(nums[i]), nums[i])) union = Union (n) for i in range (n): union.merge(i, idxs[i]) return n - union.count
Java —— 并查集 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import java.util.Arrays;class Union { private final int [] parent; private final int [] sz; int count; Union(int n) { parent = new int [n]; sz = new int [n]; count = n; for (int i = 0 ; i < n; i++) { parent[i] = i; sz[i] = 1 ; } } void merge (int x, int y) { int px = find(x); int py = find(y); if (px == py) { return ; } count--; if (sz[px] > sz[py]) { int tmp = px; px = py; py = tmp; } sz[py] += sz[px]; parent[px] = py; } int find (int x) { if (parent[x] == x) { return x; } return parent[x] = find(parent[x]); } }class Solution { private int getSum (int n) { int ans = 0 ; while (n != 0 ) { ans += n % 10 ; n /= 10 ; } return ans; } public int minSwaps (int [] nums) { int n = nums.length; Integer [] idxs = new Integer [n]; for (int i = 0 ; i < n; i++) { idxs[i] = i; } Arrays.sort(idxs, (a, b) -> { int sa = getSum(nums[a]); int sb = getSum(nums[b]); return sa == sb ? nums[a] - nums[b] : sa - sb; }); Union union = new Union (n); for (int i = 0 ; i < n; i++) { union.merge(i, idxs[i]); } return n - union.count; } }
Go —— 并查集 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package mainimport "sort" type Union3551 struct { parent []int sz []int count int }func newUnion3551 (n int ) *Union3551 { parent := make ([]int , n) sz := make ([]int , n) for i := range parent { parent[i] = i sz[i] = 1 } return &Union3551 { parent: parent, sz: sz, count: n, } }func (u *Union3551) Find(x int ) int { if u.parent[x] == x { return x } u.parent[x] = u.Find(u.parent[x]) return u.parent[x] }func (u *Union3551) Merge(x, y int ) { px := u.Find(x) py := u.Find(y) if px == py { return } if u.sz[px] > u.sz[py] { px, py = py, px } u.sz[py] += u.sz[px] u.parent[px] = py u.count-- }func getSum3551 (x int ) (ans int ) { for x > 0 { ans += x % 10 x /= 10 } return }func minSwaps (nums []int ) int { n := len (nums) idxs := make ([]int , n) sums := make ([]int , n) for i, t := range nums { idxs[i] = i; sums[i] = getSum3551(t) } sort.Slice(idxs, func (i, j int ) bool { if sums[idxs[i]] == sums[idxs[j]] { return nums[idxs[i]] < nums[idxs[j]] } return sums[idxs[i]] < sums[idxs[j]] }) union := newUnion3551(n) for i, t := range idxs { union.Merge(i, t) } return n - union.count }
Rust —— 并查集 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 struct Union { parent: Vec <usize >, sz: Vec <usize >, count: usize , }impl Union { fn new (n: usize ) -> Self { Union { parent: (0 ..n).collect (), sz: vec! [1 ; n], count: n } } fn find (&mut self , x: usize ) -> usize { if self .parent[x] == x { x } else { self .parent[x] = self .find (self .parent[x]); self .parent[x] } } fn merge (&mut self , x: usize , y: usize ) { let mut px = self .find (x); let mut py = self .find (y); if px == py { return ; } if self .sz[px] > self .sz[py] { std::mem::swap (&mut px, &mut py); } self .sz[py] += self .sz[px]; self .parent[px] = py; self .count -= 1 ; } }impl Solution { fn get_sum (mut n: i32 ) -> i32 { let mut ans = 0 ; while n > 0 { ans += n % 10 ; n /= 10 ; } ans } pub fn min_swaps (nums: Vec <i32 >) -> i32 { let n = nums.len (); let mut idxs : Vec <usize > = (0 ..n).collect (); idxs.sort_by_key (|&i| (Solution::get_sum (nums[i]), nums[i])); let mut union = Union::new (n); for i in 0 ..n { union .merge (i, idxs[i]); } (n - union .count) as i32 } }
End
同步发文于CSDN 和我的个人博客 ,原创不易,转载经作者同意后请附上原文链接 哦~
千篇源码题解已开源