首页 > 代码库 > 一个关于数组存储特定序列问题的思考

一个关于数组存储特定序列问题的思考

原题要求:

已有一个数组有奇偶数,将奇数放在数组的“左边”,偶数在“右边”

要求不允许在额外使用数组空间。


例如:

Before sort:
1 2 3 4 5 6 7 8 9 10
After sort:
1 9 3 7 5 6 4 8 2 10


初看之下立马有了一个思路:奇偶对调。即使用遍历的方式首尾各自遍历,首部发现偶数后与尾部发现的奇数进行交换位置。

  1 #include <stdio.h>
  2 
  3 #define N 10
  4 
  5 int count=0;
  6 
  7 void arrsort(int *a,int len){
  8     int i,j;
  9     for(i=0;i<len;i++){
 10         for(j=len-1;j>i;j--){
 11             count++; // 用于计数,统计循环次数
 12             if(!(a[i] & 0x01)){
 13                 if(a[j] & 0x01){
 14                     a[i] ^= a[j];
 15                     a[j] ^= a[i];
 16                     a[i] ^= a[j];
 17                     break;
 18                 }
 19             } else break;
 20         }
 21     }
 22 }
 23 
 24 int main(){
 25     int k;
 26     int a[N]={1,2,3,4,5,6,7,8,9,10};
 27     printf("Before sort:\n");
 28     for(k=0;k<N;k++){
 29         printf("%d ",a[k]);
 30     }
 31     arrsort(a,N);
 32     printf("\nAfter sort:\n");
 33     for(k=0;k<N;k++){
 34         printf("%d ",a[k]);
 35     }
 36     printf("\nTotal count is: %d\n",count);
 37     return 0;
 38 }
输出结果:

Before sort:
1 2 3 4 5 6 7 8 9 10
After sort:
1 9 3 7 5 6 4 8 2 10
Total count is: 19 -- 内循环执行了19次

看执行的结果是正确了,但是从主观的角度来说,这个循环次数有点多啊,直观上看,其实就是交换3组数(2-9、3-8、4-7),需要用到19次?

还是优化一下吧

思路:若是首部的数是奇数,则自动跳转到下一顺位继续判断,所以内层循环可以不用执行,因此将

if(!(a[i] & 0x01)){ // 判断为偶数
语句调整至外层循环内,即:

  5 void arrsort(int *a,int len){
  6     int i,j;
  7     for(i=0;i<len;i++){
  8         <span style="color:#FF0000;"><strong>if(a[i] & 0x01) continue;</strong></span>//非偶数时直接跳过
  9         for(j=len-1;j>i;j--){
 10             count++;
 11             if(a[j] & 0x01){
 12                 a[i] ^= a[j];
 13                 a[j] ^= a[i];
 14                 a[i] ^= a[j];
 15                 break;
 16             }
 17         }
 18     }
 19 }
输出结果:

Before sort:
1 2 3 4 5 6 7 8 9 10
After sort:
1 9 3 7 5 6 4 8 2 10
Total count is: 16 --内循环执行了16次

恩,总算是一个进步不是,虽然这个进步有点小!


还可以再短吗?这个好像。。。似乎。。。应该。。。可以吧?!

我们忽略了一个常见的思考手段,当首部和尾部进行一次交换后,随后的遍历应该可以把各自对应的位置“忽略”

即比如交换了第1位和第9位,那么在下一次遍历时候首部应从第2位、而尾部应该从第8位开始(首部是正序,尾部是倒序)

所以以上两种方式都忽略了尾部的倒序开始位置,所以做了很多无用的遍历。

按照这个思路调整一下,得到:

  5 void arrsort(int *a,int len){
  6     int i,j,n;
  7     n=len;
  8     for(i=0; i<n; i++){
  9         if(a[i] & 0x01) continue;
 10         for(j=n-1; j>i; j--){
 11             count++;
 12             if(a[j] & 0x01){
 13                 a[i] ^= a[j];
 14                 a[j] ^= a[i];
 15                 a[i] ^= a[j];                    n=j; 16                 break;
 17             }
 18             n=j;
 19         }
 20     }
 21 }
输出结果:

Before sort:
1 2 3 4 5 6 7 8 9 10
After sort:
1 9 3 7 5 6 4 8 2 10
Total count is: 4 -- 循环执行了4次

哇卡卡,这个进步应该不算小了吧!

还能再优化吗?这个问题留个我们大家一起吧。

突然想到一个思路:

内层循环中交换一次后采用了break退出了,其实还可以继续向前遍历,直到遇见奇数为止,

但是这样就又来一个问题是增加了一层循环,空间成本上也是不小的开销。

希望能给各位带来一点灵感!

一个关于数组存储特定序列问题的思考