注册 登录
编程论坛 C语言论坛

对同一列数据用各种排序法来统计分别各自交换了多少次,来比较用哪种方法交换的次数最少?

自学的数学 发布于 2018-05-22 16:18, 11712 次点击
数列定位:int a[10]={10,9,8,7,6,5,4,3,2,1};
要求:用C语言里的8种排序法分别对它排序(排好后是从小到大的顺序),排序的同时,统计该数据共交换了多少次才排序成功。看看用哪种方法交换的次数最少?
提示:C语言里的8种排序法分别是
     1:冒泡排序
     2:选择排序
     3:插入排序
     4:希尔排序
     5:快速排序
     6:归并排序
     7:堆排序
     8:基数排序
当然,你可以只用一种排序法写出来共交换了多少次(比如我就用冒泡排序法排出来了,也统计了冒泡排序法要用45步完成排序),这也是可以的,当然了,你如果能编出代码把8 种排序法一起编进去那就更好了。
52 回复
#2
自学的数学2018-05-22 16:39
用“冒泡排序”,俺写好了,大家可以参考下:
程序代码:
#include <stdio.h>
void sort(int *a,int len)

 {
   int i,j,t,z,count=0;
   for(i=0;i<len-1;i++)
     {
       for(j=0;j<len-i-1;j++)
          {
           if(a[j]>a[j+1])
              {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
              printf(" 第 %3d 次交换: ",++count);
              for(z=0;z<10;z++)
              printf("%4d ",a[z]);
              printf("\n");
              }
            
          }
     }
      printf("共交换%4d 次",count);
      printf("\n");
  }
int main(int argc, char *argv[])
{
   int a[10]={10,9,8,7,6,5,4,3,2,1};
   int i=0;
   printf("这段代码是用冒泡排序法来统计共交换了多少次。\n ");
   printf("交换前的数据: ");
   for(i=0;i<10;i++)
              printf(" %4d",a[i]);
   printf("\n");
   sort(a,10);
   printf(" 交换后的数据: ");
   for(i=0;i<10;i++)
             printf("%4d ",a[i]);
   return 0;
}

效果图:
只有本站会员才能查看附件,请 登录
#3
lin51616782018-05-22 16:42
你好像对排序有点误解
选择排序 冒泡排序 等排序算法有多种不同的写法
对特定序列交换次数的不一样的
#4
自学的数学2018-05-22 16:47
编程的时候,请在代码里注明:用什么排序法完成的。
还有就是,如果你和别人用的同一种排序法,但是比别人用的步数少也完成排序的,也可以写下来。
#5
dzy1232018-05-22 17:01

我只会这一种也是45次是巧合?

[此贴子已经被作者于2018-5-22 17:07编辑过]

