目录
内部排序
内部排序主要是在OI中可以使用,外部排序则不能。
下面我们将讲述\(n\)种内部排序,请做好准备。
1.内省式排序
其实是多种排序算法的集合,先进行快速排序,如果序列接近有序或比较小用插入排序,如果递归层数过多则改用堆排序。
你有没有想:一开始就介绍这么多种排序算法的集合,还没学那些排序呢!
不用担心,这个东西的实现代码最简单:
sort(arr,arr+n);
如果想要逆序排序或者其他规则排序:
#include<algorithm> //一定要用这个头文件
bool cmp(obj a,obj b) //obj指你要排序对象的类型
{
... //里面的规则如果写return a<b;就是从小到大啦,sort默认从小到大。
return ...;
}
sort(arr,arr+n,cmp);
2.冒泡排序
冒泡就是先遍历数组1~n-1
的元素,如果a[i]>a[i+1]
就把他们交换,然后遍历数组2~n-1
的元素\(\dots\)最后遍历到n-1~n-1
结束。
动图如下:
效果图(可视化感觉更强):
代码如下:
template<typename T>
void BubbleSort(T arr[], T n)
{
for (int i=0;i<n-1;i++)
for (int j=0;j<n-i-1;j++)
if (arr[j]>arr[j+1])
{
T temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
还可以加个优化,如果没有交换发生(以及有序)就不用再遍历了:
template<typename T>
void BubbleSort(T arr[], T n)
{
for (int i=0;i<n-1;i++)
for (int j=0;j<n-i-1;j++)
{
bool flag=false;
if (arr[j]>arr[j+1])
{
T temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=true;
}
if (!flag) return ;
}
}
时间复杂度\(\mathcal{O}(n^2)\)
3.[冒泡排序优化]鸡尾酒排序
鸡尾酒排序,也就是定向冒泡排序,鸡尾酒搅拌排序,搅拌排序(也可以视作选择排序的一种变形),涟漪排序,来回排序或快乐小时排序,是冒泡排序的一种变形。
这个算法不是单纯的单向排序了,而是第一次从左到右冒泡,第二次从右到左冒泡\(\dots\)
这样的话会优化常数,但还是\(\mathcal{O}(n^2)\)
动图:
代码如下:
void Cocktailsort(int s[],int n)
{
int left=0;
int right=n-1;
while(left<right)
{
for(int i=left;i<right;i++)
if (s[i]>s[i+1]) swap(s[i],s[i+1]);
right--;
for(int i=right;i>left;i--)
if(s[i]<s[i-1]) swap(s[i],s[i-1]);
left++;
}
}
鸡尾酒排序也可以加优化:判断是否产生交换,代码并不难写,读者可以自己实现。
4.[冒泡排序优化]地精排序
被Dick Grune称为最简单的排序算法。整个算法只有一层循环,默认情况下前进冒泡,一旦遇到冒泡的情况发生就往回冒,直到把这个数字放好,然后继续前进,前进到数组最后一个数结束。此排序算法虽然代码极短,但效率不高。
动图:
如果看不了来这里
代码:
template<typename T>
void GnomeSort(T *a,T n)
{
int i=0;
while (i<n)
{
if (i==0||a[i-1]<=a[i]) i++;
else swap(a[i],a[i-1]),i--;
}
}
emm也是\(\mathcal{O}(n^2)\)
5.[冒泡排序逆优化]臭皮匠排序
臭皮匠排序(Stooge Sort),是一种低效的排序算法,在《算法导论》第二版第7章的思考题中被提到,是由Howard Fine等教授提出的所谓“漂亮的”排序算法。将数列平分为三个子串,依次递归排序前两个子串、后两个子串、前两个子串,最后确保整个数列有序。此算法在最坏情况下的递归式为\(T(n) = 3T(2n/3) + 1\)。由主定理很容易知道它的算法复杂性为:\(T(n) = \mathcal{O}(n^{\log_\frac{3}{2}, 3})\)。很显然\(\log_\frac{3}{2}, 3>2\),也就是说这个算法比插入排序的\(\mathcal{O}(n^2)\)性能还差。
最大就\(\mathcal{O}(n^{2.7})\)吧。。
动图:
代码如下:
void StoogeSort(int *a,int i,int j)
{
if (a[i]>a[j]) swap(a[i],a[j]);
if ((i+1)>=j) return ;
int k=(j-i+1)/3;
StoogeSort(a,i,j-k);
StoogeSort(a,i+k,j);
StoogeSort(a,i,j-k);
}
6.[冒泡排序优化]奇偶排序
你有没有想冒泡排序优化怎么这么多
奇偶排序(OddEven Sort),是一种相对简单的排序算法,最初发明用于有本地互联的并行计算。此算法通过比较数组中相邻的(奇-偶)位置数字对,如果该奇偶对是错误的顺序(第一个大于第二个),则交换。下一步重复该操作,但针对所有的(偶-奇)位置数字对。如此交替下去,直到不发生交换,则排序结束。
在并行计算排序中,使用该算法,每个处理器对应处理一个值,并仅有与左右邻居的本地互连。所有处理器可同时与邻居进行比较、交换操作,交替以奇-偶、偶-奇的顺序。该算法由Habermann在\(1972\)年最初发表并展现了在并行处理上的效率。但在单处理器串行运行此算法,类似冒泡排序,较为简单但效率并不特别高。
示意图:
最差\(\mathcal{O}(n^2)\)
代码:
void OddEvenSort(int *a, int len)
{
bool swapped=true;
while (swapped)
{
swapped=false;
for (int i=0;i<len-1;i=i+2)
if (a[i]>a[i+1])
{
swap(a[i],a[i+1]);
swapped=true;
}
for (int i=1;i<len-1;i=i+2)
if (a[i]>a[i+1])
{
swap(a[i],a[i+1]);
swapped=true;
}
}
}
冒泡排序优化当然都是稳定排序啦。
7.选择排序
每次选出一个最小的放到左边,然后依次这么做就行。
时间复杂度\(\mathcal{O}(n^2)\)
动图:
效果图(可视化感觉更强):
代码如下:
template<typename T>
void ssort(T* array,int size)
{
for(int i=0;i<size;i++)
{
T Min=array[i];
int k=i;
for (int j=i+1;j<size;j++)
if (array[j]<Min) min=array[j],k=j;
if (k!=i) array[k]=array[i],array[i]=Min;
}
}
8.[选择排序优化]堆排序
每次取最小值用堆实现,这样就更快了,可以达到\(\mathcal{O}(n\log n)\)