关于排序(一)

   由于待排序的数据大小不一样,数据存储的位置就不一样,就产生了两种不同的排序分类.一 内部排序,也就可以将数据加载到内存中进行排序,二外部排序,也就是要排序的数据比较大,内存一次不能完全加载,在排序过程中要访问外存(通常指文件)的排序.

  内部排序有很多种类.按照排序过程的不同可以分为"插入排序,交换排序,选择排序,归并排序,和计数排序.如果按照工作了的不同,可以分为简单排序(o(n^2).先进排序O(nlogn).基数排序.(O(d.n).

  排序通常做两件事1比较大小 2交换位置

  待排序的记录序列一般有三种存储方式.一种就是记录在连续的存储单元上,类似于线性表的顺序存储结构,在序列中相邻的记录入R  R+1都是连续存储的.中间没有间隔的数据.

   一种是链表排序,数据存放在静态链表中,数据可能不是连续存储的,我们在排序时,只需移动指向它们的指针的指向.一种是地址排序,数据本身存储在连续的存储单元中,和第一种不同的是,同时设定一下地址向量来记录他们的地址.排序时先按照值对地址进行排序,然后再调整地址的值,一般指通过指针进行间接的排序.

约定数据机构:

1 快速排序算法分析与实现

     快速排序是对冒泡排序的一个改进.它采用分而治之的思想.通过一趟排序将待排记录分割成两部分.其中一部分的数据都大于另一个部分的数据

这个分割点就是枢轴(分割点)

     假设待排序的序列为(L.r[s],L.r[s+1],......L.r[t])

通常会选择第一个数据L.r[s]为支点.支点的选用很重要,后面会讨论选取支点,对快速排序进行优化.

分割点L.r[s]已经有了,下面就是把数据以L.r[s]为界限,左右的数据都小于它,右边的值都大于它.由于排序其实就是比较交换的过程.所以肯定是需要将它与其余的数据进行比较.左边比他大的就换到右边来.右边比他小的就换到左边来.

   具体的实现,利用两个指针low,high,他们的初始值为low,high,设支点记录为pivotkey.首先从high指向的位置向前找到第一个关键字(一般指数值或字符串,由于不确定比较的类型所以用关键字代替).小于支点pivotkey的值,然后交换位置.然后从low指向的位置,往后搜索.找到第一个大于支点(pivotKey)的关键字,然后交换位置.然后再从后往前,然后从前往后,循环交替搜索交换.直到high=low,也就相当于遍历了一次.

算法描述如下:

    int Partition(Sqlist&L,int low,int high) /*此处的参数Sqlist包待排序的序列的需要比较的数据.L指的数据比较的特性如正型数字的长度,字符串的长度等,low为指向序列的最左边(不等同于数组的开头)的指针,high为指向序列最右边的指针.

      {        /*功能:交换顺序表L中子表L.r[low,high]的记录,是支点达到分割点.并返回支点的位置,返回支点位置的作用:为下趟排序确定low high指定位置*/

       pivotkey = L.r[low].key;   /*未排序之前,low指向存储数据的数组的第一个记录一般以第一个记录作为支点*/

      while( low < high){   /*根据low和high是否相等作为条件进行遍历*/

             while(low < high && L.r[high].key >= pivotkey)    --high  /*第一步从后面往前搜索,如果 L.r[high].key >= pivotkey也就是大于支点的记录位置不变,直到遇到第一个小于支点的记录*/

              L.r[low]<-->L.r[high]/*将遇到的小于支点的记录交换到低端,是通过和支点进行交换位置进行实现*/

              /*第二步从前往后搜索,如果L.r[low].key <=pivotkey跳过,直到遇到第一个大于支点的记录*/

             while(low < high && L.r[low].key <= pivotkey)

                ++low/*通过移动指针的指向,遍历--high一样*/

            L.r[low]<-->L.r[high]/* 将遇到的大于支点的记录交换到高端,也是通过和支点交换位置进行实现*/

           }

          return low;/*或则retun high,因为此时low和high相遇两则指向同一位置.*/

}//Partition

 

具体实现上述算法时,需要进行3次移动操作,实际上排序过程中对支点进行赋值是多余的,因为只有在排序结束后才能确定支点的位置,所以可以将支点放在r[0]位置上不动

