使序列有序的最少交换次数-任意交换元素位置

题意: 给定一个包含1-n的数列,通过交换任意两个元素给数列重新排序。求最少需要多少次交换,能把数组排成按1-n递增的顺序,(数组中的元素互不重复)。


举例: 原数列为2,4,3,6,5,7;第一次交换(3,4)得2,3,4,6,5,7;第二次交换(5,6)得2,3,4,5,6,7。完成排序需2次。

思想: 构造“循环节”,找原位置与排序后位置不一致的形成交换环。元素2位置无需变动自形成一个环;元素4位置需变动(应在元素3位置),因此将元素4与经排序后要放入的位置处元素(‘3’)形成一个环;同理6,5形成一个环;7自形成一个环。一共四个环。我们都知道在一个环内将其元素按有序排列所要的交换次数为:环内元素个数-1。因此求解此题次数n = 数列元素个数-环数。

证明:假设有m个环,完成任务总的交换次数 = 环1元素个数-1+环2元素个数-1+....+环m元素个数-1  = (环1元素个数+环2元素个数+.....+环m元素个数 = 数列元素总个数)- (1+1+....+1 = m)。

代码:

#include
using namespace std;
/**
 *  交换任意两数的本质是改变了元素位置,
 *  故建立元素与其目标状态应放置位置的映射关系
 */
int getMinSwaps(vector v)
{
    vector v1(v);   //将A内元素复制到B。
    sort(v1.begin(), v1.end());
    map m;
    int len = v.size();
    for (int i = 0; i < len; i++)
    {
        m[v1[i]] = i;    //  建立每个元素与其应放位置的映射关系
    }
    int loops = 0;      //  循环节个数
    vector flag(len, false); //初始化
    //找出循环节的个数
    for (int i = 0; i < len; i++)
    {
        if (!flag[i])
        {
            int j = i;
            while (!flag[j])     //对环处理
            {
                flag[j] = true;
                j = m[v[j]];    //原序列中j位置的元素在有序序列中的位置
            }
            loops++;
        }
    }
    return len - loops;
}
vector v;
int main()
{
    int n,k;
    cin>>n;
    while(n--){
        cin>>k;
        v.push_back(k);
    }
    int num = getMinSwaps(v);
    cout<<"交换次数:"<

PS: 对于只在相邻两元素间交换的逆序对解法可参考我的另一篇文章:https://blog.csdn.net/lfb637/article/details/78309507

你可能感兴趣的:(数据结构与算法)