#冒泡排序 #选择排序 #插入排序 #快速排序
冒泡排序的实现:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
int a[n];
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = n; i >= 1; i--) {
for (int j = 1; j <= i - 1; j++) {
if (a[j] > a[j + 1]) {
swap(a[j ], a[j + 1]);
}
}
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
return 0;
}
总结:
冒泡排序采用双层循环,假设理想的排序结果是从左到右从小到大排列。那么外层循环则从最右边的位置开始确定,内层循环从最左边的位置相邻两两进行比较,经过一趟冒泡排序使最大的元素到达最右边。经过第二趟排序使第二大的元素到达次右边的位置上......直到使所有元素有序。
选择排序的实现:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int a[n];
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = n; i >= 1; i--) {
int max = 1;
for (int j = 1; j <= i; j++) {
if (a[j] > a[max])
max = j;
}
swap(a[max], a[i]);
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
return 0;
}
总结:
与冒泡排序不同的是,选择排序是在排序之前先定义一个下标max,双层循环通过比较以max为下标的值和内层循环 a[j] 的值;如果 a[j] > max,那么更新下标max的值,遍历完一趟,外层循环交换 a[max] 和 a[i] 的位置使 a[max] 到达最右边。(ps:初始化 int max = 1 时不要在全局里定义,否则会出现每执行一次循环max都为1,导致答案错误)
插入排序的实现:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int a[n];
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 2; i <= n; i++) {
int val = a[i], j;
for (j = i; j > 1 && val < a[j - 1]; j--) {
a[j] = a[j - 1];
}
a[j] = val;
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
return 0;
}
总结:
插入排序的排序过程与冒泡、选择排序不同的是,一开始放入一个元素是有序的(显然),之后每插入一个元素放到合适的位置使元素有序,...,直至所有元素有序。首先 i=1 这一个元素 显然有序, 随后外层循环从 i = 2 时,定义一个 val 存放 a[i] 的值。内层循环从 j = i 开始,将 val 的值与 a[j-1] 进行比较,若 val < a[j-1] ,就把 a [j-1] 向右挪一个位置,j - - ;否则就把 val 插入到 a [j] 的位置上。以此循环,直到所有元素有序。
快速排序的实现:
#include <bits/stdc++.h>
using namespace std;
const int n = 1e6 + 9;
int a[n];
int partition(int a[], int l, int r) {
int i = l, j = r;
int pivot = a[r];
while (i < j) {
while (i < j && a[i] <= pivot)
i++;
while (i < j && a[j] >= pivot)
j--;
if (i < j)
swap(a[i], a[j]);
else
swap(a[i], a[r]);
}
return i;
}
void quicksort(int a[], int l, int r) {
if (l < r) {
int mid = partition(a, l, r);
quicksort(a, l, mid - 1);
quicksort(a, mid + 1, r);
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
quicksort(a, 1, n);
for (int i = 1; i <= n; i++) {
cout << a[i] << " ";
}
return 0;
}
总结:
快速排序相对比较复杂。
定义一个 partition 函数用于将原数组分成两部分,并返回中间位置的下标。该函数的参数包括数组 a、左边界 l、右边界 r。定义一个中轴元素 pivot 为右边界元素,后续通过交换使得中轴元素左边的元素都小于中轴元素pivot,中轴元素右边的元素都大于pivot。
当 i < j 时,i 从最左边向右开始遍历, j 从最右边向左开始遍历,如果 a[ i ] <=pivot, i 就 + +,直到找到存在 a[ i ] > pivot ;相同地,如果 a[ j ] >= pivot, j 就 - -,直到找到存在 a[ j ] < pivot 。此时存在且跳出了循环,如果 i < j ,就交换 a[ i ] 和 a [ j ] 的位置;否则 i 和 j 就相遇了,因为 i 在交换后再向右走一定是 a [i] > pivot ,相遇则交换 a[ i ] 和 a[ r ] 。具体实现如下图所示:
定义一个 quicksort 函数通过 partition
函数获取中间位置的下标,然后递归地对左边和右边的子数组进行快速排序。将左半部分和右半部分分别传递给新的递归调用。递归终止的条件是 l
不小于 r
,终止了,也就实现了元素升序排序。