int partition( sqlist&L,int low,int high){

/* 功能:交换顺序表L中子表L.r[low,high]的记录,是支点达到分割点.并返回支点的位置,返回支点位置的作用:为下趟排序确定low high指定位置*/

    L.r[0] = L.r(low) /* 用表的第一个记录作分割支点,将分割点保存在表头*/

   pivotkey = L.r[low].key  /*给支点赋值*/

while (low < high){

 while(low < high && L.r[high] >= pivotkey)

       high --;

   L.r[ow] = L.r[high]; /*小于支点的记录移动到低端*/

while (low < high && L.r[low] <= pivotke)

   ++ low;

  L.r[high] = L.r[row];

}

 l.r[row] = l.r[0];/*将支点放到分割点*/

return l.r[low];

}

下面为一个实例.

49       38       65       97     76      13      27 

low                                                            high

首先将49作为第一次排序的分割点

注意Low high的位置

第一次交换从High开始27 < 49 所以交换位置

27     38    65     97    76     13   49

low                                                high

第二次从low开始搜索27跳过low+1,38跳过low+1+1 65> 49 交换

27   38  49   97  76   13  65

              low                       High

第三次13< 49 high指向49位置

27 38 13 97 76 49 65

第四次此时low 指向13,搜索前+! 97 > 49 交换

27 38 13 49 76 97  65

                第五次时 从76开始搜索,知道49位置没有找到,此时Low和High相同

第一躺结束,此时以49为分割点两部分,前面为小的部分(27 38 13),后面我I大的部分(76 97 65)

然后low-1即49前面一个Key(13)为high 27为low    大部分:low+1即76为low,65为high

分别递归调用如

13 38 27 --  13 27 38

65 97 76-- 65 76 97

所以此时序列已排好

递归形式的算法如下:

void Qsort(Sqlist&L,int low,int high)
{    /*对顺序表L的子序列进行快速排序*/

   if(low < high){

   pivotoc = parttion(L,low,high) /* 将L.r[low,high] 一份为二  第一躺排序的结果*/

Qsort(L,low ,pivotoc-1);/pivotoc为分割点,对小的部分进行递归调用排序*/

Qsort(L. pivotoc + 1,high) /* 对大的部分进行排序*/

}

}//Qsort

void QuickSort(sqList &L) {

/*对顺序表L进行快速排序*/

Qsort(L,1,L.length);

}//

 

关于快速排序,举个变化的例子.

此处以数组下标的形式实现.

/*qsort: sort v[left] ...v[right ]以递增顺序排序*/

void qsort(int v[],int left,int right)

{

     int i, last;

     void swap(int v[],int i, int j);/*交换位置函数*/

     if(left  >=  right)  /*说明数组元素少于两个,不作排序*/

          return;

      swap(v,left ,(left  + right)/2);  /*划分子集的元素*/

        last = left; /* 将支点移动到V[0]*/

        for(i = left +1; i <= right; i++)/*划分子集*/

             if( v[i] <  v[left])

                    swap(v,++last,i);/

             swap(v, left , last);  /*恢复子集*/

            qsort(v, left, last-1);

            qsort(v,last + 1; right);

}

/*swap:交换数组元素的位置*/

void swap(int v[],int i, int j)

{

   int temp;

   temp = v[i];

   v[i] = v[j];

  v[j] = temp;

}

     为了更好地理解还是拿具体的数字描述一下排序的过程.

49   38    65  97   76   13   27   

第一步找出支点(97)

然后和排头对调:97 38  65  49  76  13 27

97后面的数字都小于97 位置不动,虽然没动但,其实已经调用了6次swap函数,last已移动到27 交换97 27位置

第一趟排序结束:

小的部分27 38 65 49 76 13  大的部分 97

对27 38 65 49 76 13 排序  大的部分少于2个不作排序

找出支点49

49 38 65  27 76 13

1 .38 <49 last[38]i[38]交换  49 38 65 27 76 13

2.65 >49 不调用swap记住此时last在38 i++继续搜索

3 27 <49 调用swap ++last[65] last[65] i[27]交换  49 38 27 65  76 13 i++继续搜索

4  76 >49 不调用swap记住此时last在27 i++继续搜索

5 13 < 49 调用swap ++last[65]i[13]交换  49 38 27  13 76 65 交换v[left[49]] v[last] [13]

第二趟结束:

13  38 27 49 76  65 97 

小的部分:13 38 27   大的部分76 65 

小的部分排序:支点38 

38 27 13   

27 < 38 last[27] ;[i][27]调用swap i[27]last [27]交换

13 < 38 last i[13] ++last[13]调用swap交换 

交换v[left ][38]last[13]

大的部分 选取65 交换位置 65 76 

76 > 65

此时 last = left 交换后仍未65 76  

第三趟结束后 分为

13 27       38        49       65       76     97 

此时13 27 38  分为大的部分38 少于2个不作排序 小的部分 13 27

与 76 65 类似 

此时 排序结束

13    27   38   49   65  76   97  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值