package com.demo.common;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 置换环算法
* @Author: qk
* @Date: 2023/7/27 15:22
*/
public class ChangeLoop {
/**
* 置换环算法
* <p>
* 题目背景: 给定一个数组,一次操作可以交换任意2个节点的值,求数组按严格递增顺序排序所需的最少操作数目
* <p>
* 置换环的思想为 : 对每个节点,将其指向其排序后应该放到的位置,直到首位相接形成了一个环。
* <p>
* 具体实现思想:
* ---使用 Map 哈希表记录每个节点值以及其应该放到的位置
* ---从头到尾遍历初始数组,使用 flag[boolean] 数组标记当前元素是否已经参与过(即已经被加入环中),对已经参与过的数组则不再需要遍历。每次成环结束,记录成环个数 loop。
* ---最终最小交换次数为 :数组长度 - 成环个数 nums.size() - loop
* <p>
* --- 一个环交换的次数 = 环上元素的数量 - 1
* --- 总次数 = 元素总个数 - 环数
*/
public static int minChanges(int[] nums) {
// 使用 map<值,位置下标> 记录每个值及其正确的位置
Map<Integer, Integer> map = new HashMap<>();
int[] ints = Arrays.copyOf(nums, nums.length);
Arrays.sort(ints);
for (int i = 0; i < ints.length; i++) {
map.put(ints[i], i);
}
// 使用数组标记当前元素是否参与画环
boolean[] flag = new boolean[nums.length];
// 环的个数
int loop = 0;
// 从头到尾遍历初始数组
for (int i = 0; i < nums.length; i++) {
if (!flag[i]) {
int j = i;
// 画环
while (!flag[j]) {
// 画出当前节点指向正确的位置,画环过程
Integer next = map.get(nums[j]);
// 将 j 加入环中
flag[j] = true;
// 将当前节点移动到环上下个节点
j = next;
}
// 完成一个回环+1
loop++;
}
}
return nums.length - loop;
}
public static void main(String[] args) {
int[] ints = new int[]{7, 6, 8, 5};
System.out.println(minChanges(ints));
}
}