google面试题---排序数组所需的最小交换次数

给定n个不同元素的数组,找到排序数组所需的最小交换数。


输入:{ 4,3,2,1 } 
输出:2 说明:将索引0与3交换,1与2交换以形成已排序的数组{1,2,3,4}。

输入:{1,5,4,3,2} 
输出:2

通过将问题可视化为图形,可以轻松完成此操作。如果第i个索引处的元素必须出现在排序数组中的第j个索引处,我们将有n个节点和从节点i到节点j的边。

 

该图现在将包含许多非交叉循环。现在,具有2个节点的循环将仅需要1个交换以达到正确的排序,类似地,具有3个节点的循环将仅需要2个交换来执行此操作。

因此有:  

ans = \sum_{\\i=1}^{k}(cycle\_size-1)

k表示图中环的个数,cycle_size表示环内节点的大小.

因此就有:

public static int minSwaps(List<Integer> A){
		int count = 0;
		Map<Integer,Integer> map = new HashMap<Integer, Integer>();
		List<Integer> B = new ArrayList<>(A);
		Collections.sort(B);
		for(int i = 0; i < B.size();i++){
			map.put(B.get(i), i);
		}		
		//DFS
		ArrayList<Integer> C = new ArrayList<>(A);
		ArrayList<Integer> D = new ArrayList<>();
		for(int i = 0; i < C.size();i++){
			if(D.contains(C.get(i)))continue;
			int cycleSize = 1;			
			if(C.get(i)!=B.get(i)){				
				D.add(C.get(i));
				int a = C.get(i);
				while(true){
					int curA = map.get(a);//当前元素的正确位置
					int num = C.get(curA);//当前元素的正确位置上实际的元素
					int curNum = map.get(num);//当前元素的正确位置上的实际元素的正确位置(是否能形成cycle)
					cycleSize++;
					D.add(num);
					if(curNum==i){
						break;
					}
					a=num;
				}
				count+=(cycleSize-1);
			}	
		}
		return count;
	}

另一种类似的解法,将数组先排序,以<key,value>的形式保存到Map中,遍历未排序的数组,将元素逐个和已排序数组对应位置元素比较,若相等则跳过,不等则从Map中取出已排序元素的index更新未排序元素的value上(交换操作),同时swap list中的元素.这样遍历完成之后,就得到整个交换过程.代码如下:

public static List<List> minSwaps(List<Integer> A) {		
		 List<Integer> B = new ArrayList<>(A);
		 Collections.sort(B);	
		 Map<Integer, Integer> valToPos = new HashMap<>();		
		 for(int i = 0; i < A.size(); ++i)
		 valToPos.put(A.get(i), i);		
		 List<List> swaps = new ArrayList<>();		
		 swaps.add(new ArrayList<>(A));		
		 for(int i = 0; i < A.size(); ++i) {			
			 if(B.get(i) != A.get(i)) {			
				 int posBinA = valToPos.get(B.get(i));			
				 int posCurrA = i;				
				 valToPos.put(B.get(i), posCurrA);				
				 valToPos.put(A.get(i), posBinA);				
				 Collections.swap(A, posCurrA, posBinA);				
				 swaps.add(new ArrayList<>(A));			
			 }
		 }
		 return swaps;
	}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值