//选择、插入、冒泡、快速、归并、希尔、堆、计数、基数、桶排序 这10种经典的内排序算法及其衍生算法,得烂熟于心
//算法性能评判的准则包括时间复杂度、空间复杂度、常数项、稳定性
//排序算法分为线性算法和非线性算法,把时间复杂度为O(N)的算法称为线性算法
//各算法的公认性能参照 https://blog.csdn.net/opooc/article/details/80994353
//N!>x^n>…>3^n>2^n>N^x>…>N^3>N^2>NlogN>N>logN>1
/
数据是随机整数,时间单位是秒
数据规模|快速排序 归并排序 希尔排序 堆排序
1000万 | 0.75 1.22 1.77 3.57
5000万 | 3.78 6.29 9.48 26.54
1亿 | 7.65 13.06 18.79 61.31
/
//1.系统的sort 方法,发现传进来的值为数值型,会使用快排,如果发现传的还有比较器,会使用归并排序
//2.归并和快排哪种更快?
// 快排比归并排序的常数项要低,所以要快。
//3.为什么会有归并和快排两种呢?
// 在比较的时候,使用比较器的时候,要追求一个稳定性,使用 归并排序 可以达稳定性的效果;使用快排不能够实现稳定性的效果。
//4.面对大规模的时候,当排序量是小于等于60的时候,sort方法 会在内部使用插入排序的方法(不一定是60,是一定的规模)当数据量很低的时候,插入排序的常数项低。
//5.在c语言中有一版,把归并排序,改成非递归,是基于工程其他考虑。
//6.预防快排在数据有序时表现的N**2的时间复杂度,算法导论提出,用一个按特定规则取出的中位数作为pivot比较合适,这个规则为按照3或5个数一组取出它们的中位数,再在这些中位数中按同样的方法去除最终的那个总的中位数,即是第一轮的pivot,pation后按此方法递归左右两部分。
//7.稳定的排序算法:冒、插、归的三种标准写法+3种线性排序,选择排序为不稳定
冒泡
//经典冒泡
void bubble(int arr[], int len) {
for (int i = 0; i < len; ++i)
for (int j = 1; j < len - i; ++j)
if (arr[j - 1] > arr[j]) exchange(arr, j - 1, j);
}
外层优化
//用一个flag来判断一下,当前数组是否已经有序
void bubbleI(int arr[], int len) {
for (int i = 0; i < len; ++i) {
bool isOrder = true;
for (int j = 1; j < len - i; ++j)
if (arr[j - 1] > arr[j]) {
exchange(arr, j - 1, j);
isOrder = false;
}
if (isOrder) return;
}
}
内层优化
//用一个pos来记录上一轮冒泡中最后发生交换的位置
void bubbleII(int arr[], int len){
int isOrdered;
int right=len;
int currentRight;
for(int i=0;i<len;++i){
isOrdered=true;
for(int j=1;j<right;++j){
if(arr[j-1]>arr[j]){
swap(arr,j-1,j);
isOrdered=false;
currentRight=j;
}
}
if(isOrdered) return ;
right=currentRight;
}
}
插入排序
void insert(int arr[],int len) {
for (int i = 1; i < len; ++i)
for (int j = i; j > 0; --j){
if (arr[j - 1] > arr[j])
exchange(arr, j - 1, j);
else
break;
}
}
选择排序
void select(int arr[],int len) {
for (int i = 0; i < len; ++i) {
int minIdx = i;
for (int j = i + 1; j < len; ++j){
if (arr[minIdx] > arr[j])
minIdx = j;
}
exchange(arr, i, minIdx);
}
}
快速排序
本质上是二叉树的先序遍历
void quick(int arr[], int len) {
if (len < 2) return;
quickSort(arr, 0, len - 1);
}
}
//quickSort
void quickSort(int arr[], int left, int right) {
if (left < right) {
srand(time(0));
int random = rand() % (right - left + 1) + left;
exchange(arr, random, right);
int pivot = separate(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
}
}
int separate(int arr[], int left, int right) {
int base = arr[right];
int i = left;
int j = right;
while (i < j) {
while (i < j&&arr[i] <= base) ++i;
arr[j] = arr[i];
while (i<j&&arr[j]>=base) --j;
arr[i] = arr[j];
}
arr[i] = base;
return i;
}
归并排序
本质上是二叉树的后序遍历
void mergeSort(int arr[], int len) {
if (len < 2) return;
mergeSort(arr, 0, len - 1);
}
//mergeSort
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid+1, right);
merge(arr, left, mid, right);
}
}
void merge(int& arr[], int left, int mid, int right) {
int* t = new int[right - left + 1];
int i = left, j = mid+1;
int k = 0;
while (i <= mid&&j <= right)
t[k++] = arr[i] > arr[j] ? arr[i++] : arr[j++];
while (i <= mid)
t[k++] = arr[i++];
while (j <= right)
t[k++] = arr[j++];
for (int i = 0; i < right - left + 1; ++i)
arr[left + i] = t[i];
delete []t;
}
堆排序
根大堆用来求TopK小的元素,根小堆则是求TopK大的元素
分为初始化堆和维护堆两个操作
//根大堆排序法
void heap(int arr[], int len) {
if (len < 2) return;
for (int i = 0; i < len; ++i)
buildHeap(arr, i);//建堆
int tail = len - 1;
while (tail > 0) {
exchange(arr, 0, tail);
keepHeap(arr, 0, tail);//维护堆
tail--;
}
}
//往上走建堆
void buildHeap(int arr[], int index) {
while (arr[index] > arr[(index - 1) / 2]) {
exchange(arr, (index - 1) / 2, index);
index = (index - 1) / 2;
}
}
//往下走维护堆,时间复杂度仅为log(N)
void keepHeap(int arr[], int index, int size) {
while (index * 2 + 1 < size) {//因为是完全二叉树结构,只要保证节点的左孩子有效既可,左孩子无效右孩子一定无效
int leftChild = index * 2 + 1;
int maxChild = leftChild;
if (leftChild + 1 < size&& arr[leftChild + 1]>arr[leftChild])
maxChild = leftChild + 1;
if (arr[index] >= arr[maxChild]) return;
exchange(arr, index, maxChild);
index = maxChild;
}
}
topk问题
include
1、nth_element 不排序,只做快选式partion
2、partition会分类但不排序
3、partial_sort 既partion也排序
4、自己写NlogK的堆排序
1、nth_element 不排序,只做快选式partion
2、partition会分类但不排序
3、partial_sort 既partion也排序
4、自己写NlogK的堆排序