十大排序算法
选择排序
1. 方法
从第0个元素往后遍历,针对每一个当前元素,往后找到最小的一个元素,然后与当前元素交换位置。(即为每个位置选择正确的元素)
2. 步骤
- 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
- 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
- 以此类推,直到所有元素均排序完毕
3. 动图演示
4. 代码实现
void SelectSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
int tmp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tmp;
}
}
插入排序
1. 方法:
从第1个元素开始往后遍历,如果当前元素比它前面的元素小,则向前插入。
2. 步骤:
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5。
3. 动图演示:
4. 代码实现
void InsertSort(vector<int>& arr) {
int n = arr.size();
for (int i = 1; i < n; i++) {
int current = arr[i]; //当前元素
int pre = i - 1; //前一个元素
while (pre >= 0 && arr[pre] > current) {//如果前一个元素比当前元素大,则后移
arr[pre+1] = arr[pre];
pre--;
}
arr[pre + 1] = current;
}
}
希尔排序
1. 方法
简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。即按增量将数组分组,然后对每组进行插入排序。逐渐将增量递减,递减到1时则排序完毕。
2. 步骤
- 初始增量gap=length/2=5,意味着整个数组被分为5组,对这5组分别进行插入排序
- 缩小增量gap=5/2=2,数组被分为2组,对这两组再进行插入排序
- 再缩小增量2/2=1,于是整个数组为1组,再进行插入排序得有序数组
3. 动图演示
4. 代码示例
void ShellSort(vector<int>& arr) {
int n = arr.size();
//对每组间隔为gap的分组进行排序,开始为gap=n/2;
for (int gap = n / 2; gap > 0; gap / 2) {
//对各个局部分组进行插入排序
for (int i = gap; i < n; i++) {
int curr = arr[i];
int prev = i - gap;
while (prev >= 0 && curr < arr[prev]) {
arr[prev + gap] = arr[prev];
prev -= gap;
}
arr[prev + gap] = curr;
}
}
}
冒泡排序
1. 方法:
通过不断改善局部的有序性实现整体的有序:从前向后依次检查每一对相邻元素,一旦发现逆序即交换二者的位置。对于长度为n的序列,共需做n - 1次比较和不超过n - 1次交换,这一过程称作一趟扫描交换。每一趟扫描交换下来之后,排在最右的元素就会是最大的数。
2. 步骤
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
4. 程序示例:
//未优化版本
void BubbleSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
for (int j =0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
}
}
}
//优化版本
void bubblesort1A(int A[], int n) { //冒泡排序算法
bool sorted = false; //整体排序标志,首先假定尚未排序
while (!sorted) { //在尚未确认已全局排序之前,逐趟进行扫描交换
sorted = true; //假定已经排序
for (int i = 1; i < n; i++) { //自左向右逐对检查弼前范围A[0, n)内癿各相邻元素
if (A[i - 1] > A[i]) { //一旦A[i - 1]与A[i]逆序,则
swap(A[i - 1], A[i]); //交换
sorted = false; //因整体排序不能保证,需要清除排序标志
}
}
n--; //至此末元素必然就位,故可以缩短待排序序列的有效长度
}
} //借助布尔型标志位sorted,可及时提前退出,而不致总是蛮力地做n - 1趟扫描交换
归并排序
1. 方法
归并属于分治法,第一步把数组分成左右两份,分别对左边和右边进行排序(递归), 最后把已经排序好的左边和右边合并到结果里。
2. 步骤
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
3. 动图演示:
4. 程序代码
void Merge(vector<int>& arr, int lo, int mi, int hi) {
vector<int> res;
int i = lo, j = mi+1;
while (i <= mi && j <= hi) {
if (arr[i] < arr[j]) {
res.push_back(arr[i++]);
}
else {
res.push_back(arr[j++]);
}
}
while (i <= mi) res.push_back(arr[i++]);
while (j <= hi) res.push_back(arr[j++]);
//放回原数组
for ( int i = 0; i < res.size(); i++) {
arr[lo+i] = res[i];
}
}
void MergeSort(vector<int>& arr, int left, int right) {
if (left >= right) { // 如果只有一个元素,则不用继续排,直接返回
return;
}
int mid = left + (right - left) / 2;
MergeSort(arr, left, mid);
MergeSort(arr, mid+1, right);
Merge(arr, left, mid, right);
}