题目来源于leetcode,解法和思路仅代表个人观点。传送门。
难度:困难(今天虾仁猪心)
时间:40min
题目
N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。
人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。
这些情侣的初始座位 row[i] 是由最初始坐在第 i 个座位上的人决定的。
示例 1:
输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。
示例 2:
输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
说明:
len(row) 是偶数且数值在 [4, 60]范围内。
可以保证row 是序列 0…len(row)-1 的一个全排列。
思路
记每个对 的第一个数为num,第二个数为spouse。只需要num和spouse相邻就可以了。而且输入保证是2N个数,那么肯定能凑完N对。
- 如果要把所有对 凑完,那么num和spouse肯定在数组的(2i-2,2i-1)的位置上,即num前面有2k个数。
- 题目要求最小的交换次数。但是实际上只需要保证交换不多余就行,即每次交换至少有1对凑成
并查集
(1,3),(3,2),(1,2)凑成一个集合,(4,4)凑成一个集合。只需要计算每个集合中边的数目,即是最小的交换次数。
贪心
使用一个map记录指针到
i
i
i为止,数字的所在数组的位置。
每次都尝试着去匹配(交换它相邻元素)map中已存在的数字,(如果配对的数字不存在,就不匹配)。
为什么可行?
每次都至少有一对凑成。
要保证每次都不为以后的操作产生多余的交换。
- 每次交换的时候都至少凑成1对
- 每次交换都需要保证第 i i i对前面有 2 k 2k 2k个数字
代码
贪心
class Solution {
public:
template <typename T>
void swap(T &a,T &b){
T temp = a;
a = b;
b = a;
}
int minSwapsCouples(vector<int>& row) {
int ans = 0;
//哈希表,记录第i的元素为止的位置
//匹配交换的任务,交给第二个元素
unordered_map<int,int> map;
//数组大小
int n = row.size();
for(int i=0;i<n;i++){
//当前位置的元素
int& num = row[i];
map[num] = i;
//当前位置的元素如果为偶数,则为第一个数字,奇数为第二个数字
//如:(0,1)中0为第一个数字,1为第二个数字
int spouse = 0;
if(num%2 == 0){
//偶数,找后一个数字
spouse = num + 1;
}else{
//奇数,找前一个数字
spouse = num - 1;
}
//如果配偶不存在,则继续往后找
//如果配偶存在
if(map.count(spouse)){
//配偶的位置
int j = map[spouse];
//与它旁边的数字交换
/*
* 为什么要这样交换?
* 确保第i对前面有2k个数字【k对】
*/
if(j%2 == 0){
if(num != row[j+1]){
swap(num,row[j+1]);
ans++;
}
}else{
if(num != row[j-1]){
swap(num,row[j-1]);
ans++;
}
}
//更新map
map[row[i]] = i;
}
}
return ans;
}
};
算法复杂度
时间复杂度: O(n),n为数组长度。进行一次遍历
空间复杂度: O(n),n为数组长度。用于哈希表存储元素的位置。