通俗说法:小时候上体育课排过队伍,体育老师说:你们中间谁最小谁就出列,站到队伍的最前边,然后继续对着剩余的同学说:你们中间谁最小谁就出列,站在刚才那名同学后面,这样的队伍就有了顺序,从小到大。
算法核心:在每趟比较中,找到本趟中最小的元素放在本趟比较的第1个位置,所以选择排序的每趟比较只需要交换一次即可,只要找到本趟比较中最小的元素和本趟比较中第1位置的元素交换即可。
算法简介:
选择排序是一种简单直观的排序算法。第一趟从n个元素的数据序列中选出关键字最小的元素并放在最前位置,下一趟从n-1个元素中选出最小的元素并放在最前位置。以此类推,经过n-1趟完成排序,第n个元素不用选择了,因为只剩下它一个最大的元素了。
算法思路:
首先在未排序序列中找到最小元素,存放到排序序列的起始位置;
然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾;
以此类推,直到所有元素均排序完毕。
算法描述:
1.在一个长度为 N 的无序数组中,第一次遍历 n-1 个数找到最小的和第一个数交换;
2.第二次从下一个数开始遍历 n-2 个数,找到最小的数和第二个数交换;
3.重复以上操作直到第 n-1 次遍历最小的数和第 n-1 个数交换,排序完成。
时间复杂度:
选择排序的比较次数与序列的初始排序无关。 N 个元素的比较次数总是N (N - 1) / 2。
选择排序的比较次数为O(n^2)。
n−1+n−2+n−3+⋯+1=(n−1+1)(n−1)/2=n(n−1)/2
元素间比较,双重循环。
for (i = 0; i < 15; i++) {
...
for (k = j + 1; k < 15; k++) {
...
}
...
}
而选择排序的移动次数与序列的初始排序有关。
当序列正序时,移动次数最少,为 0;
当序列反序时,移动次数最多,为3 (N - 1) 。
选择排序的移动次数为O(n)。
因为移动元素需要n-1次,而每次做出移动需要一个辅助空间,即t = a, a = b, b = t,这就是常数3的由来。
元素间移动,三次移动。
if (j != i) {
tem = a[i];
a[i] = a[j];
a[j] = tem;
}
简单排序的时间复杂度为 O(N^2)。
时间复杂度(最好、最坏、平均)均为 O(n^2)。
无论什么数据进去都是 O(n²) 的时间复杂度,所以用到选择排序的时,数据规模越小越好。
空间复杂度:
选择排序需要占用 1 个临时空间,在交换数值时使用。
O(1)
稳定性:
不稳定。
选择排序是给每个位置选择当前未排序中元素最小的,假如序列为
5 8 9 5 2 1 7
那么在第一遍选择时,第1个元素5会和2交换,原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。
稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
C++代码实现:
#include
using namespace std;
//选择排序:从小到大
int main()
{
int a[15] = { 3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
int tem;
int i, j, k = 0;
// 直接输出排序前的数
cout << "排序前:" << endl;
for (i = 0; i < 15; i++)
{
cout << a[i] << " ";
}
cout << endl;
for (i = 0; i < 15; i++) {
j = i;
for (k = j + 1; k < 15; k++) {
if (a[k] < a[j]) {
j = k;
}
}
if (j != i) {
tem = a[i];
a[i] = a[j];
a[j] = tem;
}
// 输出排序第i次
cout << "排序第" << i + 1 << "次" << endl;
for (int x = 0; x < 15; x++)
{
cout << a[x] << " ";
}
cout << endl;
}
// 顺序输出排序后的数
cout << "排序后:" << endl;
for (i = 0; i < 15; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
控制台打印结果如下:
动图演示:引用菜鸟教程https://www.runoob.com/w3cnote/selection-sort.html
通过图片可以更好理解选择排序的过程;
在图片里:黄色是已经排好的序列,浅蓝色是未排序的序列;
在代码里我用未排序序列中的第一个数据表示I,红色表示J,绿色表示K。
通俗说法:
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
算法核心:
比较两个相邻的元素,将值大的元素交换到后边
算法简介:
冒泡排序是一种简单的排序算法,它也是一种稳定排序算法。
其实现原理是重复扫描待排序序列,并比较每一对相邻的元素,当该对元素顺序不正确时进行交换。
一直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。
算法思路:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
时间复杂度:
最好:若初始元素是正序的,一趟扫描即可完成排序,比较次数n-1,时间复杂度为O(n)。
最坏:若初始元素是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),时间复杂度为 O(n^2)。
n−1+n−2+n−3+⋯+1=(n−1+1)(n−1)/2=n(n−1)/2
最好:若初始元素是正序的,无需移动,时间复杂度为O(1)。
最坏:若初始元素是反序的,每次比较都必须移动记录三次来达到交换记录位置,时间复杂度为 O(n^2)。
3(n−1+n−2+n−3+⋯+1)=3(n−1+1)(n−1)/2=3n(n−1)/2
若初始元素是正序的,比较和移动次数均达到最小值。最好的时间复杂度为O(n)。
若初始元素是反序的,比较和移动次数均达到最大值。最坏的时间复杂度为 O(n^2)。
因此冒泡排序总的平均时间复杂度为O(n^2) 。
空间复杂度:
空间复杂度就是在交换元素时那个临时变量所占的内存空间;
最优的空间复杂度就是开始元素顺序已经排好了,则空间复杂度为:0;
最差的空间复杂度就是开始元素逆序排序了,则空间复杂度为:O(n);
平均的空间复杂度为:O(1);
稳定性:
冒泡排序就是把大的元素往后调(或者小的元素往前调)。
比较是相邻的两个元素比较,交换也发生在这两个元素之间,所以,如果两个元素相等,是不会再交换的。
如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换。
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
C++代码实现:
#include
using namespace std;
void bubble_sort(int a[], int n)
{
int i, j, y, tem = 0;
bool change;
//i控制未排序的数据,change标志位来判断是否已经排序好
for (i = n - 1, change = true, y = 1; i > 1 && change; i--, y++)
{
change = false;
for (j = 0; j < i; j++)
{
if (a[j] > a[j + 1])//两个相邻数据中,前面的数据大,则交换数据
{
tem = a[j];
a[j] = a[j + 1];
a[j + 1] = tem;
}
//if判断语句不成立时,也就是两个相邻数据中,前面的数据小。
//此时不用交换数据,change的布尔值改为true。
//如果元素已经排序好,那么循环一次就直接退出。
change = true;
}
// 输出每次排序的结果
cout << "排序第" << y << "次" << endl;
for (int x = 0; x < 15; x++)
{
cout << a[x] << " ";
}
cout << endl;
}
}
int main()
{
int a[15] = { 3,44,38,5,47,15,36,26,27,2,46,4,19,50,48 };
// 直接输出排序前的数
cout << "排序前:" << endl;
for (int i = 0; i < 15; i++)
{
cout << a[i] << " ";
}
cout << endl;
bubble_sort(a, 15);
// 顺序输出排序后的数
cout << "排序后:" << endl;
for (int i = 0; i < 15; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
上述代码是优化后的代码,标志位change变量的存在使最优时间复杂度为O(n)。
优化前的代码是没有标志位change变量的,此时最优时间复杂度为双重循环O(n^2)。
控制台打印结果如下:
动图演示:
图片来源,菜鸟教程:https://www.runoob.com/python3/python-bubble-sort.html