您的位置:首页 > 编程语言 > C语言/C++

c++ 排序算法总结

2016-02-22 19:05 471 查看
将杂乱无章的数据变得有规律。有各种各样的排序算法,看情况使用。

1. 选择排序

(1) 基本思想:

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在待排序的数列的最前,直到全部待排序的数据元素排完。

(2)排序过程:

【示例】:

初 始 关键字 [49 38 65 97 76 13 27 49]

第一趟排序后 13[38 65 97 76 49 27 49]

第二趟排序后 13 27[65 97 76 49 38 49]

第三趟排序后 13 27 38 [97 76 49 65 49]

第四趟排序后 13 27 38 49 [76 97 65 49]

第五趟排序后 13 27 38 49 49 [97 65 76]

第七趟排序后 13 27 38 49 49 65 76 [97]

最后排序结果 13 27 38 49 49 65 76 97

代码:

void SelectSort(int R[])  //对R[1..N]进行直接选择排序
{
for (int i=1;i<=n-1;i++)                   //做N - 1趟选择排序
{
K = I;
For (int j=i+1;j<=n;j++)        //在当前无序区R[I..N]中选最小的元素R[K]
{
If (R[J] < R[K])  K = J;
}
If (K!=I)                                      //交换R[I]和R[K]
{
Temp = R[I];
R[I] = R[K];
R[K] = Temp;
}
}
}


(时间复杂度为O(n^2))

2.冒泡排序

(1)基本的冒泡排序

①基本思想

依次比较相邻的两个数,把大的放前面,小的放后面。即首先比较第1个数和第2个数,大数放前,小数放后。然后比较第2个数和第3个数……直到比较最后两个数。第一趟结束,最小的一定沉到最后。重复上过程,仍从第1个数开始,到最后第2个数,然后……

由于在排序过程中总是大数往前,小数往后,相当气泡上升,所以叫冒泡排序。

下面是6个元素的排序的过程

  4 5 7 1 2 3

┗━━┛

5 4 7 1 2 3

┗━━┛

5 7 4 1 2 3

┗━━┛

5 7 4 1 2 3

┗━━┛

5 7 4 2 1 3

┗━━┛

第一趟结束

5     7    4    2    3    ①
┗━━┛
7     5    4    2    3    1
┗━━┛
7     5    4    2    3    1
┗━━┛
7     5    4    2    3    1
┗━━┛       第二趟结束
7     5    4    3    ②   1
┗━━┛
7     5    4    3    2    1
┗━━┛
7     5    4    3    2    1
┗━━┛              第三趟结束
7     5    4    ③   2    1
┗━━┛
7     5    4    3    2    1
┗━━┛                    第四趟结束
7     5    ④    3   2    1
┗━━┛                           第五趟结束
⑦    ⑤   4    3    2    1


②算法实现

for (int i=1;i<=n-1;i++)
for (int j=1;j<=n-i;j++)
if (a[j]<a[j+1])
{
temp=a[j]; a[j]=a[j+1]; a[j+1]=temp;
}


(2)改进

上例中,可以发现,第二趟结束已经排好序。但是计算机此时并不知道已经排好序。所以,还需进行一次比较,如果没有发生任何数据交换,则知道已经排好序,可以不干了。因此第三趟比较还需进行,第四趟、第五趟比较则不必要。

我们设置一个布尔变量bo 来记录是否有进行交换。值为false表示本趟中进行了交换,true 则没有。代码如下:

int i=1;
do
{
bo=true;
for (int j=1;j<=n-I;j++)
if (a[j]<a[j+1])
{
temp=a[j];    a[j]=a[j+1];   a[j+1]=temp;
bo=false;
}
 I++;
}
While (!bo);


3. 桶排序

桶排序的思想是若待排序的记录的关键字在一个明显有限范围内(整型)时,可设计有限个有序桶,每个桶装入一个值(当然也可以装入若干个值),顺序输出各桶的值,将得到有序的序列。

例:输入n个0到100之间的不相同整数,由小到大排序输出。

#include<iostream>
#include <cstring>
using namespace std;
Int main()
{
int b[101],k,I,n;
memset(b,0,sizeof(b));                    //初始化
cin>>n;
for( i=1;I<=n;i++)
{
cin>>k;   b[k]++;                //将关键字等于k的值全部装入第k桶
}
for( i=0; I<=100;i++)
while (b[i]>0)  {cout<<i<<"  " ;b[i]--;}    //输出排序结果
cout<<endl;
}


4. 插入排序

插入排序是一种简单的排序方法,其算法的基本思想是:

假设待排序的数据存放在数组R[1..n]中,增加一个哨兵结点x。

(1) R[1]自成1个有序区,无序区为R[2..n]; (2) 从i=2起直至i=n为止,将R[i]放在恰当的位置,使R[1..i]数据序列有序;

x:=R[i];


② 将x与前i-1个数比较 ,
j:=i-1;  while x<a[j] do j:=j-1;


③ 将R数组的元素从j位置开始向后移动:
for k:=i downto j do a[k]:=a[k-1];


R[j]=x;


