浙大 PAT 甲级 1067 Sort with Swap(0, i) 特殊的排序 详细过程

算法思路

本题不难,下面说一下解题过程中的思路。首先,题目要求只能通过0与i交换来排序,根据样例可以断定,最快的方法是将0与0当前所在位置应该放置的数进行对换。例如:

最开始 {4, 0, 2, 1, 3},0在1号位置,那么将0与1对换

Swap(0, 1) => {4, 1, 2, 0, 3}

这样,1就完成了正确的摆放位置。现在,0在3号位置,那么将0与3对换

Swap(0, 3) => {4, 1, 2, 3, 0}

 这样,3也完成了正确的摆放位置。现在,0在4号位置,那么将0与4对换

Swap(0, 4) => {0, 1, 2, 3, 4}

可以看到,排序完成。如果理想的话,照这种方法,只需要n-1次对换即可完成,n为不在正确位置的元素的个数。这题初始情况下只有2在正确位置,因此n=4,需要4-1=3次对换完成排序。

但存在一种情况,即排序未完成,而0回到了正确位置。比如{1,0,3,2},第一次对换是0个1,那么现在0回到了0号位置,剩下的3和2就不能完成排序了。出现这种情况的解决方法是:当0回到0号位置,但排序未完成时,将0与任意不在正确位置的元素对换,继续上述过程。比如{1,0,3,2},经过第一次对换后变成

Swap(0, 1) => {0, 1, 3, 2}

将0和3对换,继续上述过程。当然也可以将0与2对换,继续上述过程。

 

代码实现思路

有了思路之后,根据思路选择恰当的数据结构并进行编码。由于很难发现上述过程简洁的数学公式,我们不如直接进行“翻译”,模拟一遍这个过程,输入对换次数即可。 在模拟过程中有几点需要注意:

一,“将0与0当前所在位置应该放置的数进行对换。”

比如0现在在3号位置,我们需要将0和3对换,但3现在在哪个位置呢?如果遍历数组进行查找,时间复杂度肯定不允许,这是可以预料到的。除了保存序列的数组外,再定义一个int pos[i],它表示等于i的元素当前所在的位置,在模拟的过程中维护这个数组,那么在需要找到任意元素的位置时,只需要O(1)时间复杂度。

但值得注意的是,pos和arr两个数组在模拟过程中要仔细维护,它们之间的关系可能有些麻烦,需要多检查几遍,耐心编码。

二,“当0回到0号位置,但排序未完成时,将0与任意不在正确位置的元素对换。”

我们需要找到任意一个还未正确摆放的元素,通过遍历数组,看元素和下标是否相等来找这个元素。定义一个函数int check(),输出第一个元素与下标不等的下标,将0与其对换。如果元素与下标都相等,代表排序完成,结束模拟过程。看似可行,但有一个测点会超时。不停的遍历数组,在机试题中肯定是不可以的。

找一个trick解决这个问题。出现这种情况,我们只需要找到任意一个不在正确位置的元素,任意,肯定不需要那么复杂。定义一个STL set容器,用来保存目前尚未在正确位置的元素。我们应该清楚的记得set常用操作的时间复杂度

插入: O(logN)

查看:O(logN)

删除:O(logN)

非常合适。在模拟过程中维护这个set,在需要找到任意一个不在正确位置的元素时,输出第一个元素即可。如果set为空,代表已完成排序,结束模拟。

 

代码

下面给出AC代码

#include<stdio.h>
#include<set>
using namespace std;

int arr[100000];
int pos[100000];
set<int> notSorted;

int check(int* arr, int N)
{
    if (notSorted.empty() == true)
    {
        return -1;
    }
    else
    {
        return pos[*(notSorted.begin())];
    }
}

int main()
{
    int N;
    scanf("%d", &N);
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &arr[i]);
        pos[arr[i]] = i;
        if (arr[i] != 0 && arr[i] != i)
        {
            notSorted.insert(arr[i]);
        }
    }
    int ans = 0;
    while (1)
    {
        if (pos[0] == 0)
        {
            pos[0] = check(arr, N);
            if (pos[0] == -1)
            {
                break;
            }
            else
            {
                int tmp = arr[pos[0]];
                arr[0] = tmp;
                arr[pos[0]] = 0;
                pos[tmp] = 0;
                ans++;
            }
        }
        else
        {
            int num = pos[0];
            int posOfNum = pos[num];
            arr[pos[0]] = pos[0];
            arr[posOfNum] = 0;
            pos[0] = posOfNum;
            posOfNum = num;
            notSorted.erase(num);
            ans++;
        }
    }
    printf("%d\n", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值