前言
我知道自己又犯懒病了,最近购买了牛客网和力扣三个月的会员,逼迫自己拼一把。目前刷了一天,今天开始第二天刷题,决定更新个专栏【每日一练】,记录自己的刷题过程,自己弄不清楚的整理成笔记,发布成文章,便于自己再次梳理和巩固所学内容。
之前看过算法中的排序讲解,但是过了一个月,已经基本忘记。不练真的会忘,你说这算不算浪费时间呢?我一直不明白如果记不住的话,之前的努力到底会不会对未来有帮助?从来没有认真去思考这个问题,以后有机会多阅读相关内容。
今天发现选择排序和冒泡排序是比较类似的,都是进行n-1趟,每趟的结果类似,都是确定出最大值或最小值,只是每一趟寻找最大或最小值的中间过程是有区别的。
选择排序思想
设数组含n个元素,选择排序将n个元素从小到大排序,即进行n-1趟,每趟寻找出最小值放在数组最左边。(若是从大到小排序,则可寻找最大值放在最左边)
第1趟:找出n个元素min值放在最左边;
第2趟:找出n-1个元素min值放在左边第2个位置;
……
第n-1趟:找出2个元素min值放在左边第n-1个位置,也是右边第2个位置。
上面我们得到了n-1趟的寻找终态,下面继续介绍每一趟的中间寻找过程。
第1趟:固定min坐标,即数组左边第1个位置用来存放min值;将左边第1个数字和后n-1个数字做比较,若前者大于右边标记坐标下数字,则两者进行交换。
第2趟:固定min坐标,即数组左边第2个位置用来存放min值;将左边第2个数字和后n-2个数字做比较,若前者大于右边标记坐标下数字,则两者进行交换。
……
第n-1趟:固定min坐标,即数组左边第n-1个位置用来存放min值;将左边第n-1个位置(右边第2个)的数字和右边第1个数字做比较,若前者大于右边第1个数字,则两者进行交换。
从上述过程中,我们能够看出选择排序每趟交换最多一次。选择排序属于不稳定排序算法。
选择排序C++代码实现
输入:含6个元素的数组;
输出:数组元素从小到大排序。
#include <iostream>
using namespace std;
int main() {
int n = 6;
int arr[n] = { 0 };
cout << "第1趟排序前:" ;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// 选择排序方法
for (int i = 0; i < n-1; i++){ //n-1趟
int min = i; //每趟排序之前在下标i处设置最小值坐标标记
for (int j = i + 1; j < n; j++){
if (arr[j] < arr[min])
min = j; //寻找出每一趟中的最小值
}
int temp = arr[i]; //交换arr[i]与arr[min]
arr[i] = arr[min];
arr[min] = temp;
cout << "第" << i + 1 << "趟排序后:" ;
for (int k = 0; k < n; k++)
cout << arr[k] << ' ';
cout << endl;
}
return 0;
}
实现结果如下图所示:
从这次结果中,能够看见第3趟排序后就得到了我们需要的从小到大的排列顺序,说明算法存在冗余,存在更优的排序算法。或许只是特例,这里n太小,且输入是从大到小排列。
简单情形:一趟选择排序输出数组的最大值和最小值
若是输出数组的最大值和最小值,就相当于用选择排序方法进行一趟排序。C++代码如下:
#include <iostream>
using namespace std;
int main() {
int n = 6;
int arr[n] = { 0 };
int min = 0, max = 0;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// 输出数组中的最小值和最大值,两个值中间使用空格隔开
// 利用选择排序方法,第一趟就可以寻找出最大值和最小值
for (int j = 1; j < n; j++){
if (arr[j] < arr[min])
min = j;
if (arr[j] > arr[max])
max = j;
}
cout << arr[min] << " " << arr[max] << endl;
return 0;
}
冒泡排序思想
设数组含n个元素,选择排序将n个元素从小到大排序,即进行n-1趟,每趟寻找出最大值放在数组最右边。(若是从大到小排序,则可每趟寻找出最小值放在数组最右边)
第1趟:找出n个元素max值放在最右边;
第2趟:找出n-1个元素max值放在右边第2个位置;
……
第n-1趟:找出2个元素max值放在右边第n-1个位置,也是左边第2个位置。
上面我们得到了n-1趟的寻找终态,下面继续介绍每一趟的中间寻找过程。
可以看出寻找终态是一样的,中间寻找过程不同。
将碳酸饮料瓶子横躺,想象二氧化碳形成的气泡(每一趟的最大值或最小值)一步一步从左边移动到右边。(冒泡排序名字的由来)
注:习惯上i由小变大,所以从左到右进行比较更方便,本质上,左右顺序可以调换。
第1趟:从左到右进行两两比较,右边第n个位置的数字和右边第n-1个位置的数字,若右边第n个位置的数字>右边第n-1个位置的数字,则交换;再比较右边第n-1个的数字和右边第n-2个位置的数字,若右边第n-1个的数字>右边第n-2个位置的数字,则交换;…… ;比较右边第2个位置的数字和右边第1个位置的数字,若右边第2个位置的数字>右边第1个位置的数字,则交换。最终,得到第1趟max值。
第2趟:从左到右进行两两比较,右边第n-1个位置的数字和右边第n-2个位置的数字,若右边第n-1个位置的数字>右边第n-2个位置的数字,则交换;再比较右边第n-2个的数字和右边第n-3个位置的数字,若右边第n-2个的数字>右边第n-3个位置的数字,则交换;…… ;比较右边第2个位置的数字和右边第1个位置的数字,若右边第2个位置的数字>右边第1个位置的数字,则交换。最终,得到第2趟max值。
……
第n-1趟:从左到右进行两两比较,右边第2个位置的数字和右边第1个位置的数字,若右边第2个位置的数字>右边第1个位置的数字,则交换。最终,得到第n-1趟max值。
冒泡排序每趟交换次数明显多于选择排序。冒泡排序属于稳定排序算法。程序实际运行中,交换花费的时间通常多于比较花费的时间,因此整体而言,选择排序时间效率高于冒泡排序。
冒泡排序C++代码实现
输入:含6个元素的数组;
输出:数组元素从小到大排序。
#include <iostream>
using namespace std;
int main() {
int n = 6;
int arr[n] = { 0 };
cout << "第1趟排序前:" ;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// 冒泡排序方法
for (int i = 0; i < n-1; i++){ //n-1趟
for (int j = 0; j < n - 1 - i; j++){
if (arr[j] > arr[j+1]){
int temp = arr[j]; //交换
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
cout << "第" << i + 1 << "趟排序后:" ;
for (int k = 0; k < n; k++)
cout << arr[k] << ' ';
cout << endl;
}
return 0;
}
实现结果如下图所示:
采用从大到小顺序的数组作为输入,与选择排序算法不同,此次采取冒泡排序没有出现提早到达最终状态的情况。
题外话:
最近CSDN文章后台编辑模式有更新,能够添加高亮,每行文字左边有插入符号,可以直接选择代码和标题格式,用起来更加方便了,写作的动力又增加了,奈斯,感谢官方大大!