[数据结构]排序

第八章 排序

·排序定义——将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序

              列叫~

·排序分类

  按待排序记录所在位置

   内部排序:待排序记录存放在内存

   外部排序:排序过程中需对外存进行访问的排序

  按排序依据原则

   插入排序:直接插入排序、折半插入排序、希尔排序

   交换排序:冒泡排序、快速排序

   选择排序:简单选择排序、堆排序

   归并排序:2-路归并排序

   基数排序

  按排序所需工作量

   简单的排序方法:T(n)=O(n²)

   先进的排序方法:T(n)=O(logn)

   基数排序:T(n)=O(d.n)

·排序基本操作

  比较两个关键字大小

  将记录从一个位置移动到另一个位置

8.1 插入排序

直接插入排序

排序过程:整个排序过程为n-1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序

算法描述

typedef struct

{  int key;

   float info;

}JD;

 

void straisort(JD r[],int n)

{  int i,j;

   for(i=2;i<=n;i++)

   {  r[0]=r[i];

      j=i-1;

      while(r[0].key<r[j].key)

      {  r[j+1]=r[j];

         j--;

      }

      r[j+1]=r[0];

   }

}

 

算法评价

  时间复杂度

    若待排序记录按关键字从小到大排列(正序)

关键字比较次数:

记录移动次数:

若待排序记录按关键字从大到小排列(逆序)

关键字比较次数:

记录移动次数:

若待排序记录是随机的,取平均值

关键字比较次数:                              T(n)=O(n²)

记录移动次数:

空间复杂度:S(n)=O(1)

折半插入排序

排序过程:用折半查找方法确定插入位置的排序叫~

 

算法描述

void binsort(JD r[],int n)

{  int i,j,x,s,m,k;

   for(i=2;i<=n;i++)

   {  r[0]=r[i];

      x=r[i].key;

      s=1; j=i-1;

      while(s<=j)

      {  m=(s+j)/2;

         if(x<r[m].key)  j=m-1;

         else s=m+1;

      }

      for(k=i-1;k>=s;k--)

         r[k+1]=r[k];

      r[s]=r[0];

   }

}

算法评价

时间复杂度:T(n)=O(n²)

空间复杂度:S(n)=O(1)

希尔排序(缩小增量法)

排序过程:先取一个正整数d1<n,把所有相隔d1的记录放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止

 

算法描述

void shellsort(JD r[],int n,int d[T])

{  int i,j,k;

   JD x;

   k=0;

   while(k<T)

   {  for(i=d[k]+1;i<=n;i++)

      {  x=r[i];

         j=i-d[k];

         while((j>0)&&(x.key<r[j].key))

         {  r[j+d[k]]=r[j];

            j=j-d[k];

         }

         r[j+d[k]]=x;

       }

       k++;

    }

}

#define   T  3

int   d[]={5,3,1};

 

希尔排序特点

 · 子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列

 · 希尔排序可提高排序速度,因为

    分组后n值减小,n²更小,而T(n)=O(n²),所以T(n)从总体上看是减小了

关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有

·增量序列取法

    无除1以外的公因子

最后一个增量值必须为1

 

8.2 交换排序

·冒泡排序

   排序过程

     将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序r[1].key>r[2].key,

     则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1个记录和第n个

     记录比较为止——第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上

     对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记

     录位置

     重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止

 

算法描述

void bubble_sort(JD r[],int n)

{  int m,i,j,flag=1;

   JD x;

   m=n-1;

   while((m>0)&&(flag==1))

   {  flag=0;

      for(j=1;j<=m;j++)

        if(r[j].key>r[j+1].key)

        {  flag=1;

           x=r[j];

           r[j]=r[j+1];

           r[j+1]=x;

        }

      m--;

    }

}

算法评价

  时间复杂度

     最好情况(正序)

     比较次数:n-1

     移动次数:0

     最坏情况(逆序)

     比较次数:

     移动次数:

T(n)=O(n²)

空间复杂度:S(n)=O(1)

快速排序

基本思想:通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序

排序过程:对r[s……t]中记录进行一趟快速排序,附设两个指针i和j,设枢轴记录rp=r[s],x=rp.key

·初始时令i=s,j=t

·首先从j所指位置向前搜索第一个关键字小于x的记录,并和rp交换

·再从i所指位置起向后搜索,找到第一个关键字大于x的记录,和rp交换

·重复上述两步,直至i==j为止

·再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止

 

算法描述

void qksort(JD r[],int t,int w)

{  int i,j,k;

   JD x;

   if(t>=w)  return;

   i=t; j=w; x=r[i];

   while(i<j)

   {  while((i<j)&&(r[j].key>=x.key))  j--;

      if(i<j) { r[i]=r[j]; i++; }

      while((i<j)&&(r[i].key<=x.key))  i++;

      if(i<j) { r[j]=r[i]; j--; }

   }

   r[i]=x;

   qksort(r,t,j-1);

   qksort(r,j+1,w);

}

算法评价

时间复杂度

最好情况(每次总是选到中间值作枢轴)T(n)=O(nlog2n)

最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n²)

T(n)=O(n²)

空间复杂度:需栈空间以实现递归

最坏情况:S(n)=O(n)

一般情况:S(n)=O(log2n)

8.3  选择排序

  简单选择排序

    排序过程

    ·首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记

     录交换

·再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记

 录交换

    ·重复上述操作,共进行n-1趟排序后,排序结束

 

算法描述

void smp_selesort(JD r[],int n)

{  int i,j,k;

   JD x;

   for(i=1;i<n;i++)

   {  k=i;

      for(j=i+1;j<=n;j++)

         if(r[j].key<r[k].key)  k=j;

      if(i!=k)

      {  x=r[i];

         r[i]=r[k];

         r[k]=x;

      }

   }

}

