一、冒泡算法
[算法思想]:将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
#include<iostream>
using namespace std;
void BubbleSort(int R[],int n)//R(0..n-1)是待排序的文件,采用自下向上扫描,对R做冒泡排序
{
int i,j;
bool flag; //交换标志
for(i=0;i<n-1;i++) //外循环控制循环趟数,最多做n-1趟排序
{
flag=false;
int temp;
for(j=n-1;j>i;j--) //对当前无序区R[i..n-1]自下向上扫描
if(R[j]<R[j-1])
{
temp=R[j];
R[j]=R[j-1];
R[j-1]=temp;
flag=true;
}
if(!flag) return;//本趟排序未发生交换,提前终止算法
}
}
int main()
{
int a[10]={5,8,4,78,12,96,1,4,16,50};
int i;
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
BubbleSort(a,10);
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
system("pause");
return 0;
}
[算法思想]:将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据重气泡不能在轻气泡之上的原则,从上往下扫描数组R,凡扫描到违反本原则的重气泡,就使其向下"下沉"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
#include<iostream>
using namespace std;
void BubbleSort(int R[],int n)//R(0..n-1)是待排序的文件,采用自下向上扫描,对R做冒泡排序
{
int i,j;
bool flag; //交换标志
for(i=0;i<n-1;i++) //外循环控制循环趟数,最多做n-1趟排序
{
flag=false;
int temp;
for(j=0;j<n-i-1;j++) //对当前无序区R[i..n-1]自上向下扫描
if(R[j]>R[j+1])
{
temp=R[j];
R[j]=R[j+1];
R[j+1]=temp;
flag=true;
}
if(!flag) return;//本趟排序未发生交换,提前终止算法
}
}
int main()
{
int a[10]={5,8,4,78,12,96,1,4,16,50};
int i;
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
BubbleSort(a,10);
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
system("pause");
return 0;
}
注意:冒泡排序的空间复杂度为常量O(1),表示只需要一个辅助的temp存储单元来实现交换数据,即冒泡排序为就地排序。
冒泡排序的时间复杂度最好为O(n),最坏为O(n^2);
二、鸡尾酒算法(双向冒泡算法)
[算法思想]:数组中的数字本是无规律的排放,先找到最小的数字,把他放到第一位,然后找到最大的数字放到最后一位。然后再找到第二小的数字放到第二位,再找到第二大的数字放到倒数第二位。以此类推,直到完成排序
#include<iostream>
using namespace std;
void CocktailSort(int R[],int n)
{
int i,j,tail;
tail=n-1;
bool flag; //交换标志
for(i=0;i<tail;)
{
flag=false;
int temp;
for(j=tail;j>i;j--)//第一轮,先将最小的数据排到前面
if(R[j]<R[j-1])
{
temp=R[j];
R[j]=R[j-1];
R[j-1]=temp;
flag=true;
}
i++;//原来i处数据已排好序,加1
for(j=i;j<tail;j++) //第二轮,将最大的数据排到后面
if(R[j]>R[j+1])
{
temp=R[j];
R[j]=R[j+1];
R[j+1]=temp;
flag=true;
}
tail--;//原tail处数据也已排好序,将其减1
if(!flag) return;//本趟排序未发生交换,提前终止算法
}
}
int main()
{
int a[10]={5,8,4,78,12,96,1,4,16,50};
int i;
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
CocktailSort(a,10);
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
system("pause");
return 0;
}
三、快速排序算法
[算法思想]:快速排序法原理也是用了分治法,主要原理是将数组分为A[p..q-1] 和A[q+1..r],然后调整元素使得A[p..q-1]小于等于q,也小于等于A[q+1..r]。然后不断的递归,到最后就排序完成。
快速排序是一种排序算法,对包含n个数的输入数组,平均时间为O(nlgn),最坏情况是O(n^2)。通常是用于排序的最佳选择。因为,基于比较的排序,最快也只能达到O(nlgn)。
快速排序方法1:
QUICKSORT(A, p, r)
1 if p < r
2 then q ← PARTITION(A, p, r) //关键
3 QUICKSORT(A, p, q - 1)
4 QUICKSORT(A, q + 1, r)
数组划分
快速排序算法的关键是PARTITION过程,它对A[p..r]进行就地重排:
PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r - 1
4 do if A[j] ≤ x
5 then i ← i + 1
6 exchange A[i] <-> A[j]
7 exchange A[i + 1] <-> A[r]
8 return i + 1
例子如下:
1、i p/j
2 8 7 1 3 5 6 4(主元)
j指的2<=4,于是i++,i也指到2,2和2互换,原数组不变。
j后移,直到指向1..
2、j(指向1)<=4,于是i++
i指向了8,所以8与1交换。
数组变成了:
i j
2 1 7 8 3 5 6 4
3、j后移,指向了3,3<=4,于是i++
i这是指向了7,于是7与3交换。
数组变成了:
i j
2 1 3 8 7 5 6 4
4、j继续后移,发现没有再比4小的数,所以,执行到了最后一步,
即上述PARTITION(A, p, r)代码部分的 第7行。
因此,i后移一个单位,指向了8
i j
2 1 3 8 7 5 6 4
A[i + 1] <-> A[r],即8与4交换,所以,数组最终变成了如下形式,
2 1 3 4 7 5 6 8
快速排序第一趟完成。
4把整个数组分成了俩部分,2 1 3,7 5 6 8,再递归对这俩部分分别快速排序。
i p/j
2 1 3(主元)
2与2互换,不变,然后又是1与1互换,还是不变,最后,3与3互换,不变,最终,3把2 1 3,分成了俩部分,2 1,和3。再对2 1,递归排序,最终结果成为了1 2 3。
7 5 6 8(主元),7、5、6、都比8小,所以第一趟,还是7 5 6 8,不过,此刻8把7 5 6 8,分成了 7 5 6,和8.[7 5 6->5 7 6->5 6 7],再对7 5 6,递归排序,最终结果变成5 6 7 8。
#include<iostream>
using namespace std;
int Partition(int A[],int p,int r) //分治法
{
int i,j,x,temp;
x=A[r];
i=p-1;
for(j=p;j<r;j++)
{
if(A[j]<x)
{
temp=A[j];
A[j]=A[++i];
A[i]=temp;
}
}
A[r]=A[++i];
A[i]=x;
return i;
}
void QuickSort(int A[],int p,int r)
{
int q;
if (p<r)
{
q=Partition(A,p,r) ;
QuickSort(A,p,q-1);
QuickSort(A,q+1,r);
}
}
int main()
{
int a[11]={5,8,4,78,12,56,96,1,4,16,50};
int i;
for(i=0;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
QuickSort(a,0,10);
for(i=0;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
system("pause");
return 0;
}
快速排序方法2:这个版本不再选取(如上第一版本的)数组的最后一个元素为主元,而是选择数组中的第一个元素为主元。
还是以上述相同的数组,应用此快排算法的版本二,来演示此排序过程:
这次,以数组中的第一个元素2为主元。
2(主) 8 7 1 3 5 6 4
请细看:
2 8 7 1 3 5 6 4
i-> <-j
(找大) (找小)
1、j
j找第一个小于2的元素1,1赋给(覆盖重置)i所指元素2
得到:
1 8 7 3 5 6 4
i j
2、i
i找到第一个大于2的元素8,8赋给(覆盖重置)j所指元素(NULL<-8)
1 7 8 3 5 6 4
i <-j
3、j
j继续左移,在与i碰头之前,没有找到比2小的元素,结束。
最后,主元2补上。
第一趟快排结束之后,数组变成:
1 2 7 8 3 5 6 4
第二趟,
7 8 3 5 6 4
i-> <-j
(找大) (找小)
1、j
j找到4,比主元7小,4赋给7所处位置
得到:
4 8 3 5 6
i-> j
2、i
i找比7大的第一个元素8,8覆盖j所指元素(NULL)
4 3 5 6 8
i j
4 6 3 5 8
i-> j
i与j碰头,结束。
第三趟:
4 6 3 5 7 8
......
以下,分析原理,一致,略过。
最后的结果,如下图所示:
1 2 3 4 5 6 7 8
#include<iostream>
using namespace std;
int Partition(int A[],int p,int r) //分治法
{
int i=p,j=r,x;
x=A[p];
while (i<j)
{
while (i<j&&A[j]>=x)
{
j--;
}
if (i<j)
{
A[i]=A[j];
}
while (i<j&&A[i]<=x)
{
i++;
}
if (i<j)
{
A[j]=A[i];
}
}
A[i]=x;
return i;
}
void QuickSort(int A[],int p,int r)
{
int q;
if (p<r)
{
q=Partition(A,p,r) ;
QuickSort(A,p,q-1);
QuickSort(A,q+1,r);
}
}
int main()
{
int a[11]={5,8,4,78,12,56,96,1,4,16,50};
int i;
for(i=0;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
QuickSort(a,0,10);
for(i=0;i<11;i++)
cout<<a[i]<<" ";
cout<<endl;
system("pause");
return 0;
}