内部排序算法分析

  内部排序算法比较
[FONT]内部排序算法比较

[原创] 骑兵 2003-09-07
  排序算法是数据结构学科经典的内容,其中内部排序现有的算法有很多种,究竟各有什么特点呢?本文力图设计实现常用内部排序算法并进行比较。分别为起泡排序,直接插入排序,简单选择排序,快速排序,堆排序,针对关键字的比较次数和移动次数进行测试比较.

问题分析和总体设计

ADT OrderableList{
           数据对象:D={ai| ai∈IntegerSet,i=1,2,…,n,n≥0}
           数据关系:R1={〈ai-1,ai〉|ai-1, ai∈D, i=1,2,…,n}
           基本操作:
InitList(n)
  操作结果:构造一个长度为n,元素值依次为1,2,…,n的有序表。
Randomizel(d,isInverseOrser)
  操作结果:随机打乱
BubbleSort( )
  操作结果:进行起泡排序
InserSort( )
  操作结果:进行插入排序
SelectSort( )
  操作结果:进行选择排序
QuickSort( )
  操作结果:进行快速排序
HeapSort( )
  操作结果:进行堆排序
ListTraverse(visit( ))
  操作结果:依次对L种的每个元素调用函数visit( )
         }ADT  OrderableList
待排序表的元素的关键字为整数.用正序,逆序和不同乱序程度的不同数据做测试比较,
对关键字的比较次数和移动次数(关键字交换计为3次移动)进行测试比较.
要求显示提示信息,用户由键盘输入待排序表的表长(100-1000)和不同测试数据的组数(8-18).每次测试完毕,要求列表现是比较结果.
要求对结果进行分析.

详细设计
1、起泡排序
算法:核心思想是扫描数据清单,寻找出现乱序的两个相邻的项目。当找到这两个项目后,交换项目的位置然后继续扫描。重复上面的操作直到所有的项目都按顺序排好

bubblesort(struct rec r[],int n)
{
int i,j;
struct rec w;
unsigned long int compare=0,move=0;
for(i=1;i<=n-1;i++)
  for(j=n;j>=i+1;j--)
   {
    if(r[j].key<r[j-1].key)
     {
     w=r[j];
     r[j]=r[j-1];
     r[j-1]=w;
     move=move+3;
     }
    compare++;
   }
printf("/nBubbleSort compare= %ld,move= %ld/n",compare,move);
}

2、直接插入排序
基本思想
     假设待排序的记录存放在数组R[1..n]中。初始时,R[1]自成1个有序区,无序区为R[2..n]。从i=2起直至i=n为止,依次将R[i]插入当前的有序区R[1..i-1]中,生成含n个记录的有序区。

第i-1趟直接插入排序:
     通常将一个记录R[i](i=2,3,…,n-1)插入到当前的有序区,使得插入后仍保证该区间里的记录是按关键字有序的操作称第i-1趟直接插入排序。
     排序过程的某一中间时刻,R被划分成两个子区间R[1..i-1](已排好序的有序区)和R[i..n](当前未排序的部分,可称无序区)。
     直接插入排序的基本操作是将当前无序区的第1个记录R[i]插人到有序区R[1..i-1]中适当的位置上,使R[1..i]变为新的有序区。因为这种方法每次使有序区增加1个记录,通常称增量法。
     插入排序与打扑克时整理手上的牌非常类似。摸来的第1张牌无须整理,此后每次从桌上的牌(无序区)中摸最上面的1张并插入左手的牌(有序区)中正确的位置上。为了找到这个正确的位置,须自左向右(或自右向左)将摸来的牌与左手中已有的牌逐一比较。

一趟直接插入排序方法

1.简单方法
     首先在当前有序区R[1..i-1]中查找R[i]的正确插入位置k(1≤k≤i-1);然后将R[k..i-1]中的记录均后移一个位置,腾出k位置上的空间插入R[i]。
  注意:
     若R[i]的关键字大于等于R[1..i-1]中所有记录的关键字,则R[i]就是插入原位置。

2.改进的方法
  一种查找比较操作和记录移动操作交替地进行的方法。
