10-排序6 Sort with Swap(0, i)

博客探讨了在排序过程中如何通过交换0到其正确位置来优化算法。初始尝试的时间复杂度为O(N^3),通过记录0的位置和数组顺序进行优化,降低到O(N^2)。最后引入了数论中的环形结构概念,进一步优化至O(N),显著减少了运行时间。
  • 题目
    10-排序6 Sort with Swap(0, i) (25分)
  • 分析
    我刚开始想着直接排序。先试了几个例子,经过总结之后,觉得步骤应该是这样:
    1. 看0在不在位置0处(即得到0所在的位置pos0),如果不在,把0与应该在pos0位置处的数字交换,然后重复执行步骤1;如果在则向下执行。
    2. 看数组是否有序,如果数组无序,把0与第一个无序的数字交换,然后执行步骤1;如果有序则停止。
      在上述执行中,记录swap0i的次数。
  • 代码
#include <stdio.h>
int num[100000];

int find(int x,int n){
    for(int i=0; i<n; i++){
        if(num[i] == x) return i;
    }
    return -1;
}
void swap0i(int pos0,int i, int n){
    int tmp = num[i];
    num[i] = num[pos0];
    num[pos0] = tmp;
}

//返回-1表明有序了,否则返回第一个不是正确数的位置
int isSorted(int n){
    int i;
    for(i=0; i<n; i++){
        if(num[i] != i) return i;
    }
    return -1;
}
int main(int argc, const char * argv[]) {
    int n,i,ans = 0;
    int pos0 = 0,loc;
    scanf("%d",&n);
    for(i=0; i<n; i++){
        scanf("%d",&num[i]);
        //if(num[i] == 0) pos0 = i;
    }
    while ((loc =isSorted(n)) != -1) {

        pos0 = find(0, n);
        if(pos0 == 0){
            swap0i(pos0,loc, n);
            ans++;
        }
        while (pos0 != 0) {
            loc = find(pos0, n);
            swap0i(pos0,loc, n);
            ans++;

            pos0 = loc;
        }
    }

    printf("%d\n",ans);
    return 0;
}
  • 运行结果
    超时
  • 算法复杂度分析
    有两层循环(while),最里面的时间复杂度loc = find(pos0, n)是O(N),所以大概的时间复杂度是O(N^3)。所以肯定会超时。
  • 改进一:以空间换时间,把每一个数的位置直接保存在一个数组中,这样loc = find(pos0, n)就可以变成O(1)的时间复杂度。
#include <stdio.h>
int num[100000];
int pos[100000];

void swap0i(int pos0,int i, int n){
    int tmp = num[i];
    num[i] = num[pos0];
    num[pos0] = tmp;
    pos[0] = i;
    pos[tmp] = pos0;
}

//返回-1表明有序了,否则返回第一个不是正确数的位置
int isSorted(int n){
    int i;
    for(i=0; i<n; i++){
        if(num[i] != i) return i;
    }
    return -1;
}
int main(int argc, const char * argv[]) {
    int n,i,ans = 0;
    int pos0 = 0,loc;
    scanf("%d",&n);
    for(i=0; i<n; i++){
        scanf("%d",&num[i]);
        pos[num[i]] = i;
        //if(num[i] == 0) pos0 = i;
    }
    while ((loc =isSorted(n)) != -1) {

        pos0 = pos[0];
        if(pos0 == 0){
            swap0i(pos0,loc, n);
            ans++;
        }
        while (pos0 != 0) {
            loc = pos[pos0];
            swap0i(pos0,loc, n);
            ans++;

            pos0 = loc;
        }
    }

    printf("%d\n",ans);
    return 0;
}


  • 运行结果
    还是超时
  • 算法复杂度分析
    有两层循环(while),最里面的时间复杂度是O(1),所以大概的时间复杂度是O(N^2)。所以还是会超时。
  • 再次分析
    我参考了浙江大学陈姥姥的讲课内容,需要用到一个结论:

N个数字的排列是有若干个独立的环组成。
环的分类

其实我们通过上述的步骤,就可以很容易上图中的意思。
同时注意如果多元环中都没有0,那么总的交换次数就等于(假设n个元素中有S个单元环,K个多元环):
每个多元环的交换次数为Ni+1,全部加起来就是n-S+K。
现在我们只需要直接统计各种环的个数了。

#include <stdio.h>
#define maxn 100000
int num[maxn];
int flag[maxn];   //flag[i]用来标记第i个位置是否被遍历过
int pos[maxn];    //pos[i]用来标记数字i在num[]中的哪个位置

int main(int argc, const char * argv[]) {
    int n,i,ans = 0;
    int S = 0, K = 0;
    scanf("%d",&n);
    for(i=0; i<n; i++){
        //初始化三个数组
        scanf("%d",&num[i]);
        flag[i] = 0;
        pos[num[i]] = i;
        //顺便直接统计单元素环
        if(num[i] == i){
            S++;
            flag[i] = 1;
        }
    }
    int beginNum,tmp;
    for(i=0; i<n; i++){
        if(flag[i]) continue;
        else{
            K++;
            beginNum = num[i];
            flag[beginNum] = 1;
            flag[i] = 1;
            tmp = i;
            while ((tmp=pos[tmp]) != beginNum) {
                flag[tmp] = 1;
            }

        }
    }
    if(num[0] == 0){
        ans = n-S+K;
    }else{
        ans = n-S+K-2;
    }
    printf("%d\n",ans);
    return 0;
}
  • 运行结果
  • 正确
    明显所花费的时间短多了。
  • 时间复杂度:
    我们只对整个数组进行了一次遍历,所以时间复杂度是O(N)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值