题目描述
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 的一个全排列。
并查集(From 《挑战程序设计竞赛》)
并查集是一种处理元素分组情况的数据结构。并查集可进行如下操作:
- 查询两个元素是否属于同一组。
- 合并两个元素所在的组。
并查集使用树形结构:每个组对应一颗树,每个元素对应一个节点。哪个节点作为根节点无需关注,整体组成一棵树即可。
相关操作:
- 初始化:每个节点作为一棵树。
- 合并:从一颗树的根向另一颗树的根连边,合并为一棵树,即一棵树作为另一棵树的子树。
- 查询:沿着树向上走,查询包含这个元素的根节点。如果两个元素具有相同的根节点,则两个元素属于同一组。
- 优化:
- 合并时,高度小的树作为高度大的树的子树。确保最终的树的高度尽可能小。
- 路径压缩:所有节点直接指向根节点,即树的高度为2。
并查集的实现:
class Union{ // 每个元素的根 private int[] parent; // 并查集中分组个数 private int count; // 初始化 Union(int count){ this.count=count; parent=new int[count]; for(int i=0;i<count;++i){ parent[i]=i; } } // 查询元素所在树的根 public int findParent(int n){ // 自己是自己的parent,即:自己的树的根 while(n!=parent[n]){ parent[n]=parent[parent[n]];// 压缩:同一集合中的所有元素指向同一个parent。 n=parent[n]; } return n; } // 合并元素x和y所属的组 public void union(int x,int y){ int parentX=findParent(x); int parentY=findParent(y); // 已在同一组 if(parentX==parentY){ return; } // 合并 parent[parentX]=parentY; --count;// 总分组数 } }
本题解题思路(参考)
坐错位置的情况与最少需要交换次数:
- 1对情侣、2个座位,不需要交换。
- 2对情侣、4个座位,交换1次。
- 3对情侣、6个座位。首先交换1次使得其中1对情侣坐在一起,剩下2对情侣、4个座位。即需要交换2次。
- 以此类推,得到f(n)=n-1。即:n对情侣相互坐错位置,最少需要交换n-1次。
把相互坐错位置的情侣放在一组,组内有n对情侣就需要n-1次交换。将N对情侣分为K组:n1,n2...nk,有n1+n2+...+nk=N。需要交换的次数分别为:n1-1、n2-1、...、nk-1,则总的最少交换次数为n1-1+n2-1+...+nk-1=n1+n2+...+nk-k=N-k。则,问题变为:N对情侣,根据相互坐错位置的条件分组,共有多少个分组。
class Solution { public int minSwapsCouples(int[] row){ int maxCount=row.length/2;// 情侣对数 Union union=new Union(maxCount); for(int i=0;i<row.length-1;i+=2){ // 除2表示属于哪一对情侣(0...n-1) union.union(row[i]/2,row[i+1]/2); } return maxCount-union.count; } class Union{ private int[] parent; private int count; Union(int count){ this.count=count; parent=new int[count]; for(int i=0;i<count;++i){ parent[i]=i; } } public int findParent(int n){ while(n!=parent[n]){ parent[n]=parent[parent[n]]; n=parent[n]; } return n; } public void union(int x,int y){ int parentX=findParent(x); int parentY=findParent(y); if(parentX==parentY){ return; } parent[parentX]=parentY; --count;// 总分组数 } } }