排序的种类:
排序分为内部排序和外部排序,涉及多种排序的方法。
内部排序是将待排序的记录全部放在内存的排序。插入排序(直接插入排序、折半插入排序和希尔排序)、交换排序(冒泡排序和快速排序)、选择排序(简单选择排序和堆排序)、归并排序和基数排序。
1.归并排序:
按照分治法:①把序列分成元素个数尽量相等的两半 ②把两半元素分别排序 ③把两个有序表合并成一个
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
int A[99],T[99];
void pai(int x,int y) //x、y是左右边界
{
if(y - x == 1) return;
else
{
int m = x +(y - x) / 2; //划分
int p = x,q = m,i = x;
pai(x,m); //递归
pai(m,y); //递归
while(p < m || q < y) //合并
if(q >= y || (p < m && A[p] <= A[q])) //右半序列空的时候,直接从另一个序列尾部复制元素,右半序列非空(可能左空右不空,可能左右都空)时,当且仅当第一个序列也非空的时候,且A[P]<=A[q]的时候,才复制A[p]
T[i++] = A[p++];
else //否则全部复制A[q] 巧用||,好算法!
T[i++] = A[q++];
for(i = x;i < y;i++) A[i] = T[i]; //从辅助数组T复制回A数组
memset(T,0,sizeof(T));
}
}
int main()
{
int n;
cin>>n;
for(int i = 0;i < n;i++)
cin>>A[i];
pai(0,n);
for(int i = 0;i < n;i++)
cout<<A[i]<<" ";
}
2.快速排序:
按照分治法:①把数组重排后分成左右两部分(由于划分方法不同,不只有一种实现方法,会有不同的版本,设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。),使左边任意元素小于等于右边的 ②把左右两边的分别排序 ③不用合并。
值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
数据
|
6
|
2
|
7
|
3
|
8
|
9
|
下标
|
0
|
1
|
2
|
4
|
5
| |
数据
|
3
|
2
|
7
|
6
|
8
|
9
|
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
数据
|
3
|
2
|
6
|
7
|
8
|
9
|
3.冒泡法(比较法)排序:
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
-
针对所有的元素重复以上的步骤,除了最后一个。
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
![](https://i-blog.csdnimg.cn/blog_migrate/dc15aaddebe0f88faadbdc2a644589e9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5d802ef99f4f53fa00d98234740aabe3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5d802ef99f4f53fa00d98234740aabe3.png)
#include<stdio.h>
#define SIZE 8
voidbubble_sort(inta[],int n)//n为数组a的元素个数
{
int i,j,temp;
for(j=0;j<n-1;j++)
for(i=j;i<n-1;i++)
if(a[i]>a[i+1])//数组元素大小按升序排列
{
temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
}
}
int main()
{
int number[SIZE]={95,45,15,78,84,51,24,12};
int i;
bubble_sort(number,SIZE);
for(i=0;i<SIZE;i++)
{
printf("%d",number[i]);
}
printf("\n");
}
4.选择法排序:
简单选择排序的基本思想:
以下为简单选择排序的存储状态,其中大括号内为无序区,大括号外为有序序列:
初始序列:{ 49 27 65 97 76 12 38 }
第1趟:12与49交换:12 { 27 65 97 76 49 38 }
第2趟:27不动 :12 27 { 65 97 76 49 38 }
第3趟:65与38交换:12 27 38 { 97 76 49 65 }
第4趟:97与49交换:12 27 38 49 { 76 97 65 }
第5趟:65与76交换:12 27 38 49 65 { 97 76 }
第6趟:97与76交换:12 27 38 49 65 76 97 完成
#include<stdio.h>
void sa(int array[],int n)
{
int i,j,k,temp;
for(i=0;i<10;i++)
{
k=i; //保存i的值,用k来进行循环排序
for(j=i+1;j<n;j++) //将第i个元素后面的元素与第i个元素进行比较
if(array[j]<array[k]) //如果第k=i个元素后面的元素小于i号元素,交换两个元素的标号, 这样就将最小元素的标号放到最前面
k=j; //交换标号
temp=array[k]; //循环结束后,交换两个标号下的元素的值
array[k]=array[i];
array[i]=temp;
}
}
void main()
{
void sa(int array[],int n);
int array[10],i;
printf("enter the array:\n");
for(i=0;i<10;i++)
scanf("%d",&array[i]);
sa(array,10);
printf("the sorted array:\n");
for(i=0;i<10;i++)
printf("%d\t",array[i]);
getch();
}
5.插入法排序:
接下来,我们看第3个数字有没有在正确的位置。这个数字是4,它的左边数字是5,4比5小,所以我们将4和5交换,排列变成了 “1,4,5,2,3 "我们必须继续看4有没有在正确的位置,4的左边是1,1比4小,4就维持不动了。
再来看第四个数字,这个数字是2,我们将2和它左边的数字相比,都比2大,所以就将2一路往左移动,一直移到2的左边是1,这时候排序变成了 “1,2,4,5,3 ”
最后,我们检查第五个数字,这个数字是3,3必须往左移,一直移到3的左边是2为止,所以我们的排列就变成了 “1,2,3,4,5 ”排序因此完成了。
优点:稳定,快;
时间复杂度:O(n^2)# include <stdio.h>
main()
{
int a[10],i,j,t;
printf("Please input 10 numbers: ");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=1;i<10;i++) /*外循环控制趟数,n个数从第2个数开始到最后共进行n-1次插入*/
{
t=a[i]; /*将待插入数暂存于变量t中*/
for( j=i-1 ; j>=0 && t>a[j] ; j-- ) /*在有序序列(下标0 ~ i-1)中寻找插入位置*/
a[j+1]=a[j]; /*若未找到插入位置,则当前元素后移一个位置*/
a[j+1]=t; /*找到插入位置,完成插入*/
}
printf("The sorted numbers: ");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
6.树形选择排序(锦标赛排序)
7.堆排序:O(n log n) 只需要一个额外的辅助空间。
8.基数排序:O(n)
9.希尔排序(缩小增量排序):O(n^1.25)
10.典型比较排序法时间复杂度对比:
|
外部排序是对存放在外存的大型文件的排序,外部排序基于对有序归并段的归并,而其初始归并段的产生基于内部排序。
依据这些因素,可得出如下几点结论:
(1) 若n较小(如n值小于50),对排序稳定性不作要求时,宜采用选择排序方法,若关键字的值不接近逆序,亦可采用直接插入排序法。但如果规模相同,且记录本身所包含的信息域比较多的情况下应首选简单选择排序方法。因为直接插入排序方法中记录位置的移动操作次数比直接选择排序多,所以选用直接选择排序为宜。
(2) 如果序列的初始状态已经是一个按关键字基本有序的序列,则选择直接插入排序方法和冒泡排序方法比较合适,因为“基本”有序的序列在排序时进行记录位置的移动次数比较少。
(3) 如果n较大,则应采用时间复杂度为O(nlog2n)的排序方法,即快速排序、堆排序或归并排序方法。快速排序是目前公认的内部排序的最好方法,当待排序的关键字是随机分布时,快速排序所需的平均时间最少;堆排序所需的时间与快速排序相同,但辅助空间少于快速排序,并且不会出现最坏情况下时间复杂性达到O(n2)的状况。这两种排序方法都是不稳定的,若要求排序稳定则可选用归并排序。通常可以将它和直接插入排序结合在一起用。先利用直接插入排序求得两个子文件,然后,再进行两两归并。
11.二分查找(折半查找):
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
//检索某个值在数组第一次出现的地方,二分法查找,迭代实现
using namespace std;
int A[99];
int bsearch(int *A,int x,int y,int v)
{
int m;
while(x < y)
{
m = x+ (y - x) / 2;
if(A[m] == v) return m;
else if(A[m] > v) y = m;
else x = m + 1;
}
return -1; //出现错误
}
int main()
{
int n,v;
cin>>n;
for(int i = 0;i < n;i++)
cin>>A[i];
cin>>v;
cout<<bsearch(A,0,n - 1,v)<<endl;
}
//二分法查找求下界
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
//检索某个值在数组第一次出现的地方,二分法查找,迭代实现
using namespace std;
int A[99];
int bsearch(int *A,int x,int y,int v)
{
int m;
while(x < y)
{
m = x+ (y - x) / 2;
if(A[m] >= v) y = m;
else x = m + 1;
}
return x;
}
int main()
{
int n,v;
cin>>n;
for(int i = 0;i < n;i++)
cin>>A[i];
cin>>v;
cout<<bsearch(A,0,n - 1,v)<<endl;
}