接下来实现使用的交换函数为:
void swap(vector<int>& arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
冒泡排序:时间复杂度O() 空间复杂度O(1),稳定
//冒泡排序
void Bubble_Sort(vector<int>& arr) {
for (int i = 0; i < arr.size() - 1; i++) {
for (int j = 0; j < arr.size() - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
选择排序:时间复杂度O() 空间复杂度O(1),不稳定
//选择排序
void Selection_Sort(vector<int>& arr) {
for (int i = 0; i < arr.size() - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.size(); j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
swap(arr, min, i);
}
}
直接插入排序:最坏时间复杂度O(),最好时间复杂度O(n),空间复杂度O(1),稳定
//插入排序
void Insertion_Sort(vector<int>& arr) {
//外层循环的含义:保证0~i是有序的,插入排序实际上是倒着进行比较
//故当数据比较好的时候,它的时间复杂度也挺好
//并不像选择和冒泡一样,固定了排序
for (int i = 1; i < arr.size(); i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j + 1] < arr[j]) {
swap(arr, j + 1, j);
}
}
}
}
二路归并排序:时间复杂度O() 空间复杂度O(n),稳定
//归并排序
void Merge(vector<int>& arr, int l, int r,int m) {
int p1 = l;
int p2 = m + 1;
int* help = new int[r - l + 1];
int index = 0;
while (p1 <= m && p2 <= r) {
help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[index++] = arr[p1++];
}
while (p2 <= r) {
help[index++] = arr[p2++];
}
for (index = 0; index < r - l + 1; index++) {
arr[index + l] = help[index];
}
}
void Merge_Sort(vector<int>& arr,int left,int right) {
if (left == right) {
return;
}
int mid = left + ((right - left) >> 1);
Merge_Sort(arr, left, mid);
Merge_Sort(arr, mid + 1, right);
Merge(arr, left, right, mid);
}
快速排序:最快的排序算法,时间复杂度O(),空间复杂度(logn),不稳定
//快速排序
int* Partition(vector<int>& arr, int L, int R) {
int less = L - 1;//左边界
int more = R;//右边界
while (L < more) {
if (arr[L] < arr[R]) {
swap(arr, ++less, L++);
}
else if (arr[L] > arr[R]) {
swap(arr, --more, L);
}
else {
L++;
}
}
int* ret = new int[2];
swap(arr, R, more);
ret[0] = less + 1;
ret[1] = more;
return ret;
}
void Quick_Sort(vector<int>& arr,int L,int R) {
if (L < R) {
swap(arr, R, L + rand() % (R - L + 1));//随机取一个值作为partition的根据值
int* ret = Partition(arr, L, R);
Quick_Sort(arr, L, ret[0] - 1);
Quick_Sort(arr, ret[1] + 1, R);
}
}
为什么quickSort中的递归base case是L>=R就终止递归?
比如现在分割到只剩两个元素的情况下,那么partition返回的数组中的两个元素的值其实是一样的(都指向同一个位置),那么ret[0] - 1就可能变成一个比L小的数,这样肯定是不合理的(注意是可能,也有可能变成和L相同的数,但如果是这种条件的话,ret[1]+1就会变得比R大,那么还是造成了L > R)
而在归并中是不会这样的,故归并的base case只要是L == R即可
堆排序:时间复杂度O(),空间复杂度O(1),不稳定
//堆排
void heapify(vector<int>& arr, int index, int heapsize) {
int left = index * 2 + 1;
while (left < heapsize) {
int max = left + 1 < heapsize && arr[left] < arr[left + 1] ? left + 1 : left;
max = arr[index] > arr[max] ? index : max;
if (max == index) {
return;
}
swap(arr, max, index);
index = max;
left = index * 2 + 1;
}
}
void heapInsert(vector<int>& arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
void Heap_Sort(vector<int>& arr) {
int heapsize;
for (heapsize = 0; heapsize < arr.size(); heapsize++) {
heapInsert(arr, heapsize);
}
swap(arr, 0, --heapsize);
while (heapsize > 0) {
heapify(arr, 0, heapsize);
swap(arr, 0, --heapsize);
}
}
以上都是基于比较的排序,基于比较的意思是都有用到大于、小于、等于的操作符,所以这些排序都可以改成通用的排序算法,让用户提供一个二元谓词的比较器,然后根据这个比较器来进行排序
工程上的排序算法是如何进行优化的呢?
首先我们写一个排序算法的接口,经过实验我们得知,当数据量较小时,使用O()的算法实际所花费时间更少,当数据量多的时候,再使用快排等时间复杂度为O(nlogn)的算法
想要常数时间快,那就选择快速排序;想要稳定,那就使用归并排序;想要节省空间,那就使用堆排序
稳定:若记录序列中有两个或两个以上相等的元素,那么其排序后的相对位置是否改变叫做稳定或者不稳定
目前已知的排序算法最快也就nlogn,你可能会看到许多改进的算法,但请你记住,有失必有得,你想更快,那么空间复杂度和稳定性可能就会变差
如下面的希尔排序,是插入排序的改进算法,它变得更快了,但不再稳定
以下是其他的排序算法的补充(学习数据结构ing)
希尔排序:时间复杂度O(),空间复杂度O(1),不稳定
直接插入排序只要数据越有序,那么其排序速度越快,那么如果有一种方法能使得待排序序列越来越有序,此时使用直接插入排序那不就变快了嘛,这就是希尔排序的核心思想
利用希尔增量将待排序序列分成增量个组,组内进行直接插入排序,直到增量 = 1时,即对整个序列进行一次直接插入排序
//希尔排序
//缩小增量法,将整个待排序序列进行分组,组内进行直接插入排序
//数据越有序,直接插入排序越快
void Shell_Sort(vector<int>& arr) {
int gap = arr.size();
while (gap > 1) {
gap = gap / 3 + 1;//希尔增量进行变化
for (int i = 0; i < arr.size() - gap; i++) {
int end = i;//有序序列
int temp = end + gap;//无序序列首元素
while (end >= 0) {
if (arr[end] > arr[temp]) {
swap(arr, end, temp);
temp = end;
end -= gap;
}
else {
break;
}
}
}
}
}
#include<stdio.h>
#include <stdlib.h>
#include<vector>
#include <iostream>
using namespace std;
template<class T>
void shell_Sort(vector<T>& arr) {
int gap = arr.size();
while (gap > 1) {
gap = gap / 3 + 1;
//这里i从gap开始,也就是每个组的第二个元素
//然后进行插入排序,也是一个不完全的插入排序
//在希尔增量为1时,就是一个从数组1位置开始的插入排序了
for (int i = gap; i < arr.size(); i++) {
for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap) {
T temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
基数排序:时间复杂度O(n),空间复杂度O(n),稳定
一种不基于比较的排序算法,也被称为数字排序或者桶排序(因为有入桶出桶操作),针对于全是数字的有效的排序算法
先根据每一位(个位十位百位等)来进行局部排序,入桶和出桶操作遵循先进先出的准则
为什么要先进先出,比如10和12,如果从左往右入桶的话,个位排完出桶是10、12,而十位也是10先进去,只要是十位数相同的,就会根据个位数来进行排序
这也侧面说明了其是稳定的(因为先进先出嘛)
//基数排序/桶排序
int maxbits(vector<int>& arr) {
int res = 0;
int max = INT_MIN;
for (int i : arr) {
if (i > max) {
max = i;
}
}
while (max != 0) {
res++;
max /= 10;
}
return res;
}
int getdigit(int num, int digit) {
int res;
while (digit > 0) {
res = num % 10;
num /= 10;
digit--;
}
return res;
}
void radix_sort(vector<int>& arr, int L, int R, int digit) {
const int radix = 10;
int* help = new int[R - L + 1];
for (int d = 1; d < digit; d++) {
int* count = new int[radix]();
int j;
for (int i = L; i <= R; i++) {
j = getdigit(arr[i], d);
count[j]++;
}
for (int i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
for (int i = R; i >= L; i--) {
j = getdigit(arr[i], d);
help[--count[j]] = arr[i];
}
for (int i = 0; i < R - L + 1; i++) {
arr[i + L] = help[i];
}
}
}
void Radix_Sort(vector<int>& arr) {
radix_sort(arr, 0, arr.size() - 1, maxbits(arr));
}