解题思路:

有三个数组,p,b,a。p为从小到大元素数值排序,并且起到储存元素数值的作用。

a为p对应到原位置的顺序,从0开始,注意a也是按某种意义的从小到大排序,与p不同的是,它存的是原位置不是数值。

b为原顺序的大小顺序,但是是从0开始。b承担了许多作用,它是唯一按原顺序储存的,而a和p是按大小排序的,所以想按原顺序的排序方式输出时就要用b。

实际上,b和a 是相互储存对方的位置(a为从小到大对应到原位置的顺序,b为原顺序的大小顺序)。也就有了这步 b[i]=x,a[x]=i;

很好玩的是 a[b[i]]就是原顺序的第i个的原位置,也就是等于i 。

b[a[i]]就是从小到大的第i个的原位置的大小排序位置,也就还是等于i 。(很绕是吗,没错,我自己都快整不明白了)

由于p为从小到大元素数值排序,所以p[b[i]]进行循环输出就可以输出未进行排序的原元素数值。

在循环过程中,因为 a和p是大小顺序进行储存,所以经常要整个移动。而b是按原插入顺序存储,所以位置并不变,只改变数值,即储存的大小顺序。


循环过程:每次插入一个新元素时,从大到小进行大小比较,直达找到位置。
具体表现为新元素数值 c<p[x] 时,即要往左移,所以p[x]和a[x]的位置都要往右移一格,表现为p[x+1]=p[x]和a[x+1]=a[x]。(但是a的这一步要慢一步执行)

同时p[x]的大小顺序要加一,而a并非大小顺序,要往外再套一层b才是p[x]的大小顺序,具体表现为b[a[i]]加一。

经过这两行就成功将p[x]往右一格。

然后就可以重复直到找到位置,赋值后就可以开启下一步循环。

(其实应该可以用二路并归排序的,但我还没构思好,没想到用插入排序就过了,居然才测试了两个例子);


注意事项:

由于我们代码的大小顺序从0开始,所以输出时要加一。

由于涉及到+1,因此0号位也就是初始赋值要循环外。

大小比较时要防止下溢。
参考代码:

# include<stdio.h>
int main(){ 
        int n;
	int *p;
	int *b;
	int *a;
	while(scanf("%d",&n)!=EOF){
    p=(int *)malloc(n*sizeof(int));
    b=(int *)malloc(n*sizeof(int));
    a=(int *)malloc(n*sizeof(int));
    scanf("%d",&p[0]);b[0]=a[0]=0;  //防止p[x+1]上溢
    for(int i=1,c,x;i<n;i++){
    	scanf("%d",&c);  //数值存给c
		x=i-1;  //循环起始点为i-1,表现为与已排序的最大值进行比较
    	while(x>=0){   //x小于0时就是c是最小值
		if(c<p[x])b[a[x]]++,p[x+1]=p[x],a[x+1]=a[x];  //上面十几行文字解释的这步操作
    	else break;
    	x--;  //继续与下一位比较大小
		}
		x++;  //由于每次循环最后都是x--,x是指向指定位置的左一位,因此要+1调整到正确位置,此时x就是大小排序了
    	p[x]=c,b[i]=x,a[x]=i;  //将数值c赋值给p[x],将大小排序x赋值给b[i],将原位置i赋值a[x]
	}
	for(int i=0;i<n-1;i++)printf("%d ",b[i]+1);
	printf("%d\n",b[n-1]+1);//最后不能有空格
	free(p);
	free(b);
	free(a);
	}
	return 0;
}


点赞(0)
 

0.0分

0 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论