题目来源
题目描述
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
}
};
题目解析
题意:
- 给出一个数组,数组里面两两相邻的元素代表一对情侣。情侣编号是从0开始的::0 和 1 是情侣,2 和 3 是情侣……这些情侣坐在⼀排,但是并⾮成对坐着⼀起的,问如何⽤最⼩的次数交换座位以后,情侣能两两坐在⼀起
贪心
举个例子:[3 1 4 0 2 5]
我们来两个两个的看数字。
- 前两个数是3和1,不是情侣,3的伴侣应该是2,怎么办呢?那么我们找到2的位置,将2和1交换位置。
- [3 2 4 0 1 5]
- 再取两个数字4和0,不是情侣,4的伴侣应该是5,怎么办呢?那么我们找到5的位置,将5与0交换位置
- [3 2 4 5 1 0]
- 最后两个数字1和0,不用动,因为前面都成对,最后两个数字一定成对
怎么写?
class Solution {
public:
int minSwapsCouples(vector<int>& row) {
int result = 0;
int n = row.size();
for (int i = 0; i < n; i += 2) {
if (row[i] == (row[i + 1] ^ 1)) continue; // 运用异或运算判断是不是一对情侣
for (int j = i + 1; j < n; j++) { // 如果不是,再进行搜寻
if ((row[j] ^ 1) == row[i]) { // 找到了另一半
swap(row[i + 1], row[j]); // 交换座位
result++; // 交换次数+1
break;
}
}
}
return result;
}
};
并查集
这道题说到底是一个群组问题,所以可以用并查集来搞。
举个例子:[3 1 4 0 2 5]
刚开始的群组关系是:
- 群组0:0,1
- 群组1:2,3
- 群组2:4,5
取出前两个数字3和1,其群组号分别为1和0,带入 find 函数返回不同值,则此时将群组0和群组1链接起来,变成一个群组,则此时只有两个群组了,res 自增1,变为了1。
- 群组0 & 1:0,1,2,3
- 群组2:4,5
此时取出4和0,其群组号分别为2和0,带入 find 函数返回不同值,则此时将群组 0&1 和群组2链接起来,变成一个超大群组,res 自增1,变为了2。
- 群组0 & 1 & 2:0,1,2,3,4,5
此时取出最后两个数2和5,其群组号分别为1和2,因为此时都是一个大组内的了,带入 find 函数返回相同的值,不做任何处理。最终交换的步数就是 res 值,为2。
又如果一对情侣恰好坐在了一起,并且坐在了成组的座位上,其中一个下标一定是偶数,另一个一定是奇数,并且「偶数的值 + 1 = 奇数的值」。例如编号数对 [2, 3]、[9, 8],这些数对的特点是除以 22(下取整)得到的数相等。
代码如下:
class Solution {
class UnionFind{
private:
std::vector<int> parent; // parent[i] = k : i的父亲是k
std::vector<int> size; // size[i] = k : 如果i是代表节点,size[i]才有意义( i所在的集合大小是多少),否则无意义
std::vector<int> help; // 辅助结构
int cnt; //一共有多少个集合
public:
explicit UnionFind(int n){
cnt = n;
parent.resize(n);
size.resize(n);
help.resize(n);
for (int i = 0; i < n; ++i) {
parent[i] = i;
size[i] = 1;
}
}
// 返回i的代表节点
// 这个过程要做路径压缩
int findRoot(int i){
int hi = 0;
while (i != parent[i]){
help[hi++] = parent[i];
i = parent[i];
}
for (hi--; hi >= 0; --hi) {
parent[help[hi]] = i;
}
return i;
}
void merge(int i, int j){
int f1 = findRoot(i);
int f2 = findRoot(j);
if(f1 != f2){
if(size[f1] >= size[f2]){
parent[f2] = f1;
size[f1] = size[f1] + size[f2];
}else{
parent[f1] = f2;
size[f2] = size[f2] + size[f1];
}
--cnt;
}
}
int counts() const{
return cnt;
}
};
public:
int minSwapsCouples(vector<int>& row) {
int len = row.size();
int N = len / 2;
UnionFind unionFind(N);
for (int i = 0; i < len; i += 2) {
unionFind.merge(row[i] / 2, row[i + 1] / 2);
}
return N - unionFind.counts();
}
};