1. 交换排序
冒泡排序
最快 | 平均 | 最慢 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|
o(n) | o(n2) | o(n2) | o(1) | 稳定 | 简单 |
冒泡排序是一种用时间换空间的排序方法
最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序,最差时间复杂度O(N^2)只是表示其操作次数的数量级。
最好的情况是数据本来就有序,复杂度为O(n)
#include <stdlib.h>
#include <time.h>
#include <sys/timeb.h>
#include<iostream>
#define NUM 10000
using T = int;
void print(T arr[], int nNum);
void swap(T *a, T *b);
long getSystemTime();
void BubbleSort(T arr[], int nNum)
{
T i = 0, j = 0;
for (i = 0; i < nNum; ++i)
for (j = 1; j < nNum - i; ++j)
if (arr[j - 1] > arr[j])
{
swap(&arr[j - 1], &arr[j]);
}
}
int main()
{
int arr[NUM] = { 0 };
long t_start = 0, t_end = 0;
srand((unsigned int)time(NULL));
for (int i = 0; i < NUM; ++i)
{
arr[i] = rand() % 100;
}
//print(arr,NUM);
t_start = getSystemTime();
BubbleSort(arr,NUM);
t_end = getSystemTime();
//print(arr, NUM);
std::cout << "time:" << t_end - t_start << std::endl;
return 0;
}
void swap(T *a, T *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
void print(T arr[], int nNum)
{
for (int i = 0; i < NUM; ++i)
{
std::cout << arr[i];
std::cout << ",";
}
std::cout << "\b\n" << std::endl;
}
long getSystemTime()
{
struct timeb tb;
ftime(&tb);
return tb.time * 1000 + tb.millitm;
}
快速排序
所有排序算法里命名最嚣张的一个。
最快 | 平均 | 最慢 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|
o(N*log2N) | o(N*log2N) | o(n2) | o(log2N)-o(N) | 不稳定 | 复杂 |
快速排序以空间换时间,但却是效率高不稳定的排序算法。元素越多优势越明显。
划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(N^2)
最好的情况是每次都能均匀的划分序列,O(N*log2N)
void QuickSort(T arr[], int start, int end)
{
int left = start, right = end;
T base = arr[start];
if (left < right)
{
while (left < right)
{
// search <base from right
while (left < right && arr[right] >= base) --right;
if (left < right)
{
arr[left] = arr[right];
++left;
}
// search >base from left
while (left < right && arr[left] < base) ++left;
if (left < right)
{
arr[right] = arr[left];
--right;
}
}
arr[left] = base;
QuickSort(arr, 0, left - 1);
QuickSort(arr, left + 1, end);
}
}
2. 选择排序
直接排序
最快 | 平均 | 最慢 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|
o(n) | o(n2) | o(n2) | o(1) | 不稳定 | 简单 |
比冒泡快,因为交换少。
void SelectSort(T arr[], int nNum)
{
T i = 0, j = 0;
for (i = 0; i < nNum; ++i)
{
T min = i;
for (j = 1; j < nNum - i; ++j)
{
if (arr[j - 1] > arr[j])
{
min = j;
}
}
if (min != i)
{
swap(&arr[min], &arr[i]);
}
}
}
堆排序
最坏,最好,平均时间复杂度均为O(nlog2n),不稳定且复杂。
堆是一种完全二叉树:
- 大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
- 小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]
步骤:
- 将序列构建称为大顶堆;
- 取出根节点,与末尾元素交换;
- 对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质,然后交换;
总之就是建堆和交换,不断重复。
#include <windows.h>
#include <iostream>
using namespace std;
using T = int;
void print(T arr[], int nNum)
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { 0 };
short x = 40, y = 0;
int rowNum = 1;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOut, pos);
printf("%2d",arr[0]);
for (int i = 1; i < nNum; i += rowNum)
{
x -= 3;
pos.X = x;
pos.Y = ++y;
SetConsoleCursorPosition(hOut, pos);
rowNum *= 2;
for (int j = 0; j < rowNum && i + j < nNum; ++j)
{
printf("%2d ", arr[i + j]);
}
}
system("pause");
system("cls");
}
void HeapAdjust(T arr[], int index, int nLen)
{
int max = index;
int lchild = index * 2 + 1;
int rchild = index * 2 + 2;
if (lchild < nLen && arr[lchild] > arr[max])
{
max = lchild;
}
if (rchild < nLen && arr[rchild] > arr[max])
{
max = rchild;
}
if (max != index)
{
T tmp = arr[max];
arr[max] = arr[index];
arr[index] = tmp;
HeapAdjust(arr,max,nLen);
}
}
void HeapSort(T arr[], int nLen)
{
// 1. Init big root heap
for (int i = nLen / 2 - 1; i >= 0; --i)
{
HeapAdjust(arr, i, nLen);
}
// 2. swap
for (int i = nLen - 1; i >= 0; --i)
{
T tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
HeapAdjust(arr, 0, i);
}
}
int main()
{
T arr[] = {4,2,8,0,5,7,1,3,9};
print(arr, _countof(arr));
HeapSort(arr, _countof(arr));
print(arr, _countof(arr));
system("pause");
return 0;
}
3. 插入排序
直接插入
类似打扑克。。。
两种情况下效率高:
- 序列基本有序
- 元素少
最快 | 平均 | 最慢 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|
o(n) | o(n2) | o(n2) | o(1) | 稳定 | 简单 |
void InsertSort(T arr[], int nNum)
{
T i = 0, j = 0;
for (i = 1; i < nNum; ++i)
{
if (arr[i] < arr[i - 1])
{
T min = arr[i];
for (j = i- 1;
j >= 0 && min < arr[j];
--j)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = min;
}
}
}
希尔排序
最快 | 平均 | 最慢 | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|
o(n) | o(n^(1.3)) | o(n2) | o(1) | 不稳定 | 复杂 |
实质上是一种分组插入方法,又称缩小增量排序,是插入排序的高效版本。
数列有n个元素,取一个小于n的整数gap,将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。
步长递减:gap = gap / 3 + 1;
void ShellSort(T arr[], int nNum)
{
T i = 0, j = 0, k = 0;
int increasement = nNum;
do
{
increasement = increasement / 3 + 1;
for (i = 0; i < increasement; ++i)
{
/*
The same as InsertSort()
*/
for (j = i + increasement; j < nNum; j += increasement)
{
if (arr[j] < arr[j - increasement])
{
T min = arr[j];
for (k = j - increasement;
k >= 0 && min < arr[k];
k -= increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = min;
}
}
}
} while (increasement > 1);
}
4. 归并排序
3种时间复杂度均为o(nlog2n),空间复杂度o(n)。稳定但复杂。
数量达到一定级别后,必须利用多线程,计算后合并。
void Merge(T arr[], int start, int end, int mid)
{
int i_start = start, i_end = mid;
int j_start = mid + 1, j_end = end;
int nLen = 0;
T buf[NUM] = { 0 };
while (i_start <= i_end && j_start <= j_end)
{
if (arr[i_start] < arr[j_start])
{
buf[nLen] = arr[i_start];
++nLen;
++i_start;
}
else
{
buf[nLen] = arr[j_start];
++nLen;
++j_start;
}
}
while (i_start <= i_end)
{
buf[nLen] = arr[i_start];
++nLen;
++i_start;
}
while (j_start <= j_end)
{
buf[nLen] = arr[j_start];
++nLen;
++j_start;
}
for (int i = 0; i < nLen; ++i)
{
arr[start + i] = buf[i];
}
}
// interface
void MergeSort(T arr[], int start, int end)
{
if (start == end) return;
int mid = (start + end) / 2;
MergeSort(arr,start,mid);
MergeSort(arr, mid + 1, end);
Merge(arr,start, end, mid);
}
5. 基数排序
最坏,最好,平均时间复杂度O(d(k+n)),稳定,复杂。
空间复杂度:O(n+k)
k为常数
稳定,复杂(代码还没看懂)。
算法:按位(个位、十位、百位。。。)排序,
基数排序不是比较排序,而是通过分配和收集的过程来实现排序
初始化10个桶(固定的),桶下标为0-9
根据待排序数字的对应位的数字,把这个数字对应的item放到对应的桶中
两种排序方式:LSD和MSD,最小位优先(从右边开始)和最大位优先(从左边开始)
#include <iostream>
int maxbit(int arr[], int nLen)
{
int max = 1; //保存最大的位数
int p = 10;
for (int i = 0; i < nLen; ++i)
{
while (arr[i] >= p)
{
p *= 10;
++max;
}
}
return max;
}
void RadixSort(int arr[], int nLen) //基数排序
{
int d = maxbit(arr, nLen);
int *tmp = new int[nLen];
int count[10]; //计数器
int i, j, k;
int radix = 1;
for (i = 0; i < d; ++i) //进行d次排序
{
memset(count, 0, 10 * sizeof(int));
for (j = 0; j < nLen; ++j)
{
k = (arr[j] / radix) % 10; //统计每个桶中的记录数
count[k]++;
}
for (j = 1; j < 10; ++j)
count[j] += count[j - 1]; //将tmp中的位置依次分配给每个桶
for (j = nLen - 1; j >= 0; --j) //将所有桶中记录依次收集到tmp中
{
k = (arr[j] / radix) % 10;
tmp[ count[k] - 1 ] = arr[j];
--count[k];
}
for (j = 0; j < nLen; ++j) //将临时数组的内容复制到arr中
arr[j] = tmp[j];
radix = radix * 10;
}
delete[] tmp;
}
int main()
{
int arr[10] = {9,51,50,10,54,16,78,51,23};
RadixSort(arr,10);
for (int i = 0; i < 10; ++i)
{
printf("%d ",arr[i]);
}
return 0;
}