由于待排序的数据大小不一样,数据存储的位置就不一样,就产生了两种不同的排序分类.一 内部排序,也就可以将数据加载到内存中进行排序,二外部排序,也就是要排序的数据比较大,内存一次不能完全加载,在排序过程中要访问外存(通常指文件)的排序.
内部排序有很多种类.按照排序过程的不同可以分为"插入排序,交换排序,选择排序,归并排序,和计数排序.如果按照工作了的不同,可以分为简单排序(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