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

1136

被折叠的 条评论
为什么被折叠?



