冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。
有6个元素需要排序: 6 5 3 4 1 2
**程序实现方法:**用两层循环完成算法,外层循环i控制每轮要进行多少次的比较,第1轮比较n-1次,第2轮比较n-2次,……,最后一轮比较1次。内层循环j控制每轮i次比较相邻两个元素是否逆序,若逆序就交换这两个元素。
程序输入:
第一行:一个整数n(0
#include
using namespace std;
const int maxn=505;
int n,a[maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n-1;i>=1;i--)//第i轮排序
{
for(int j=1;j<=i;j++)
if(a[j]>a[j+1])//比较相邻元素大小
swap(a[j],a[j+1]);
}
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
对于有些数据,我们发现,不一定要n-1次才能排完。例如1 5 2 3 4 6,我们发现只需一趟排序就可以将整个序列排完,于是,我们可以设置一个布尔变量,判断是否有进行交换,如果没有交换,说明已经排序完成,进而减少几趟排序。
#include
using namespace std;
const int maxn=505;
int n,a[maxn];
bool flag;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n-1;i>=1;i--)//第i轮排序
{
flag=0;
for(int j=1;j<=i;j++)
if(a[j]>a[j+1])//比较相邻元素大小
{
flag=1;
swap(a[j],a[j+1]);
}
if(flag==0)//没有发生交换,说明已经排好序
break;
}
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在待排序的数列的最前,直到全部待排序的数据元素排完。
对以下序列进行排序:
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 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
最后排序结果 13 27 38 49 49 65 76 97
程序输入:
第一行:一个整数n(0
#include
using namespace std;
const int maxn=505;
int n,a[maxn],t,MIN,MINA;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n-1;i++)
{
MIN=i,MINA=a[i];
for(int j=i+1;j<=n;j++)
{
if(a[j]<MINA)
{
MIN=j;//找出后面的最小值和最小值的坐标
MINA=a[j];
}
}
swap(a[i],a[MIN]);//把最小值放在前面
}
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
当读入一个元素时,在已经排序好的序列中,搜寻它正确的位置,再放入读入的元素。但不该忽略一个重要的问题:在插入这个元素前,应当先将将它后面的所有元素后移一位,以保证插入位置的原元素不被覆盖。
例如:设n=8,数组a中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
第4步:[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]
程序输入:
第一行:一个整数n(0
#include
using namespace std;
const int maxn=505;
int n,a[maxn],tmp;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
int j;
for(j=i-1;j>=1;j--)
if(a[j]<a[i])//找到第一个比a[i]小的元素位置j,插入位置为j+1
break;
if(j!=i-1)
{
tmp=a[i];//存储插入元素的值
for(int k=i-1;k>=j+1;k--)//j+1到i-1的元素后移
a[k+1]=a[k];
a[j+1]=tmp;//插入
}
}
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
桶排序的思想是若待排序的值在一个明显有限范围内(整型)时,可设计有限个有序桶,待排序的值装入对应的桶(当然也可以装入若干个值),桶号就是待排序的值,顺序输出各桶的值,将得到有序的序列。
程序输入:
第一行:一个整数n(0
#include
using namespace std;
const int maxn=10005;
int n,a[2*maxn],MAXA=-1,num;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>num;
a[num+maxn]++;//把相对应的整数放在对应的桶中
MAXA=max(MAXA,num+maxn);//求出所有桶中数的最大值,排除空桶
}
for(int i=0;i<=MAXA;i++)
{
while(a[i]!=0)
{
cout<<i-maxn<<" ";
a[i]--;
}
}
return 0;
}
快速排序是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
程序输入:
第一行:一个整数n(0
#include
int a[505],n;//定义全局变量,这两个变量需要在子函数中使用
void quicksort(int left,int right)
{
int i,j,t,temp;
if(left>right)
return;
temp=a[left]; //temp中存的就是基准数
i=left;
j=right;
while(i!=j)
{
//顺序很重要,要先从右边开始找
while(a[j]>=temp && i<j)
j--;
//再找右边的
while(a[i]<=temp && i<j)
i++;
//交换两个数在数组中的位置
if(i<j)
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
//最终将基准数归位
a[left]=a[i];
a[i]=temp;
quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程
quicksort(i+1,right);//继续处理右边的 ,这里是一个递归的过程
}
int main()
{
int i,j,t;
//读入数据
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
quicksort(1,n); //快速排序调用
//输出排序后的结果
for(i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
#include
using namespace std;
const int maxn=505;
int n,a[maxn];
void qsort(int l,int r)
{
int i,j,mid;
i=l,j=r;//i、j为当前序列的左右端点
mid=a[(l+r)/2];//mid为此序列的基准数
while(i<j)
{
while(a[i]<mid)i++;//从左往右找到第一个大于等于基准数的元素a[i]
while(a[j]>mid)j--;//从右往左找到第一个小于等于基准数的元素a[j]
if(i<=j)
{
swap(a[i],a[j]);//交换两个数
i++;//继续寻找
j--;
}
}
if(l<j)//基准数左边序列
qsort(l,j);
if(i<r)//基准数右边序列
qsort(i,r);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
qsort(1,n);
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
有8个数据需要排序:10 4 6 3 8 2 5 7
归并排序主要分两大步:分解、合并。
合并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。
程序输入:
第一行:一个整数n(0
#include
using namespace std;
const int maxn=505;
int n,a[maxn],b[maxn];//b为辅助数组
void msort(int l,int r)
{
if(l==r)//递归出口
return;
int mid=(l+r)/2;
msort(l,mid);//左边序列
msort(mid+1,r);//右边序列
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r)//左右序列进行合并
{
if(a[i]<=a[j])//把小的元素放在前面
b[k]=a[i++];
else
b[k]=a[j++];
k++;
}
while(i<=mid)//如果有剩余序列,直接加在辅助数组最后面
b[k++]=a[i++];
while(j<=r)
b[k++]=a[j++];
for(int i=l;i<=r;i++)//赋值已经排好序的序列给a数组
a[i]=b[i];
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
msort(1,n);
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
**稳定排序:**插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的;
**不稳定排序:**选择排序、希尔排序、快速排序、堆排序是不稳定的。
插入排序、冒泡排序、选择排序的时间复杂性为O(n^2);快速排序、堆排序、归并排序的时间复杂性为O(nlog2n);桶排序的时间复杂性为O(n);
若从最好情况考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它算法的最好情况同平均情况相同;若从最坏情况考虑,则快速排序的时间复杂度为O(n2),直接插入排序和冒泡排序虽然平均情况相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情况对直接选择排序、堆排序和归并排序影响不大。
由此可知,在最好情况下,直接插入排序和冒泡排序最快;在平均情况下,快速排序最快;在最坏情况下,堆排序和归并排序最快。
桶排序、二路归并排序的辅助空间为O(n),快速排序的辅助空间为O(log2n),最坏情况为O(n),其它排序的辅助空间为O(1);
插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。反而在这种情况下,快速排序反而慢了。
当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。
若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。
当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。
当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序
快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。