几种经典的算法在工作中一直用不到,很容易忘记,所以还是总结一下。
先实现swap函数:
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
冒泡排序(Bubble sort)
初级的冒泡排序
也是最容易想到的算法,从第一个数开始,让之后的每一个数与其比较,如果后者比前者小,则二者交换,一直到最后一个数。
现有一组数{4, 9, 10, 2, 32, 6}
,排序过程如下:
4 9 10 2 32 6
// 省略9,10的比较
2 9 10 4 32 6
// 省略32,6的比较
//第一个数成为最小的数,开始处理第二个数 9
......
代码如下:
void BubbleSort(QVector<int> &a)
{
int n = a.size();
int temp;
for(int i=0; i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
}
}
}
}
看起来像把最小的数“冒”到最左边,这种算法的效率很低,时间复杂度为O(n²)
标准的冒泡排序
从第一个数开始,每次从左到右两两比较,看起来像把最大的数“冒”到最右边。
同样是上面的数列,排序过程如下:
4 9 10 2 32 6
// 4和9
// 9和10
4 9 2 10 32 6
// 10和32
4 9 2 10 6 32
/* 上面是第一趟,排序n-1次,n为数的个数*/
以此类推,第n趟只需排序1次,所以总计n(n-1)/2
次,时间复杂度为O(n²)
,空间复杂度是O(n)
。
代码:
void BubbleSort(QVector<int> &a)
{
int n = a.size();
int temp;
// bool flag = true;
for(int i=1; i<n+1 && flag;i++) // i代表第几趟
{
// flag = false;
for(int j=0; j<n-i;j++) // n-i 代表这一趟需要排序几次
{
if(a[j]>a[j+1])
{
swap(a[j],a[j+1]);
// flag = true;
}
}
}
}
还可以对算法做出改进,增加标志位判断,若有交换数值则flag
为true,说明这一步有排序,否则就是已经排好了,减少多余步骤。
选择排序
选择排序的特点是,第一趟的交换发生在找出最小元素之后,而不像初级冒泡那样每找到一个较小的数就跟第一个交换。排序过程的评价元素其实是数组的下标,对比的值是下标的元素,而不是某个固定位置。
从下面的数列看交换过程:
4 9 10 2 32 6 1
// 先假设最小值的下标是min, 最开始的min=0 然后从第二个数开始比较
// 9>4,10>4 不处理
// 2<4, 交换下标,现在min=3
// 从32开始,与min对应的值比较,因为已经比较的数肯定是大于min的元素的
// 32和6都大于2,不处理
// 1<2,交换下标,现在min=6。 现在可以说数列中的最小值是a[6]
第一趟结束,然后交换a[6]和a[0]。
代码如下:
void MainWindow::sort(QVector<int> &a)
{
int n = a.size();
int min;
for(int i=0; i<n-1;i++)
{
min = i; // min保存最小元素的下标,假设刚开始位置i的元素最小,然后和之后每个元素比较
for(int j=i+1;j<n;j++)
{
// 只要后面的值更小就更新min,这样一趟循环下来min肯定是最小元素下标
if(a[j]<a[min])
{
min = j;
}
}
if(min!=i) // 如果最初的元素就是最小值,无需交换
{
swap(a[min],a[i]);
}
}
}
这种方法的核心就是更新min下标,与初级冒泡相比,不必访问已经有序的元素。时间复杂度还是O(n²)
简单选择排序是不稳定的排序算法,举个例子 [5, 4, 5, 3, 2]
,第一轮会将最下的2与数组第一个位置的5交换,两个等值的5相对位置发生了改变。
插入排序
插入排序的思路是从扑克牌的排序而来的。基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。
假设左手拿了几张未排序的牌,我们将第一张放到右手,然后从第二张开始,与右手的牌进行比较,选择合适的位置插入。插入排序就相当于现在只有一个手,将一组数分成两部分,左边为sorted,右边unsorted,从第二个数开始,与左侧的数逐个比较,如果比左边的数小,就进行交换,仍然以上面的数列,为例,排序过程如下:
代码如下:
void InsertSort(QVector<int> &a)
{
int n = a.size();
int value,j;
for(int i=1; i<n;i++)
{
j=i; //从第二个数开始比较
//利用了快捷运算,j成为0时,j>0为假,不必再算后面的比较。
//如果交换二者,会报错,因为a[j-1]下标为-1
while(j>0 && a[j]<a[j-1])
{
swap(a[j],a[j-1]);
j--;
}
}
}
最好情况的时间复杂度为O(n),最坏情况的时间复杂度为O(n²). 是一种稳定排序方法。