算法评价

时间复杂度

 记录移动次数

   最好情况:0

   最坏情况:3(n-1)

比较次数:

T(n)=O(n²)

空间复杂度:S(n)=O(1)

堆排序

堆的定义:n个元素的序列(k1,k2,……kn),当且仅当满足下列关系时,称之为堆

 

 

堆排序:将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值;重复执行,得到一个有序序列,这个过程叫~

堆排序需解决的两个问题:

如何由一个无序序列建成一个堆?

如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?

第二个问题解决方法——筛选

方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”

 

 

 

算法描述

int sift(JD r[],int k,int m)

{  int i,j;

   JD x;

   i=k;  x=r[i]; j=2*i;

   while(j<=m)

   {  if((j<m)&&{r[j].key>r[j+1].key))  j++;

      if(x.key>r[j].key)

      {  r[i]=r[j];

         i=j;

         j*=2;

      }

      else   j=m+1;

   }

   r[i]=x;

}

第一个问题解决方法

方法:从无序序列的第ën/2û个元素(即此无序序列对应的完全二叉树的最后一个非终端

      结点)起,至第一个元素止,进行反复筛选

 

算法描述

void heapsort(JD r[],int n)

{  int i;

   JD x;

   for(i=n/2;i>=1;i--)

      sift(r,i,n);

   for(i=n;i>=2;i--)

   {  x=r[1];

      r[1]=r[i];

      r[i]=x;

      sift(r,1,i-1);

   }

}

int sift(JD r[],int k,int m)

{  int i,j;

   JD x;

   i=k;  x=r[i]; j=2*i;

   while(j<=m)

   {  if((j<m)&&{r[j].key>r[j+1].key))  j++;

      if(x.key>r[j].key)

      {  r[i]=r[j];

         i=j;

         j*=2;

      }

      else   j=m+1;

   }

   r[i]=x;

}

算法评价

时间复杂度:最坏情况下T(n)=O(nlogn)

空间复杂度:S(n)=O(1)

 

8.4  归并排序

归并——将两个或两个以上的有序表组合成一个新的有序表,叫

2-路归并排序

排序过程

·设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1

·两两合并,得到ën/2û个长度为2或1的有序子序列

·再两两合并,……如此重复,直至得到一个长度为n的有序序列为止

 

算法描述

void mergesort(JD r[],int n)

{  int i,s=1;

   JD t[M];

   while(s<n)

   {  tgb(s,n,r,t);

      s*=2;

      if(s<n) { tgb(s,n,t,r); s*=2; }

      else  {  i=1;

               while(i<=n)  r[i]=t[i++];

            }

   }

}

 

void tgb(int s,int n,JD r[],JD t[])

{  int i=1;

   while(i<=(n-2*s+1))

   {  merge(r,i,i+s-1,i+2*s-1,t);

      i=i+2*s;

   }

   if(i<(n-s+1))  merge(r,i,i+s-1,n,t);

   else   

      while(i<=n)  t[i]=r[i++];

}

算法评价

时间复杂度:T(n)=O(nlog2n)

空间复杂度:S(n)=O(n)

 

8.5  基数排序

多关键字排序方法

·最高位优先法(MSD):先对最高位关键字k1(如花色)排序,将序列分成若干子序列,每个子序列有相同的k1值;然后让每个子序列对次关键字k2(如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字kd排序;最后将所有子序列依次连接在一起成为一个有序序列

·最低位优先法(LSD):从最低位关键字kd起进行排序,然后再对高一位的关键字排序,……依次重复,直至对最高位关键字k1排序后,便成为一个有序序列

MSD与LSD不同特点

·按MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序

·按LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序

链式基数排序

·基数排序:借助“分配”和“收集”对单逻辑关键字进行排序的一种方法

·链式基数排序:用链表作存储结构的基数排序

链式基数排序步骤

·设置10个队列,f[i]和e[i]分别为第i个队列的头指针和尾指针

·第一趟分配对最低位关键字(个位)进行,改变记录的指针值,将链表中记录分配至10个链队列中,每个队列记录的关键字的个位相同

·第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的队头记录,重新将10个队列链成一个链表

·重复上述两步,进行第二趟、第三趟分配和收集,分别对十位、百位进行,最后得到一个有序序列

 

 

 

算法描述

#define D 3

typedef struct node

{  int key;

   float info;

   int link;

}JD;

 

int radixsort(JD r[],int n)

{  int i,j,k,t,p,rd,rg,f[10],e[10];

   for(i=1;i<n;i++)  r[i].link=i+1;

   r[n].link=0;

   p=1; rd=1; rg=10;

   for(i=1;i<=D;i++)

  {  for(j=0;j<10;j++)

     {  f[j]=0;

        e[j]=0;

     }

     do{

          k=(r[p].key%rg)/rd;

          if(f[k]==0)

              f[k]=p;

          else

              r[e[k]].link=p;

          e[k]=p;

          p=r[p].link;

       }while(p>0);

     j=0;

     while(f[j]==0) j++;

     p=f[j];  t=e[j];

     for(k=j+1;k<10;k++)

        if(f[k]>0)

        {  r[t].link=f[k];

           t=e[k];

        }

     r[t].link=0;

     rg*=10;

     rd*=10;

  }

   return(p);

}

算法评价

时间复杂度:

分配:T(n)=O(n)

收集:T(n)=O(rd)

T(n)=O(d(n+rd))

其中:n——记录数

      d——关键字数

      rd——关键字取值范围

空间复杂度:S(n)=2rd个队列指针+n个指针域空间

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值