(3) 生成包含n个数据的有序区。.

例如:设n=8,数组R中8个元素是: 36,25,48,12,65,43,20,58,执行插入排序程序后,其数据变动情况:

第0步:[36] 25 48 12 65 43 20 58

第1步:[25 36] 48 12 65 43 20 58

第2步:[25 36 48] 12 65 43 20 58

第3步:[12 25 36 48] 65 43 20 58

第5步:[12 25 36 43 48 65] 20 58

第6步:[12 20 25 36 43 48 65] 58

第7步:[12 20 25 36 43 48 58 65]

代码:

void insertsort(int r[])
//对r[1..n]按递增序进行插入排序,x是监视哨
{
for (i=2;i<=n;i++)                       //依次插入r[2],...,r

{
x=r[i];
j= i-1;
While (x< r[j])                     //查找r[i]的插入位置//
{
r[j+1] =r[j];                     //将大于r[i]的元素后移//
j--;
}
r[j+1] = x;                             //插入r[I] //
}
}


5.快速排序

快速排序是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

假设待排序的序列为{a[L],a[L+1],a[L+2],……,a[R]},首先任意选取一个记录(通常可选中间一个记作为枢轴或支点),然后重新排列其余记录,将所有关键字小于它的记录都放在左子序列中,所有关键字大于它的记录都放在右子序列中。由此可以将该“支点”记录所在的位置mid作分界线,将序列分割成两个子序列和。这个过程称作一趟快速排序(或一次划分)。

一趟快速排序的具体做法是:附设两个指针i和j,它们的初值分别为L和R,设枢轴记录取mid,则首先从j所指位置起向前搜索找到第一个关键字小于的mid的记录,然后从i所指位置起向后搜索,找到第一个关键字大于mid的记录,将它们互相交换,重复这两步直至i>j为止。

快速排序的时间的复杂性是O(nlog2n),速度快,但它是不稳定的排序方法。就平均时间而言,快速排序是目前被认为是最好的一种内部排序方法

由以上讨论可知,从时间上看,快速排序的平均性能优于前面讨论过的各种排序方法,但快速排序需一个栈空间来实现递归。若每一趟排序都将记录序列均匀地分割成长度相接近的两个子序列,则栈的最大深度为log(n+1)。

代码:

void qsort(int l,int r)
{   int i,j,mid,p;
i=l;
j=r;
mid=a[(l+r) / 2];                  //将当前序列在中间位置的数定义为分隔数
do
{
while (a[i]<mid) i++;       //在左半部分寻找比中间数大的数
while (a[j]>mid) j--;      //在右半部分寻找比中间数小的数
if (i<=j)
{                                //若找到一组与排序目标不一致的数对则交换它们
p=a[i];
a[i]=a[j];
a[j]=p;
i++;
j--;                            //继续找
}
}
while (i<=j);                                  //注意这里不能有等号
if (l<j)  qsort(l,j);              //若未到两个数的边界,则递归搜索左右区间
if (i<r)  qsort(i,r);
end;


6.归并排序

归并排序(Merge sort)就是利用归并操作把一个无序表排列成一个有序表的过程。二路归并排序的过程是首先把待排序区间(即无序表)中的每一个元素都看作为一个有序表,则n个元素构成n个有序表,接着两两归并(即第一个表同第二个表归并,第三个表同第四个表归并,…),得到[n/2]个长度为2的有序表(最后一个表的长度可能小于2),称此为一趟归并,然后再两两有序表归并,得到[[n/2]/2]个长度为4的有序表(最后一个表的长度可能小于4),如此进行下去,直到归并第[log2n]趟后得到一个长度为n的有序表为止。

归并排序算法我们用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。对左右子区间的排序与原问题一样,所以我们可以调用同样的子程序,只是区间大小不一样。

代码:

归并简洁版

#include<iostream>
using namespace std;
void Merge_sort(int *a,int x,int y,int *t){
if(y-x>1){
int m=x+(y-x)/2;
int p=x,q=m,i=x;
Merge_sort(a,x,m,t);
Merge_sort(a,m,y,t);
while(p<m||q<y){
if(q>=y||(p<m&&a[p]<=a[q])) t[i++]=a[p++];
else t[i++]=a[q++];
}
for(i=x;i<y;i++) a[i]=t[i];
}
}
int main(){
int a[100],t[100];
int  i,n;
cin>>n;
for(i=0;i<n;i++)  cin>>a[i];
Merge_sort(a,0,n,t);
for(i=0;i<n;i++) cout<<t[i]<<' ';
}


归并排序完全版:

#include<iostream>
using namespace std;
int n;
void Merge(int (&a)[100],int low,int mid,int high){
int i,j,k;
int r[100]={0};
i=low; j=mid+1; k=low;
while(i<=mid&&j<=high){
if(a[i]<=a[j]){
r[k]=a[i];
i++;
k++;
}
if(a[j]<a[i]){
r[k]=a[j];
j++;
k++;
}
}
for(;i<=mid;i++){
r[k]=a[i];
k++;
}
for(;j<=high;j++){
r[k]=a[i];
k++;
}
for(i=low;i<=high;i++){
a[i]=r[i];
}
}
//拆分数组
void MergeSort(int (&a)[100],int low,int high){
int mid;
int low1=low;
int high1=high;
if(low<high){
mid=(low+high)/2;
MergeSort(a,low1,mid);
MergeSort(a,mid+1,high1);
Merge(a,low1,mid,high1);
}
}
int main(){
int a[100];
int i;
cin>>n;
for(i=1;i<=n;i++){
cin>>a[i];
}
MergeSort(a,1,n);
for(i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
return(0);
}


7.c++标准模板库STL(sort版)

使用排序算法必须包含 algorithm 头文件

   自带排序算法的一般形式为:

//将数组arr的下标为m的元素到下标为n-1的元素进行从小到大排序
   sort(arr+m,arr+n);
   sort(arr+m,arr+n,comp);   //与sort(arr+m,arr+n); 相比,这个                             //写法可以自己定义排序的规则
                //其中,comp为自定义的函数


对于sort(arr+m,arr+n) 我们举个简单的例子:

   //这个程序实现从键盘读入10个数,然后从小到大输出的功能
   #include<iostream>
   #include<algorithm>
   using namespace std;
   int a[10];
   int main()
   {
    for (int i=0;i<10;++i) cin>>a[i];
    sort(a+0,a+10);
    for (int i=0;i<10;++i) cout<<a[i]<<' ';
    cout<<endl;
    return 0;
   }


当然,有时我们需要从大到小的进行排序。那么我们可以用 sort(arr+n,arr+m,comp)进行排序。

不过,在调用sort(arr+n,arr+m,comp) 之前我们需要自己写个 comp函数。

   从大到小排序的comp函数可以这样写:

   

int my_comp(const int & a,const int & b)
   {
   return a>b;      //在两元素相同时一定要返回 0 或者 false
   }  //comp函数的名字是由我们自己决定的


代码:

   #include<iostream>
   #include<algorithm>
   using namespace std;
   int a[10];
   int my_comp(const int & a,const int & b)
   {
    return a>b;     //如果a>b 则返回1 ,否则返回 0
   }
   int main()
   {
    for (int i=0;i<10;++i) cin>>a[i];
    sort(a+0,a+10,my_comp);
    for (int i=0;i<10;++i) cout<<a[i]<<' ';
    cout<<endl;
    return 0;
}


   在更多情况下,我们不仅对一个特征进行排序,而是多个特征。例如将学生的成绩进行排序,当然用上面的做法是行不通的。这时,我们就想到了结构体这种数据类型。当我们采用sort()函数的默认规则排序结构体时,sort()默认结构体中的第一个成员为第一关键字,第二个成员为第二关键字,……,第N个元素为第N关键字,然后从小到大排序。

   例如我们要将学生的成绩从大到小排序,当成绩相同时,根据姓名字典序小的优先的规则排进行序。显然我们无法采用默认规则进行排序。

   这时我们可以定义这样的comp:

   

int score_comp(const student & a,const student & b)
   {
    if (a.score>b.score) return 1;
    if (a.score<b.score) return 0;
    if (a.name<b.name) return 1;
    return 0;
   }


8.c++标准模板库STL(优先队列版)

自己参悟吧……道理同7

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
string name;
int sumscore;
int mathscore;
}p[1001];
struct cmp
{
bool operator()(const node &a,const node &b)
{
if(a.sumscore>b.sumscore) return true;
//      if(a.sumscore<b.sumscore) return false;
//      if(a.mathscore>b.mathscore) return true;
return false;
}
//  bool operator()(const node &a,const node &b)
//  {
//
//  }
};
int main()
{
int n;cin>>n;
priority_queue<node,vector<node>,cmp> q;
for(int i=1;i<=n;i++)
{
cin>>p[i].name;
cin>>p[i].sumscore>>p[i].mathscore;
q.push(p[i]);
}
for(int i=1;i<=n;i++)
{
cout<<q.top().name<<" ";
q.pop();
}
}


9.各种排序算法的比较

1.稳定性比较

插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的。

选择排序、希尔排序、快速排序、堆排序是不稳定的。

2.时间复杂性比较

插入排序、冒泡排序、选择排序的时间复杂性为O(n2);快速排序、堆排序、归并排序的时间复杂性为O(nlog2n);桶排序的时间复杂性为O(n);

若从最好情况考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它算法的最好情况同平均情况相同;若从最坏情况考虑,则快速排序的时间复杂度为O(n2),直接插入排序和冒泡排序虽然平均情况相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情况对直接选择排序、堆排序和归并排序影响不大。

由此可知,在最好情况下,直接插入排序和冒泡排序最快;在平均情况下,快速排序最快;在最坏情况下,堆排序和归并排序最快。

3.辅助空间的比较

桶排序、二路归并排序的辅助空间为O(n),快速排序的辅助空间为O(log2n),最坏情况为O(n),其它排序的辅助空间为O(1);

4.其它比较

插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。反而在这种情况下,快速排序反而慢了。

当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。

当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序

  快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: