程序员面试宝典_第13章_数据结构基础_排序算法小结(1)

来源:互联网 发布:80后和90后的区别 知乎 编辑:程序博客网 时间:2024/04/30 16:55

一、冒泡算法

[算法思想]:将被排序的记录数组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处数据已排好序,加1for(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处数据也已排好序,将其减1if(!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;}


 

 

0 0