#6
自学的数学2018-05-22 17:25
你换个思路呢。你把
for(i=0;i<len-1;i++)
     {
       for(j=0;j<len-i-1;j++)
          {
           if(a[j]>a[j+1])
里面的(i=0;i<len-1;i++)改成(i=len-1;i0;i--)
(j=0;j<len-i-1;j++)同理
试试看。
#7
lin51616782018-05-22 17:27
回复 5楼 dzy123
45 是因为 这个序列刚刚好逆序
对应冒泡算法 最差时间复杂度

其他排序比如基数排序
根据选择的基数不一样
排序过程区别很大的
#8
九转星河2018-05-22 18:55
其实说到底排序不一定是数据交换,可能只是简单的赋值,交换一次是3步,每次赋值可以单独算一步~
例如插入,快排和归并就是典型的赋值而并非交换~

还有插入排序查找数据也需要时间~有分线性查找和二分查找,进行赋值也有分直接移动插入和修改结点指针(用数组模拟链表),还有同一种插入排序用链表和用数组的赋值次数是不一样的,正常来说是链表赋值次数比较少,但对链表指针进行运算却是比一般用数组要多一些时间的,所以具体效率就很难说了~

还有补充一下~还有个叫二叉排序树,二叉排序树也有分普通二叉树,平衡二叉树,平衡二叉树也有分AVL树,红黑树,当然还有别的树的~

就算单单从交换的角度来判断,至少还是看不出什么的,可以试试看执行多少条语句,每执行一条语句算一次这样会不会好一点呢~

[此贴子已经被作者于2018-5-22 20:01编辑过]

#9
自学的数学2018-05-22 21:27
用“选择排序法”写好了,排序顺序和冒泡排序顺序不一样,但是交换次数一样。都是45次。
程序代码:
# include "stdio.h"
# define N 10
int main()
{
    int i,j,temp,count,z;
    int a[N]={10,9,8,7,6,5,4,3,2,1};
    printf("这段代码是用选择排序法来统计共交换了多少次。\n ");
    printf("交换前的数据: ");
    for(i=0;i<N;i++)
              printf(" %4d",a[i]);
    printf("\n");
    for(i=0;i<N;i++)
    for(j=i+1;j<N;j++)
    {
        if (a[i]>a[j])
        {
        temp=a[i];
        a[i]=a[j];
        a[j]=temp;
        printf(" 第 %3d 次交换: ",++count);
        for(z=0;z<N;z++)
              printf("%4d ",a[z]);
         printf("\n");
        }
    }
    printf("共交换%4d 次",count);
    printf("\n");
    printf(" 交换后的数据: ");
    for(i=0;i<N;i++)
    printf("%5d",a[i]);
}
#10
自学的数学2018-05-22 21:48
只有本站会员才能查看附件,请 登录
#11
自学的数学2018-05-22 21:53
用“插入排序法”写好了,排序顺序和上面的不一样,但是交换次数一样。都是45次。
程序代码:
#include<stdio.h>
#include<malloc.h>
void InsertSorting(int a[],int len)
{   int count=0;
    for(int i=1;i<len;i++)
    {
        int k=i,z;
        int temp=a[k];
        for(int j=i-1;(j>=0)&&(a[j]>temp);j--)
        {
            a[j+1]=a[j];
            k=j;
        a[k]=temp;
        printf(" 第 %3d 次交换: ",++count);
        for(z=0;z<10;z++)
              printf("%4d ",a[z]);
         printf("\n");
         }
        
    }
    printf("共交换%4d 次",count);
}
int main(int argc, char *argv[])
{
   int a[10]={10,9,8,7,6,5,4,3,2,1};
   int i=0;
   printf("这段代码是用插入排序法来统计共交换了多少次。\n ");
   printf("交换前的数据: ");
    for(i=0;i<10;i++)
              printf(" %4d",a[i]);
    printf("\n");
   InsertSorting(a,10);
   printf(" 交换后的数据: ");
   for(i=0;i<10;i++)
             printf("%3d ",a[i]);
  return 0;
}

效果图:
只有本站会员才能查看附件,请 登录
#12
九转星河2018-05-22 22:08
回复 11楼 自学的数学
编程论坛 - 各种插入排序算法小结

https://bbs.bccn.net/viewthread.php?tid=435801&highlight=%B2%E5%C8%EB%C5%C5%D0%F2

关于插入排序可以看看这个~
#13
自学的数学2018-05-22 22:25
回复 12楼 九转星河
不错的文章,我准备对文中提到的各种排序方法都来试试,比较一番,是不是还是45呢。
#14
lin51616782018-05-22 22:26
回复 8楼 九转星河
插入,快排和归并
都是交换排序的
不知道什么是赋值排序 你想说分配排序??
#15
九转星河2018-05-22 22:28
回复 14楼 lin5161678
其实实质理解成交换排序也没有错,只是交换不是只有t=a;a=b;b=t;这种表达形式,分配额外空间的通常就不是那种形式的了~
#16
自学的数学2018-05-22 22:36
回复 14楼 lin5161678
分配排序分为:桶排序和基数排序。
   桶排序和基数排序均属于分配排序。分配排序的基本思想:排序过程无须比较关键字,而是通过用额外的空间来"分配"和"收集"来实现排序,它们的时间复杂度可达到线性阶:O(n)。简言之就是:用空间换时间,所以性能与基于比较的排序才有数量级的提高!
    桶排序所需要的额外空间取决于关键字的个数,若 array[0..n - 1] 中关键字的取值范围是 0 到 m - 1 的整数,则必须设置 m 个箱子。因此箱排序要求关键字的类型是有限类型,否则可能要无限个箱子。一般情况下每个箱子中存放多少个关键字相同的记录是无法预料的,故箱子的类型应设计成链表为宜。
   基数排序基本思想:基数排序是对桶排序的改进和推广。如果说桶排序是一维的基于桶的排序,那么基数排序就是多维的基于桶的排序。我这么说,可能还不是太清楚。比方说:用桶排序对 [0, 30] 之间的数进行排序,那么需要 31 个桶,分配一次,收集一次,完成排序;那么基数排序则只需要 0 - 9 总共 10 个桶(即关键字为数字 0 - 9),依次进行个位和十位的分配和收集从而完成排序。
#17
lin51616782018-05-22 22:50
回复 15楼 九转星河
既然提到额外空间
那么你应该是想说分配排序了
插入 快排 归并这些不是分配排序
虽然归并有辅助空间
但是这些排序是基于比较 进行排序的
交换操作怎么写倒比较次要
基于比较的排序 和 分配排序 主要区别是
一个排序依赖比较 时间复杂度在理论上的下限是O(n*log(n))
分配排序不依赖比较 时间复杂度可以达到O(n)
#18
xzlxzlxzl2018-05-22 22:51
答主显然没有正真完全吃透各种排序算法,我也没有,但我知道选择法排序交换数据最多只需要交换n-1次数据,像题主这种倒序变顺序只需要交换n/2次数据即可,即交换5次,最糟糕的数据为“a[]={2,3,5,7,9,10,8,6,4,1}”,这样交换9次,代码如下:
程序代码:
#include<stdio.h>
void print(int *a,int c)
{
    int i;
    printf("第%d次:",c);
    for(i=0;i<10;i++)printf("%3d",a[i]);
    printf("\n");
}
void main()
{
    int i,j,k,l=0,a[]={10,9,8,7,6,5,4,3,2,1};
    for(i=0;i<9;i++)
    {
        k=i;
        for(j=i+1;j<10;j++)if(a[k]>a[j])k=j;
        if(k!=i)
        {
            a[k]^=a[i];
            a[i]^=a[k];
            a[k]^=a[i];
            print(a,++l);
        }
    }
}


其实排序效率不仅仅在数据交换上,还有遍历数据集合的效率,有的算法还针对不同的数据结构,比如插入排序在链表排序中效率就很高。

[此贴子已经被作者于2018-5-22 23:00编辑过]

#19
九转星河2018-05-22 22:59
回复 17楼 lin5161678
对哦,可以看比较次数而不是交换次数,这就直接多了~

[此贴子已经被作者于2018-5-22 23:01编辑过]

#20
自学的数学2018-05-23 09:29
回复 19楼 九转星河
你说:"可以看比较次数"。那么就以下面的冒泡排序法为例,如何查看呢??
程序代码:
#include <stdio.h>

 void sort(int *a,int len)

 {
   int i=0;
   int j;
   int t;
   for(i=0;i<len-1;i++)  
     {
       for(j=0;j<len-i-1;j++)
          {
           if(a[j]>a[j+1])
              {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
             }
          }
    }
  }
int main(int argc, char *argv[])
{
   int a[10]={10,9,8,7,6,5,4,3,2,1};
   int i=0;
   sort(a,10);
   for(i=0;i<10;i++)
             printf("%d ",a[i]);
  return 0;
}


[此贴子已经被作者于2018-5-23 09:50编辑过]

#21
九转星河2018-05-23 10:00
回复 20楼 自学的数学
看执行了多少次   if(a[j]>a[j+1])

~
#22
自学的数学2018-05-23 11:08
回复 21楼 九转星河
你这个回答不对,if(a[j]>a[j+1])不是判断了多少次,而是执行了多少次,按照你的意思,如果if(a[j]<a[j+1])就不属于判断了吗?还有如果原始数据本来就是{1,2,3,4,5,6,7,8,9,10};就不用判断了吗?
所以判断次数应该是
for(i=0;i<len-1;i++) 乘以for(j=0;j<len-i-1;j++)。乘出来的结果是多少,好象是90次。
#23
九转星河2018-05-23 11:14
回复 22楼 自学的数学
的确像你所说的,用判断这个词感觉比较合理~
正常来说,其实冒泡排序无论10个数字怎么排列都是90次,这个和本身的算法有关,至于其它排序就不一定了~
#24
自学的数学2018-05-23 11:21
回复 23楼 九转星河
但是怎么把这个判断次数加到代码里面去呢??
#25
lin51616782018-05-23 11:21
回复 23楼 九转星河
统计到某次内层循环没有执行交换就可以break 不一定要跑完全部循环
这里出现次数最多是因为这个数组逆序 刚刚好对应最差时间复杂度
而且不是90次
9+8+7+6+5+4+3+2+1 == 45
每次冒泡 比上一次冒泡减少1
#26
自学的数学2018-05-23 11:38
请用代码说话
#27
九转星河2018-05-23 11:59
回复 25楼 lin5161678
这样可用break可以看看平均能减少多少次也好~
#28
lin51616782018-05-23 13:35
回复 26楼 自学的数学
只有本站会员才能查看附件,请 登录
#29
自学的数学2018-05-23 16:08
不对,还是45次。
见代码:
程序代码:
#include <stdio.h>

 void sort(int *a,int len)

 {
   int i,j,t,z,count=0;
   for(i=0;i<len-1;i++)  
     {
         int mask=1;
       for(j=0;j<len-i-1;j++)
          {
           if(a[j]>a[j+1])
              {
                  mask=0;
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
                printf(" 第 %3d 次交换: ",++count);
                for(z=0;z<10;z++)
                   printf("%4d ",a[z]);
                printf("\n");
             }
          }
          if(mask)
             break;
    }
    printf("共交换%4d 次",count);
      printf("\n");
  }
int main(int argc, char *argv[])
{
   int a[10]={10,9,8,7,6,5,4,3,2,1};
   int i=0;
   //sort(a,10);
   printf("这段代码是用冒泡排序法来统计共交换了多少次。\n ");
    printf("交换前的数据: ");
   for(i=0;i<10;i++)
              printf(" %4d",a[i]);
   printf("\n");
   sort(a,10);
   printf(" 交换后的数据: ");
   for(i=0;i<10;i++)
             printf("%4d ",a[i]);
   return 0;
}
#30
lin51616782018-05-23 16:29
以下是引用自学的数学在2018-5-23 16:08:35的发言:
   int a[10]={10,9,8,7,6,5,4,3,2,1};


我强调了很多次
这里出现次数最多是因为这个数组逆序 刚刚好对应最差时间复杂度


25次排列的数列是
   int a[10]={10,5,3,6,1,9,4,8,2,7};
#31
自学的数学2018-05-23 17:34
回复 28楼 lin5161678
对于 a[10]={10,9,8,7,6,5,4,3,2,1};用你的方法来判断,还是45次,并且代码中的 if(mask) break;根本不起作用,看来对于这个数列,用冒泡法来判断和交换都始终是45次了。
#32
lin51616782018-05-23 20:21
以下是引用自学的数学在2018-5-23 17:34:35的发言:

对于 a[10]={10,9,8,7,6,5,4,3,2,1};用你的方法来判断,还是45次,并且代码中的 if(mask) break;根本不起作用,看来对于这个数列,用冒泡法来判断和交换都始终是45次了。

关于这一点我已经解释过很多次了
原来你一直不理解我为什么要不停强调逆序?
mask只对逆序不起作用
其他情况 都能让交换次数少于45
#33
自学的数学2018-05-23 21:07
回复 32楼 lin5161678
我用 a[10]={11,9,8,12,6,14,15,3,2,1};来验证,得出的判断和交换次数(有if(mask)break;和无 if(mask)break;都一样)是31次,就是说 if(mask)break;没用。
#34
renkejun19422018-05-23 22:21
冒泡排序只有只有41次。

程序代码:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef int E_T;

void sort( E_T *array, int l, int r );

int main( void )
{
    E_T a[ 10 ]={10,9,8,7,6,5,4,3,2,1};
   // srand( ( unsigned long )time( NULL ) );
   

 /*   for( int i = 0; i < 10; ++i )
    {
        a[ i ] = rand() % 1000;
        printf( "%d ",a[ i ] );
    }
*/
    printf( "\n" );
    sort( a, 0, 10 );
    for( int i = 0; i < 10; ++i )
        printf( "%d ", a[ i ] );
    return 0;
}


void sort( E_T *array, int l, int r )
{
    int minix, maxix;
    int R;
    int c;
   
    c=0;

    for( ; l < r; ++l, --r )
    {
        for( minix = l + 1, maxix = r - 2, R = r - 1; minix <= R; ++minix, --maxix )
        {
            if( array[ l ] > array[ minix ] )
            {
                c++;
                E_T temp = array[ l ];
                array[ l ] = array[ minix ];
                array[ minix ] = temp;
            }
            if( array[ R ] < array[ maxix ] )
            {
                c++;
                E_T temp = array[ R ];
                array[ R ] = array[ maxix ];
                array[ maxix ] = temp;
            }
        }
    }
   
    printf("jiao huan %d ci",c);
   
}


[此贴子已经被作者于2018-5-23 22:22编辑过]

#35
lin51616782018-05-23 23:07
回复 33楼 自学的数学
判断次数45
交换次数31
这里我理解错了
除了逆序的确有其他序列是mask也拯救不了的
#36
九转星河2018-05-23 23:32
对于mark有没有效果~

int a[]={1,2,3,4,5,6,7,8,9,0};
就是一个好好的例子~
#37
九转星河2018-05-23 23:48
回复 34楼 renkejun1942
以前我看不出这个程序有什么可以优化的地方,但现在感觉应该是可以减少平均交换次数而起到优化作用吧~
#38
renkejun19422018-05-24 11:15
回复 37楼 九转星河
比如呢?
#39
九转星河2018-05-24 11:17
回复 38楼 renkejun1942
我表示的只是一种猜测,实际上我也好奇为啥这种做法会比一般的冒泡排序要快,还是要请 renkejun1942明示一下才行~
#40
renkejun19422018-05-24 11:28
回复 39楼 九转星河
因为同时从两头开始“冒泡”。

[此贴子已经被作者于2018-5-24 11:45编辑过]

#41
renkejun19422018-05-24 11:30
回复 36楼 九转星河
跳出循环我以前写冒泡的时候就考虑了,只是没实现,因为一个简单的例子就能狠狠的打脸。

{0,2,3,6,7,8,1,4,5,9}
#42
lin51616782018-05-24 11:30
回复 38楼 renkejun1942
程序代码:
void sort( E_T *array, int l, int r )
{
    int minix, maxix;
    int R;
    int c;
   
    c=0;

    for( ; l < r; ++l, --r )
    {
        int mask = 1;
        for( minix = l + 1, maxix = r - 2, R = r - 1; minix <= R; ++minix, --maxix )
        {
            if( array[ l ] > array[ minix ] )
            {
                mask = 0;
                c++;
                E_T temp = array[ l ];
                array[ l ] = array[ minix ];
                array[ minix ] = temp;
            }
            if( array[ R ] < array[ maxix ] )
            {
                mask = 0;
                c++;
                E_T temp = array[ R ];
                array[ R ] = array[ maxix ];
                array[ maxix ] = temp;
            }
        }
        if(mask)
            break;
    }
   
    printf("jiao huan %d ci",c);
   
}

比如万能的 mask

=========================
好吧 不行

[此贴子已经被作者于2018-5-24 11:33编辑过]

#43
renkejun19422018-05-24 11:31
回复 42楼 lin5161678
考虑楼上的例子。
#44
renkejun19422018-05-24 11:34
要跳出循环,我的想法是加一次遍历,但是这样的后果是效率不稳定。
#45
wmf20142018-05-24 11:37
回复 39楼 九转星河
一次扫描同时扫出最大数和最小数,减少了扫描次数。
#46
九转星河2018-05-24 11:46
以下是引用renkejun1942在2018-5-24 11:28:48的发言:

因为同时从两头开始“冒泡”,比较次数并未减少,减少的是循环次数。


以下是引用wmf2014在2018-5-24 11:37:33的发言:

一次扫描同时扫出最大数和最小数,减少了扫描次数。


可以理解成是循环语句跳转需要时间么,就是说从循环结束跳转到开始这样需要一定的时间么,因为这种写法设计到要处理的变量多了,嗯,我只能这样理解了~
#47
renkejun19422018-05-24 18:47
回复 46楼 九转星河
最终修改版。

程序代码:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef int E_T;

void sort( E_T *array, int l, int r );

int main( void )
{
    E_T a[ 10 ] = {9,9,2,3,4,5,6,7,0,0};
  /*  srand( ( unsigned long )time( NULL ) );
   
    for( int i = 0; i < 10; ++i )
    {
        a[ i ] = rand() % 1000;
        printf( "%d ",a[ i ] );
    }
*/
    printf( "\n" );
    sort( a, 0, 10 );
    for( int i = 0; i < 10; ++i )
        printf( "%d ", a[ i ] );
    return 0;
}


void sort( E_T *array, int l, int r )
{
    int minix, maxix;
    int R;
    int c;
    int f;
    int s;
   
    s = 0;
   
    if( l >= r )
        return;
   
   
    for( int ix = l; ix < r -1; ix++ )
    {
        if( array[ ix ] <= array[ ix + 1 ] )
            s++;
        else if( array[ ix ] > array[ix + 1 ] )
        {
            s--;
            if( s > 0 )
                break;
        }
        else
            break;
    }
   
    c=0;
   
    if( s == r - l - 1 )
    {
         printf("jiao huan %d ci\n",c);
        return;
    }
    else if( s == l - r + 1)
    {
        for(r -= 1 ; l < r; l++,r-- )
        {
            c++;
            E_T temp = array[ l ];
            array[ l ] = array[ r ];
            array[ r ] = temp;
        }
    }
    else
    {
        for( ; l < r; ++l, --r )
        {
             f = 0;
            for( minix = l + 1, maxix = r - 2, R = r - 1; minix <= R; ++minix, --maxix )
            {
                if( array[ l ] > array[ minix ] )
                {
                    c++;
                    f = 1;
                    E_T temp = array[ l ];
                    array[ l ] = array[ minix ];
                    array[ minix ] = temp;
                }
                if( array[ R ] < array[ maxix ] )
                {
                    c++;
                    f = 1;
                    E_T temp = array[ R ];
                    array[ R ] = array[ maxix ];
                    array[ maxix ] = temp;
                }
            }
            
            if( f != 0 )
                sort( array, l + 1, r - 1 );
        }
    }
   
    printf("jiao huan %d ci\n",c);
   
}




[此贴子已经被作者于2018-5-24 21:24编辑过]

#48
九转星河2018-05-24 20:09
回复 47楼 renkejun1942
这个就是能够减少比较次数了吧~

如果一次交换后是顺序那么就不用再排了,如果一次交换刚好是逆序,那么就直接把数据倒过来~
但这样减少交换次数却额外增加了比较次数~

    for( int ix = l; ix < r -1; ix++ )
    {
        if( array[ ix ] <= array[ ix + 1 ] )
            s++;   
        else
            break;
    }

这样看看有没有问题~
#49
renkejun19422018-05-24 21:01
回复 48楼 九转星河
这几行代码的目的是判断数组是顺序还是逆序用的。当然也可以再加几行,用来判断顺序或逆序的元素有多少个,到乱序的元素时就跳出来。


在细节上还可以再完善。

[此贴子已经被作者于2018-5-24 21:18编辑过]

#50
renkejun19422018-05-24 21:10
回复 48楼 九转星河
用一次遍历换N次潜逃循环,怎么算都是值得的。
#51
九转星河2018-05-25 12:01
看了一下,这个贴还是很有参考价值的,加精后收藏可以方便看看

冒泡排序renkejun1942这个还挺有参考价值的~
https://bbs.bccn.net/thread-480209-1-1.html
https://bbs.bccn.net/thread-487252-1-1.html
~

[此贴子已经被作者于2018-5-25 12:04编辑过]

12