Sort
排序是经典问题,关于排序的算法也有很多,根据平时的使用习惯,下面简单总结了几种常见的,简单易懂的排序算法。其中部分内容参考了数据结构自考网,还有各位大神,小鬼的blog。
目录:(按策略划分)
交换排序
冒泡排序(BubbleSort)
快速排序(QuickSort)
插入排序
直接插入排序(InsertionSort)
希尔排序(Shell Sort)
选择排序
直接选择排序(Selection Sort)
堆排序(HeapSort)
以上几种排序方法比较
BubbleSort
经典冒泡排序,两项比较,如果大小关系和关键字相反就交换位置
#include<stdio.h>
void main()
{
int i,j,t;
int a[9]={9,2,1,5,6,3,4,7,8};
for(i=1;i<9;i++)
for(j=0;j<8;j++)
if(a[j]>a[i])
{
t=a[j];a[j]=a[i];a[i]=t;
}
for(i=0;i<9;i++)
printf("%d ",a[i]);
printf("/n");
}
另:
算法思想:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
QuickSort
快速排序(qsort),分治递归思想制定标志变量,以其分割数组,并对前数组,后数组分别递归调用函数,直到数组长度为1,则完成排序
#include <iostream>
#include <stdlib.h>
#include <ctime>
using namespace std;
int partition(int *A,int p,int r) //数组分割
{
int i,j,x;
x=A[r]; //指定标志值
i=p-1; //保证第一次找到的小于,等于X的数被换到
for(j=p;j<r;j++) //a[0],第二个放到a[1]……
if(A[j] <= x) //大于X的数不会使i变大
{
i++;
swap<int>(A[i],A[j]);
}
swap<int>(A[i+1],A[r]); //前i个数都小于a[r]
return i+1; //返回a[r],即分割数组的标志值位置
}
void quicksort(int *A,int p,int r)
{
if(p >= r)
return ;
int q=partition(A,p,r);
quicksort(A,p,q-1); //q位置的数不用参与排序,因为当前位置就是
quicksort(A,q+1,r); //其正确位置
}
void Show(int *A,int p,int r)
{
for(int i=p;i<=r;i++)
cout<<A[i]<<" ";
cout<<endl;
}
void main()
{
int i;
int A[10];
srand((unsigned int)time(NULL));
for(i=0;i<10;i++)
A[i]=rand()%20;
Show(A,0,9);
quicksort(A,0,9);
Show(A,0,9);
system("pause");
}
InsertionSort
插入排序,每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。
默认当前项前面的序列有序,并在前面的序列中查找第一个比当前项大(小)的数,找到后,把该数以及其后面的数全部后移一位,空出来的位置就是当前的位置。
插入排序的操作很类似打牌时整理手中的牌,很有意思的方法。
#include<stdio.h>
void main()
{
int i,j,t;
int a[9]={9,2,1,5,6,3,4,7,8};
for(i=1;i<9;i++)
{
if(a[i]<a[i-1])//若R[i].key大于等于有序区中所有的keys,则R[i]
//应在原有位置上
{
t=a[i];
for(j=i-1;j>=0;j--)
if(a[j]>t)
{
a[j+1]=a[j];
a[j]=t;
}
else break;
}
}
for(i=0;i<9;i++)
printf("%d ",a[i]);
printf("/n");
}
另:希尔排序实质上就是分组插入排序,效率较直接插入排序有很大提高,但是不稳定。
SelectionSort
选择排序,在待排数据中查找最小(大)值放在第一位,然后在剩下的数中查找第二小(大)的数放在第二位,……依次操作
#include<stdio.h>
void main()
{
int i,j,t,min;
int a[9]={9,2,1,5,6,3,4,7,8};
for(i=0;i<9;i++)
{
min=i;
for(j=i+1;j<9;j++)
if(a[j]<a[min])
min=j;
t=a[i];a[i]=a[min];a[min]=t; //t做存储单元,也可以用a[0]做存储单元
}
for(i=0;i<9;i++)
printf("%d ",a[i]);
printf("/n");
}
直接选择排序时就地排序,是不稳定的排序。
HeapSort
堆排序
堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。根结点的关键字是堆里所有结点关键字中最大者,称为大根堆。堆中任一子树亦是堆。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
步骤:
建堆(假设是大根堆),把堆顶元素a[1]和最后一个元素a[n]交换位置,则a[n]就是最大值;然后以a[1]到a[n-1]为对象,调整建堆,再把堆顶元素和最后一个元素(现在是a[n-1])交换,则a[n-1]到a[n]就是有序的了。以此重复操作即可。
以上几种排序方法比较
平均时间:
(1) 平方阶(O(n2))排序
一般称为简单排序,例如直接插入、直接选择和冒泡排序;
(2)线性对数阶(O(nlgn))排序
如快速、堆排序;
(3)O(n1+£)阶排序
£是介于0和1之间的常数,即0<£<1,如希尔排序;
各种排序方法比较
简单排序中直接插入最好,快速排序最快,当文件为正序时,直接插入和冒泡均最佳。
影响排序效果的因素
因为不同的排序方法适应不同的应用环境和要求,所以选择合适的排序方法应综合考虑下列因素:
1待排序的记录数目n;
2记录的大小(规模);
3关键字的结构及其初始状态;
4对稳定性的要求;
5语言工具的条件;
6存储结构;
7时间和辅助空间复杂度等。
不同条件下,排序方法的选择
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序;
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。