冒泡排序 Bubble Sort
一种简单的排序算法
动图演示
比较相邻的元素
一趟冒泡排序确定一个数的最终位置
当输入的数据已经是正序时,排序最快
当输入的数据是反序时,排序最慢 (for 循环反序输出数据)
时间复杂度:平均和最坏情况:O(N^2)
最好情况:O(N)
空间复杂度:O(1)
当有n个数要冒泡排序时,要进行n-1趟冒泡
for(int i=1;i<=n-1;i++){
for(int j=1;j<=n-i;j++{
if(a[j]>a[j+1]) swap(a[j],a[j+1]); //从小到大排序
if(a[j]<a[j+1]) swap(a[j],a[j+1]); //从大到小排序
}
}
优化
立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。
for(int i=1;i<=n-1;i++){
bool flag=false;
for(int j=1;j<=n-i;j++{
if(a[j]>a[j+1]) swap(a[j],a[j+1]); //从小到大排序
if(a[j]<a[j+1]) swap(a[j],a[j+1]); //从大到小排序
flag=true;
}
if(!flag) break;
}
选择排序 Selection Sort
一种简单的排序算法
动图演示
在未排序列中找到最小(大)元素,存放到序列的起始位置。
一趟选择排序确定一个数的最终位置
当有n个数要选择排序时,要进行n-1趟选择
时间复杂度:O(N^2)
空间复杂度:O(1)
for(int i=1;i<=n-1;i++){
int x=i;
for(int j=i;j<=n;j++){
if(a[j]<a[x]) x=j;
}
swap(a[i],a[x]);
}
插入排序 Insertion Sort
一种最简单直观的排序算法
动图演示
将第一个元素看做有序序列,把第二个元素到最后一个元素当做排序序列。
当有n个数要插入排序时,要进行n-1趟插入
时间复杂度:平均和最坏情况:O(N^2)
此时待排序列为逆序,或者说接近逆序
最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)
for(int i=2;i<=n;i++){
int x=a[i];
int j=i-1;
while(j>=1&&a[j]>x){ //降序a[j]<x
a[j+1]=a[j];
j--;
}
a[j+1]=x;
}
希尔排序 Shell Sort
希尔排序是插入排序的一种,它是针对直接插入排序算法的改进。
希尔排序目的为了加快速度改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
在此我们选择增量 gap=length/2,缩小增量以 gap = gap/2 的方式,用序列 {n/2,(n/2)/2…1} 来表示。
(1)初始增量第一趟 gap = length/2 = 4
(2)第二趟,增量缩小为 2
(3)第三趟,增量缩小为 1,得到最终排序结果
桶排序
用数组做统计 (桶)
动图演示
当输入的数据可以均匀的分配到每一个桶中,排序最快
当输入的数据被分配到了同一个桶中,排序最慢
数组下标的范围:输入数字的范围
时间复杂度:O(N+K),K是桶的数量
空间复杂度:O(N+K),K是桶的数量
for(int i=1;i<=n;i++){
cin>>x;
a[x]++;
}
for(int i=0;i<=100;i++){ //降序 for(int i=100;i>0;i--)
while(a[i]!=0){
cout<<i<<" ";
a[i]--;
}
}
计数排序 Counting Sort
线性排序算法
1. 找出待排序的数组中最大和最小的元素;
2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
基数排序 Radix Sort
一种非比较排序算法
1. 确定数组中的最大元素有几位(MAX)(确定执行的轮数)
2. 创建0 ~ 9个桶(桶的底层是队列),因为所有的数字元素都是由0 ~ 9的十个数字组成
3. 依次判断每个元素的个位,十位至MAX位,存入对应的桶中,出队,存入原数组;直至MAX轮结束输出数组。
时间复杂度:O(N*K),K是桶的数量
空间复杂度:O(N+K),K是桶的数量
sort排序
STL排序函数
sort(第一个下标 ,最后一个下标+1,比较函数名)
sort无比较名,则默认为从小到大
bool cmp(int a,int b){
return a<b; //从小到大
return a>b; //从大到小
}
sort(a+1,a+n+1,cmp);
归并排序 Merge Sort
采用分治法的排序算法
动图演示
分而治之
时间复杂度:O(NlogN)
空间复杂度:O(n)
不停的把序列分成两组,直到将序列分为一个个元素为止,再将一个个元素有序排序
int n,a[100010],temp[100010],cnt;
void mergesort(int l,int r){
if(l<r){
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
int i=l,j=mid+1,k=l-1;
while(i<=mid&&j<=r){
if(a[i]<=a[j]) temp[++k]=a[i++];
else temp[++k]=a[j++];
while(i<=mid) temp[++k]=a[i++];
while(j<=r) temp[++k]=a[j++];
for(int i=l;i<=r;i++){
a[i]=temp[i];
}
}
}
快速排序 Quick Sort
采用分治法的排序算法
在冒泡排序基础上的递归分治法。
从数列中挑出一个元素,称为 “基准”。
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区操作;
递归地把小于基准值元素的子数列和大于基准值元素的子数列排序;
时间复杂度:平均和最好情况:O(NlogN)
最坏情况:O(N^2)
空间复杂度:O(1)
void quicksort(int l,int r){
if(l>=r) return;
int i=l,j=r;
int k=a[i];
while(i<j){
while(i<j&&a[j]>=k)
j--;
swap(a[i],a[j]);
while(i<j&&a[i]<=k)
i++;
swap(a[i],a[j]);
}
quicksort(l,i);
quicksort(i+1,r);
}
堆排序 Heap Sort
利用堆这种数据结构所设计的一种排序算法
子结点的值总是小于(或者大于)它的父节点
1. 升序排列:大顶堆:每个节点的值都大于或等于其子节点的值
2. 降序排列:小顶堆:每个节点的值都小于或等于其子节点的值
1. 将待排序序列构造成一个大顶堆
2. 此时整个序列的最大值就是顶堆的根节点
3. 将其与末尾元素进行交换,此时末尾就为最大值
4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
1.假设给定无序序列结构如下
2.此时我们从最后一个非叶子节点开始。从左至右,从下至上进行调整,观察6的两个子节点,从右至左,9大于6就和6互换
3.找到第二个非叶子节点4,由于[4,9,8]中9元素最大,4和9交换
4.此时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6
5.此时我们就将一个无序序列构造成了一个大顶堆
6.将堆顶元素与末尾元素进行交换,使末尾元素最大,然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素,如此反复进行交换,重建,交换
总结
稳定性:排序过程中,相同元素在排序后的相对位置保持不变