前言
这里涉及到三种时间复杂度高的排序算法,分别是冒泡排序,选择排序,插入排序。系统学习算法,提高编程能力,不能只会用封装好的函数库,也要知道底层原理。因此,不可眼高手低。后面面对其他较难的思想时会学习到时间复杂度较低的排序算法,如快速排序等,因此这里不进行讲解。等入门算法学习结束,再对所有排序算法进行总结。
1. 冒泡排序
算法思想:
==两两交换,一趟交换下来就会把最大(或者最小)的移到最右边,一共移动n-1趟==
![](https://img-blog.csdnimg.cn/img_convert/c60a5e9f72bade5ffacf78f9db1d7103.gif)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
//冒泡排序
int main(){
int n,a[100];
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
//优化标记
int flag;
//核心代码
for(int i=1;i<=n-1;i++){ //交换n-1趟
flag=1;
for(int j=0;j<=n-1-i;j++){
//当前位置和下一位进行比较,第一次j最大到n-2(倒数第二位)
//从小到大排序,将数值大的向右移
if(a[j]>a[j+1]){
int temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
flag=0;
}
}
if(flag) break; //一趟下来没有进行大小交换,说明已经排序好
}
//输出
for(int i=0;i<n;i++){
cout<<a[i]<<' ';
}
return 0;
}
复杂度分析
时间复杂度:最坏情况:O(N^2)
最好情况:O(N)
空间复杂度:O(1)
2. 插入排序
算法思想:
选中元素i,i前面的数据已经排列完成,取出i元素在前面进行对比插入
注意:插入趟数是n-1,因为默认第一个数是已经排列好的状态,选中元素i从第二个到第n个
。若是从小到大排序,对比后移结束条件是比第i元素大则后移,小于等于第i个则插入并结束对比循环
![](https://img-blog.csdnimg.cn/img_convert/f69ff5f8742cde56d2322b5ca94dec27.gif)
#include<bits/stdc++.h>
using namespace std;
//选择排序
int main(){
int n,a[55];
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=1;i<n;i++){
int tmp=a[i],j;
for(j=i-1;j>=0;j--){
if(a[j]>tmp)
a[j+1]=a[j];
else{
//一定要跳出循环
break;
}
}
//当遇到a[j]比tmp小时,跳出循环
//此时将tmp放在a[j+1],不能在跳出循环之前插入
//就会导致最小值无法放在a[0]
a[j+1]=tmp;
}
for(int i=0;i<n;i++){
if(i==0) cout<<a[i];
else cout<<' '<<a[i];
}
return 0;
}
复杂度分析
时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)
3. 选择排序
算法思想:
每次从待排序列中选择最小值,放在序列的起始位置,知道全部排列完成。当然我们可以一趟筛选除最大值和最小值,放在末尾和首位,提高效率
容易看出有两层循环,第一层记录未排列序列的首位(和末尾),第二层来遍历筛选最小值(和最大值)
![](https://img-blog.csdnimg.cn/img_convert/1787f3dea83bbd4826b3fc1e2a4bb6b0.gif)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
//插入排序
int main(){
int n,a[100];
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
//核心代码
//未排序列,只剩最后一个相当于已排序列,即只用循环到倒数第二个
for(int i=0;i<n-1;i++){
int tmp=a[i],cnt=i;
for(int j=i;j<n;j++){ //该循环是找到
if(a[j]<tmp){
tmp=a[j];
cnt=j;
}
}
swap(a[i],a[cnt]);
}
//输出
for(int i=0;i<n;i++){
cout<<a[i]<<' ';
}
return 0;
}
同时记录最大值和最小值代码
//核心代码
//未排序列,只剩最后一个相当于已排序列,即只用循环到倒数第二个
for(int p=0,len=n-1;p<len;p++,len--){
//用临时变量存放未排序列的第一个和最后一个
int Maxtmp=a[len],Maxcnt=len,Mintmp=a[p],Mincnt=p;
//对未排序列进行遍历,找最大值和最小值
for(int i=p;i<=len;i++){
if(Maxtmp<a[i]){
Maxtmp=a[i];
Maxcnt=i;
}
if(Mintmp>a[i]){
Mintmp=a[i];
Mincnt=i;
}
}
//注意交换时容易出现的bug,如果刚好最大值在第一个,最小值在最后一个,则只需要交换一次
if(p==Maxcnt && len==Mincnt)
swap(a[p],a[Mincnt]);
else{
swap(a[p],a[Mincnt]);
swap(a[len],a[Maxcnt]);
}
}
复杂度分析
时间复杂度:最坏情况:O(N^2)
最好情况:O(N^2)
空间复杂度:O(1)