具体做法:
     将待插入记录R[i]的关键字从右向左依次与有序区中记录R[j](j=i-1,i-2,…,1)的关键字进行比较:
     ① 若R[j]的关键字大于R[i]的关键字,则将R[j]后移一个位置;
      ②若R[j]的关键字小于或等于R[i]的关键字,则查找过程结束,j+1即为R[i]的插入位置。
     关键字比R[i]的关键字大的记录均已后移,所以j+1的位置已经腾空,只要将R[i]直接插入此位置即可完成一趟直接插入排序。

直接插入排序算法

1.算法描述
  void lnsertSort(SeqList R)
   { //对顺序表R中的记录R[1..n]按递增序进行插入排序
    int i,j;
    for(i=2;i<=n;i++) //依次插入R[2],…,R[n]
      if(R[i].key<R[i-1].key){//若R[i].key大于等于有序区中所有的keys,则R[i]
                              //应在原有位置上
        R[0]=R[i];j=i-1; //R[0]是哨兵,且是R[i]的副本
        do{ //从右向左在有序区R[1..i-1]中查找R[i]的插入位置
         R[j+1]=R[j]; //将关键字大于R[i].key的记录后移
         j-- ;
         }while(R[0].key<R[j].key); //当R[i].key≥R[j].key时终止
        R[j+1]=R[0]; //R[i]插入到正确的位置上
       }//endif
   }//InsertSort

3、简单选择排序
算法:首先找到数据清单中的最小的数据,然后将这个数据同第一个数据交换位置;接下来找第二小的数据,再将其同第二个数据交换位置,以此类推。

selectsort(struct rec r[],int n)
{
unsigned long int compare=0,move=0;
int i,j,k;
struct rec w;
for(i=1;i<=n-1;i++)
{      k=i;
     for(j=i+1;j<=n;j++)

     { if(r[j].key>r[k].key) {k=j; compare++; }
     w=r
;
     r
=r[k];
     r[k]=w;
     move=move+3;

     }
}
printf("/nSelectSort compare= %ld,move= %ld/n",compare,move);
}

4、快速排序
算法:首先检查数据列表中的数据数,如果小于两个,则直接退出程序。如果有超过两个以上的数据,就选择一个分割点将数据分成两个部分,小于分割点的数据放在一组,其余的放在另一组,然后分别对两组数据排序。
通常分割点的数据是随机选取的。这样无论你的数据是否已被排列过,你所分割成的两个字列表的大小是差不多的。而只要两个子列表的大小差不多


q(struct rec r[],int s,int t)
{
int i=s,j=t;
if(s<t)
{
  r[0]=r[s];    ++a;  c++;
  do{
while(j>i&&r[j].key>=r[0].key)
   {j--;
    ++a; }
if(i<j)
{ r
=r[j];
   i++;
   c++; }
while(i<j&&r
.key<=r[0].key)
   {i++;
    ++a; }
if(i<j)
   { r[j]=r
;
     j--;
     c++; }
    } while(i<j);
r
=r[0];
c++;
q(r,s,j-1);
q(r,j+1,t);
}
}

5. 堆排序
(1) 基本思想:
  堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
(2) . 堆的定义: N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:
       Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])


sift(struct rec r[],int l,int m)
{
int i,j;
struct rec w;
i=l; j=2*i;
w=r
;
while(j<=m)
{
  if(j<m&&r[j].key<r[j+1].key)  { j++;
  }
  if(w.key<r[j].key)
  {
   r
=r[j];
   i=j;
   j=2*i;
   }
  else j=m+1;
  }
  r
=w;
}

heapsort(struct rec r[],int n)
{
  unsigned long int compare=-1,move=-1;
  struct rec w;
  int i;
  int a;
  for(i=n/2;i>=1;i--) a=sift(r,i,n);
  compare++;
  move++;

  for(i=n;i>=2;i--)
  {
    w=r
;
    r
=r[1];
    r[1]=w;
    a=sift(r,1,i-1);
    compare+=a;
    move+=a;
   }
}

小结:
    1.学会使用随机函数randomize( ) 为数组赋初值要在头文件中添加#include
    2.在做此程序之前基本上是在理解了各种排序过程以后完成的
    3.对排序算法的总结:
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
     当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
     快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
     堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值