最近在数据结构课程中学习了几种排序方法,说实话,以前的我用c++的sort用麻痹了,所以其他的排序方法基本都忘了,但是学了这些才发现,原来排序方法也可以有那摩多种,所以今天就来总结下我学习到的几种排序方法:
还是例题引入:
7-1 本程序题请用尽量多的排序算法测试 排序
给定N个(长整型范围内的)整数,要求输出从小到大排序后的结果。
本题旨在测试各种不同的排序算法在各种数据情况下的表现。各组测试数据特点如下:
- 数据1:只有1个元素;
- 数据2:11个不相同的整数,测试基本正确性;
- 数据3:103个随机整数;
- 数据4:104个随机整数;
- 数据5:105个随机整数;
- 数据6:105个顺序整数;
- 数据7:105个逆序整数;
- 数据8:105个基本有序的整数;
- 数据9:105个随机正整数,每个数字不超过1000。
输入格式:
输入第一行给出正整数N(≤105),随后一行给出N个(长整型范围内的)整数,其间以空格分隔。
输出格式:
在一行中输出从小到大排序后的结果,数字间以1个空格分隔,行末不得有多余空格。
1.直接插入排序法
所谓直接插入排序法,就是将数组看做左侧为有序数组,右侧为无序数组,开始时左侧只有一个元素,就是有序的,然后开始逐渐向右扩展即加入右侧的元素,但是需要注意每次右侧的数加入时,都要遍历一遍左侧的数组以确认其插入位置,即每插入一个元素就要遍历边左侧已经有序的数组,在数据量小的情况下是可行的,
#include <iostream>
using namespace std;
//直接插入法处理题目中类型的数据可行
int main()
{
int n;
cin>>n;
int a[n];
for(int i=1;i<=n;i++)
cin>>a[i];
int i,j;
for(i=2;i<=n;i++)//默认第一个数已经是有序的,所以用第二个数和第一个数作为第一次比较
{
if(a[i]<a[i-1])
{
int temp=a[i];
for( j=i-1;j>0&&a[j]>temp;--j)//要将所有左侧的数组中的有序元素都与当前待插入元素比较,用来找到插入位置
a[j+1]=a[j];
a[j+1]=temp;
}
}
cout<<a[1];
for(int i=2;i<=n;i++)
cout<<" "<<a[i];
return 0;
}
2.希尔排序法
希尔排序法是基于直接插入排序法的优化,其本质上是将数据量大的数组划分为几个子集,先将子集排序(注意是在原数组的位置上排序,移动也是在原数组的位置上移动),最后再来一次插入排序,这样做看似增添了一些步骤,但是这几部可以在数据杂乱无序程度高的条件下大大减少时间的消耗,这也是希尔排序基于直接插入排序的优化和适用条件,目前一般可将子集划分为在原数组中
每隔五个作为一个子集元素插入,或者每隔三个作为一个子集元素插入,最后是全集,即每隔零个作为一个子集插入,形成子集增量数组dk,每次取dk的一个元素作为子集划分增量,来实现子集的排序
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//希尔排序可行
int main()
{
int n;
cin>>n;
int a[n];
for(int i=1;i<=n;i++)
cin>>a[i];
//由于目前希尔子集增量数组最优设置算法尚未有算法研究,所以本次使用的是默认增量数组dk[3]={5,3,1};
int dk[3]={5,3,1};
int j,k;
for(int i=0;i<3;i++)
{
for(j=1+dk[i];j<=n;j++)
{
int temp=a[j];
if(a[j]<a[j-dk[i]])
{
for(k=j-dk[i];k>0&&a[k]>temp;k-=dk[i])//注意是对子集进行插入排序
a[k+dk[i]]=a[k];
a[k+dk[i]]=temp;
}
}
}
cout<<a[1];
for(int i=2;i<=n;i++)
cout<<" "<<a[i];
return 0;
}
3.快速排序
顾名思义,快速排序在特殊情况下可以算是比较快的排序,究其原理,是对于一个数组,每次拿出最左边或者最右边的数看做中间值,拿这个数来和数组中的其他数作比较,其最终目的是通过交换数组中元素的大小,使得拿出的数找到一个合适的位置插入(该位置的左侧的数都比拿出的数小,右侧的数都比拿出的数大),实现了这个之后,便可以通过递归来实现对其左侧数组和右侧数组同样的操作,最终达到排序的目的
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n;
void qsort(int a[],int low,int high)
{
if(low>high)//递归边界
return ;
else
{
int temp=a[low];
int i=low;
int j=high;
while(i<j)
{
while(a[j]>=temp&&i<j) j--;
a[i]=a[j];//注意此时的i等于low
while(a[i]<=temp&&i<j) i++;
a[j]=a[i];//注意此时的i的值已经改变
}
a[i]=temp;
qsort(a,low,i-1);
qsort(a,i+1,high);
}
}
int main()
{
cin>>n;
int a[n];
for(int i=1;i<=n;i++)
cin>>a[i];
qsort(a,1,n);
cout<<a[1];
for(int i=2;i<=n;i++)
cout<<" "<<a[i];
return 0;
}
4.归并排序
归并排序是稳定性与时间复杂度低二者兼得的排序方法,但是在二者兼得的条件下必然要有所牺牲那就是空间复杂的,其原理与递归非常相似,就是将数组慢慢递归划分为更小的数组,直到数组中只有一个元素时便是达到了有序的排列,最后在逐级返回,用一个函数将他们返回的值“拼起来”(归并函数),在遍历的过程中,因为需要开辟一个辅助数组来保存修改顺序后的元素各个位置上的值,所以空间复杂的比起其他的排序方法要高那抹一点(但是在数据量比较小的时候可以忽略)
归并函数听起来名字高大上,但是我说一案例你们就会恍然大悟,例如将两个有序的数组合并成一个有序的数组,你会怎么做,这就是此处归并函数存在的意义,不就是对两个数组和一个新开的数组分别设三个指针,三个指针一次比较时两个向后移动,直到有一个指针将要越界,证明其中一个数组已经被合并完,剩下的全是一个数组里的有序元素,直接往里添加就好了,最后再将辅助数组赋值还给原数组,嫌麻烦的直接输出辅助数组即可。
主要理解了递归的原理,就很容易
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//归并排序可行
int n;
int a[100005],t[100005];
void merg(int a[],int low,int mid,int high,int t[])//归并函数
{
int p_left=low,p_right=mid+1,p=low;
while(p_left<=mid&&p_right<=high)
{
if(a[p_left]<a[p_right])
{
t[p++]=a[p_left];
p_left++;
}
else
{
t[p++]=a[p_right];
p_right++;
}
}
while(p_left<=mid)//注意这个地方不能写while(p_left++<=mid)会有bug
{t[p++]=a[p_left];p_left++;}
while(p_right<=high)
{t[p++]=a[p_right];p_right++;}
for(int i=low;i<=high;i++)
a[i]=t[i];
}
void mersort(int a[],int low,int high)
{
if(low==high)
return ;
else
{
int mid=(low+high)/2;
mersort(a,low,mid);//左数组递归
mersort(a,mid+1,high);//右数组递归排序
merg(a,low,mid,high,t);//归并
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
mersort(a,1,n);
cout<<a[1];
for(int i=2;i<=n;i++)
cout<<" "<<a[i];
return 0;
}
5.脸皮很厚的c++表示他也想进数据结构项目(哈哈哈)
数据结构老师大喊(我没教你数据结构吗???)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
c++ stl
int main()
{
int n;
cin>>n;
vector<int> p;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
p.push_back(x);
}
sort(p.begin(),p.end());
cout<<*p.begin();
for(vector<int>::iterator it=p.begin()+1;it!=p.end();it++)
cout<<" "<<*it;
return 0;
}
最后,关于归并排序的非递归方法,我想了很长时间都没太弄懂,但是pta的6-2 Iterative Mergesort 貌似给出了方法,我没怎么看懂,如有大佬不吝赐教,晚辈感激不尽~~