}
三插入排序
已知一组,一组无序数据b[1]、b[2]、……b[m],需将其变成一个升序数列。
先创建一个变量a。
首先将不b[1]与b[2],如果b[1]大于b[2]则交换位置,否则不变;再比较b[2]与b[3],如果b[3]小于b[2],则将b[3]赋值给a,再将a与b[1]比较,如果a小于b[1];则将b[2],b[1]依次后移;在将a放在b[1]处以此类推,直到排序结束。
初始关键字[4938659776132759]
a b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8]
1-----49 49> 38 65 97 76 13 27 59
38 49 49 ……….
38 38 49 ……….
2-----38 38 49<65 97 76 13 27 59
3-----38 38 49 65 <97 76 13 27 59
4----38 38 49 65 97> 76 13 27 59
76 38 49 65 97 97……..
76 38 49 65 76 97……..
以此类推
void insertSort(Type*arr,longlen)
{
longi=0,j=0;
for(i=1;i {
j=i;
tmpData=arr[j];//tmpData用来储存数据
while(tmpData0)
{
arr[j]=arr[j-1];
j--;
}
arr[j]=tmpData;
}
}
四缩小增量排序(希尔排序)
由希尔在1959年提出,又称希尔排序。
已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。
发现当n不大时,插入排序的效果很好。
首先取一增量d(d增量d(1,3,7,15,31,…,2^k-1)是使用最广泛的增量序列之一.
优点:
快,数据移动少;
缺点:
不稳定,d的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取。
初始:
d=5
49 38 65 97 76 13 27 49 55 44
一趟结果
13 27 49 55 44 49 38 65 97 76
d=3 |----------------------|----------------------|---------------------|
二趟结果
13 44 49 38 27 49 55 65 97 76
d=1
三趟结果
13 27 38 44 49 49 55 65 76 97
#include
usingnamespacestd;
#defineMAX16
voidshell_sort(int*x,intn)
{
inth,j,k,t;
for(h=n/2;h>0;h=h/2)/*控制增量*/
{
for(j=h;j {
t=*(x+j);
for(k=j-h;(k>=0&&t<*(x+k));k-=h)
{
*(x+k+h)=*(x+k);
}
*(x+k+h)=t;
}
}
}
voidmain()
{
int*p,i,a[MAX];
p=a;
cout<<"InputMAXnumberforsorting:
"< for(i=0;i cin>>*p++;
p=a;
shell_sort(p,MAX);
for(p=a,i=0;i {
cout<<*p++< }
cout<}
五、快速排序
快速排序是冒泡排序的改进版,是目前已知的最快的排序方法。
已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。
首先任取数据a[x]作为基准。
比较a[x]与其它数据并排序,使a[x]排在数据的第k位,并且使a[1]~a[k-1]中的每一个数据a[x],然后采用分治的策略分别对a[1]~a[k-1]和a[k+1]~a[n]
两组数据进行快速排序。
优点:
极快,数据移动少;
缺点:
不稳定。
分段插入排序
voidQuickSort(int*pData,intleft,intright)
{
inti,j;
intmiddle,iTemp;
i=left;
j=right;
middle=pData[(left+right)/2];//求中间值
do
{
while((pData[i] i++;
while((pData[j]>middle)&&(j>left))//从右扫描小于中值的数
j--;
if(i<=j)//找到了一对值
{
//交换
iTemp=pData[i];
pData[i]=pData[j];
pData[j]=iTemp;
i++;
j--;
}
}while(i<=j);//如果两边扫描的下标交错,就停止(完成一次)
//当左边部分有值(left if(left QuickSort(pData,left,j);
//当右边部分有值(right>i),递归右半边
if(right>i)
QuickSort(pData,i,right);
}
六归并排序算法
合并排序(MERGESORT)是又一类不同的排序方法,合并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。
它的基本思想就是假设数组A有N个元素,那么可以看成数组A是又N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个N/2 个长度为2或1的有序子序列,再两两合并,如此重复,值得得到一个长度为N的有序数据序列为止,这种排序方法称为2—路合并排序。
例如数组A有7个数据,分别是:
49 38 65 97 76 13 27,那么采用归并排序算法的操作过程如图7所示:
初始值[49] [38] [65] [97] [76] [13] [27]
第一次合并之后 [38 49] [65 97] [13 76] [27]
第二次合并之后 [38 49 65 97] [13 27 76]
第三次合并之后 [13 27 38 49 65 76 97]
合并算法的核心操作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列。
合并算法也可以采用递归算法来实现,形式上较为简单,但实用性很差。
合并算法的合并次数是一个非常重要的量,根据计算当数组中有3到4个元素时,合并次数是2次,当有5到8个元素时,合并次数是3次,当有9到16个元素时,合并次数是4次,按照这一规律,当有N个子序列时可以推断出合并的次数是X(2 >=N,符合此条件的最小那个X)。
其时间复杂度为:
O(nlogn).所需辅助存储空间为:
O(n)
归并排序:
#include
voidmerge(inta[],intp,intq,intr)
{
intn1=q-p+1,n2=r-q,i,j,k;
intl[1002],R[1002];
for(i=1;i<=n1;i++)l[i]=a[p+i-1];
for(j=1;j<=n2;j++)R[j]=a[q+j];
R[n2+1]=l[n1+1]=999999;
i=j=1;
for(k=p;k<=r;k++)
{
if(l[i]<=R[j])
{
a[k]=l[i];
i++;
}
else
{
a[k]=R[j];
j++;
}
}
}
voidmergesort(inta[],intp,intr)
{
intq;
if(p{
q=(p+r)/2;
mergesort(a,p,q);
mergesort(a,q+1,r);
merge(a,p,q,r);
}
}
intmain()
{
inta[1001],t,n,i;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
mergesort(a,1,n);
for(i=1;i<=n;i++)
{
printf("%d",a[i]);
if(i!
=n)printf("");
}
printf("/n");
}
return0;
}
七堆排序
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。
n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
(1)ki≤K2i且ki≤K2i+1或
(2)Ki≥K2i且ki≥K2i+1(1≤i≤
--[if!
vml]-->
--[endif]-->)
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
①先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
②再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。
然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
直到无序区只有一个元素为止。
(2)大根堆排序算法的基本操作:
①初始化操作:
将R[1..n]构造为初始堆;
②每一趟排序的基本操作:
将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。
注意:
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。
堆排序和直接选择排序相反:
在任何时刻,堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。
3.具体算法
template
heapsort(Tr[],intn) //n为文件的实际记录数,r[0]没有使用
{inti,m;nodex;
for(i=/2;i>=1;i--)heappass(r,i,n); //初建堆
//以下for语句为输出堆顶元素、调整堆操作
for(m=n-1;m>=1;m--)//逻辑堆尾下标m不断变小
{cout< x=r[1];r[1]=r[m+1];r[m+1]=x; //堆顶与堆尾元素对换
heappass(r,1,m);//恢复堆
}
cout< }//heapsort
4.直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。
事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作.堆排序可通过树形结构保存部分比较结果,可减少比较次数。
八基数排序
箱排序(BinSort)
1、箱排序的基本思想
箱排序也称桶排序(BucketSort),其基本思想是:
设置若干个箱子,依次扫描待排序的记录R[0],R[1],…,R[n-1],把关键字等于k的记录全都装入到第k个箱子里(分配),然后按序号依次将各非空的箱子首尾连接起来(收集)。
【例】要将一副混洗的52张扑克牌按点数A<2<…2、箱排序中,箱子的个数取决于关键字的取值范围。
若R[0..n-1]中关键字的取值范围是0到m-1的整数,则必须设置m个箱子。
因此箱排序要求关键字的类型是有限类型,否则可能要无限个箱子。
箱排序实用价值不大,仅适用于作为基数排序的一个中间步骤。
桶排序
箱排序的变种。
为了区别于上述的箱排序,姑且称它为桶排序(实际上箱排序和桶排序是同义词)。
1、桶排序基本思想
桶排序的思想是把[0,1)划分为n个大小相同的子区间,每一子区间是一个桶。
然后将n个记录分配到各个桶中。
因为关键字序列是均匀分布在[0,1)上的,所以一般不会有很多个记录落入同一个桶中。
由于同一桶中的记录其关键字不尽相同,所以必须采用关键字比较的排序方法(通常用插入排序)对各个桶进行排序,然后依次将各非空桶中的记录连接(收集)起来即可。
注意:
这种排序思想基于以下假设:
假设输入的n个关键字序列是随机分布在区间[0,1)之上。
若关键字序列的取值范围不是该区间,只要其取值均非负,我们总能将所有关键字除以某一合适的数,将关键字映射到该区间上。
但要保证映射后的关键字是均匀分布在[0,1)上的。
基数排序
基数排序的基本思想是:
从低位到高位依次对Kj(j=d-1,d-2,…,0)进行箱排序。
在d趟箱排序中,所需的箱子数就是基数rd,这就是"基数排序"名称的由来。
假设原来有一串数值如下所示:
73,22,93,43,55,14,28,65,39,81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0
181
222
3739343
414
55565
6
7
828
939
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81,22,73,93,43,14,55,65,28,39
接着再进行一次分配,这次是根据十位数来分配:
0
114
22228
339
443
555
665
773
881
993
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14,22,28,39,43,55,65,73,81,93
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好,MSD的方式恰与LSD相反,是由高位数为基底开始进行分配,其他的演算方式则都相同。
实作
*C
#include
#include
intmain(void){
intdata[10]={73,22,93,43,55,14,28,65,39