以从小到大排序举例,默认数组下标从 0开始
先从最简单的冒泡开始好了,就像他的名字,最大的数从原始位置一直冒冒冒冒到最后一位,对每一对相邻的元素进行比较,如果a[j]>a[j+1]就交换位置,比如 5 4 1 3 2 经过第一轮冒泡会变成 4 1 3 2 5。
因为一共有n个数,所以第一层循环要进行n趟排序;而在第一趟时的元素要与n-1个元素比较,而在第二趟时,第二趟的元素只需要与n-2个元素比较,其余依次类推。而下标从0开始,所以第二层循环要比较n-i-1次,代码如下:
void BubbleSort(int a[],int n)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n-i-1;j++)
{
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
}
}
}
接下来是插入排序,插入排序思想是将一个数插入到一组已经排好的数组中。
虽然给到的数据是混乱的,但我们可以把第一个元素就当一个有序的数组,而我们要将除第一个数外的所有数插入到这个有序数组中,所以第一层循环下标从1开始。在第二层循环中,每个元素只要与位于有序数组中的元素比较即可,所以下标从上一层下标开始,依次与排好序的数组中的元素比较,代码如下:
void Insertionsort(int a[],int n)
{
for(int i=1;i<n;i++)
{
for(int j=i;j>0;j--)
{
if(a[j]<a[j-1])
swap(a[j],a[j-1]);
else break;
}
}
}
再接下来是选择排序,选择排序要选出所有待排数组中的最小值放在待排数组首位,所以我们可以每次都讲数组首位元素视为最小,然后依次比较,如果有比他更小的就先储存下标,比较完所有待排元素后在交换即可,代码如下:
void Selectsort(int a[],int n)
{
for(int i=0;i<n-1;i++)
{
int min=i;
for(int j=i+1;j<n;j++)
{
if(a[j]<a[min])
min=j;
}
swap(a[i],a[min]);
}
}
最后是快速排序,首先选待排数组第一个元素a[0]作为关键元素,将剩下的数分为小于a[0]和大于a[0]两部分,分别位于a[0]的左右两边。然后对这里两部分数组在执行依次上述操作,直到排好序,代码如下:
int partition(int a[],int l,int r)
{
int key=a[l];
int j=l;
for(int i=l+1;i<=r;i++)
{
if(a[i]<key)
{
swap(a[j+1],a[i]);
j++;
}
}
swap(a[j],a[l]);
return j;
}
void Quicksort(int a[],int l,int r)
{
if(l>=r)
return;
int p= partition(a,l,r);
Quicksort(a,l,p-1);
Quicksort(a,p+1,r);
}
这种用到递归的写法就相当于是对冒泡的一种改进,其实也有点二分查找的感觉,前两天在WB看到oppo的暑期实习面试题中就有一个问题是:
“快速排序的原理是什么?快速排序要用到递归来实现吗?数据特别大的时候,快排用递归会出现什么问题?怎么解决。”
(快速排序又称为分划交换排序,主要采取分治的主要思想)
然后我就想起要写这篇排序总结,但是对于这个问题我只能回答前半部分(果然还是垃圾啊 )于是我就去看了大佬们的非递归解法。大致就是当数据过大时,用递归就会爆栈(这个我也知道,但我不会优化 )然后非递归写法用到栈,然后我的数据结构学的一塌糊涂,搬一下大佬代码(原文链接:
int pot(vector &a,int l,int r)
{
int x = a[l];
while(l<r)
{
while(l<r&&a[r]>=x)
r--;
a[l]=a[r];
while(l<r&&a[l]<x)
l++;
}
a[l]=x;
return l;
}
void quicksort(vector a,int l ,int r)
{
stack s;
int k;
s.push(l);
s.push®;
if(l<r)
{
while(!s.empty())
{
int j = s.top();
s.pop();
int i = s.top();
s.pop();
k=pot(a,i,j);
if(i<k-1)
{
s.push(i);
s.push(k-1);
}
if(k+1<j)
{
s.push(k+1);
s.push(j);
}
}
}
}
虽然我还是个小垃圾,但是我还是想吐槽一下原文作者的代码风格,每行都是顶格写,这样看起来真的好费力啊。
要是有解释不正确的地方还请各位大佬指正。