1.快速排序(使用最常见的排序算法)
算法分析
- 快速排序是一个分治思想,平均复杂度是n*lg(n)。
Divide:数组A[p…r]分为两个子数组A[p…q-1],A[q+1…r],使得A[p…q-1]上所有元素小于A[q];A[q+1…r]上所有的元素都大于A[q]。
Conquer:通过对快排的递归,对两个子数组A[p…q-1]和A[q+1…r]进行排序。
Combine:分完就已经有序。
实现
//=========快排算法=========
//使得arr[low]..arr[q-1]中的每一个元素都小于等于arr[q], arr[q]也小于等于arr[q+1]..arr[high]。
//简而言之,让左边的数字都小于右边的数字
//最坏时间复杂度:n^2 , 平均时间复杂度:n*lg(n)
//优化:为了使得输入均匀的分布,对输入使用随机数
//排序一个数组,初始调用Qsort(A,0,A.size()-1).
//=========================
int partirtion(int A[],int low,int high){
int x = A[high];
int i = low - 1;
for(int j = low;j < high; j++){
//左边的数字如果大于右边的数字,就交换位置
if(A[j] <= x){
i++;
//互换A[i]和A[j]
int tmp = A[i];
A[i] =A[j];
A[j] = tmp;
}
}
//将主元(pivot)给到i的下一个
int temp = A[i+1];
A[i+1] = A[high];
A[high] = temp;
return i+1;
}
void Qsort(int arr[], int low, int high){
if(low < high){
int q =partirtion(arr,low,high);
Qsort(arr,low,q-1);
Qsort(arr,q+1,high);
}
}
2.插入排序
算法分析
- 适用于少量元素的排序,工作方式就像是排序一副扑克牌,每次从右向左对每一张牌进行比较。最坏情况是n2。
实现
//=============插入排序==========
//从插入的位置起步,往前作比较,输入n指长度
//===============================
void insertSort(int A[],int n){
for(int i = 1;i < n;i++){
//把A[i]插入到A[1..i-1]中
int key = A[i];
int j = i - 1;
while(j >0 && A[j] > key){
A[j+1] = A[j];
j--;
}
//j+1就是需要插入的位置
A[j + 1] = key;
}
}
3.并归排序
算法分析
- 归并排序完全按照分治的思想,关键的操作在于合并步骤,需要时间复杂度为n。
实现
//===========归并排序==========
//Merge就是合并的过程,相当于在两堆排好序的牌中,每次选取最上面的两张进行比较,选取其中较小的一张
//A是数组,p<=q<r,Merge过程假设A[p..q]和A[q+1..r]都已经排好序了,元素的总数是:n = r-p+1
//r,q,p都是指的下标
//=============================
void merge(int A[], int p, int q, int r){
//n1 n2 就是两个新数组的界
int n1 = q - p +1;
int n2 = r - q;
//将这两个赋值为左右两个新数组,最后一个数放入一个最大数作为结尾标志
int *L = new int[n1+1];
int *R = new int[n2+1];
for(int i = 0; i < n1; i++){
L[i] = A[p+i];
}
for(int i = 0; i < n2; i++){
R[i] = A[q+i+1];
}
L[n1] = INT8_MAX;
R[n2] = INT8_MAX;
//i记录左边数组的下标,j记录右边数组的下标
int i = 0, j = 0;
for(int k = p; k < r; k++){
if(L[i] <= R[j]){
A[k] = L[i];
i++;
} else{
A[k] = R[j];
j++;
}
}
}
void mergeSort(int A[], int p, int r){
if(p < r){
int q = (p+r)/2;
mergeSort(A,p,q);
mergeSort(A,q+1,r);
merge(A,p,q,r);
}
}
4.计数排序
算法分析
- 计数排序的基本思想是:对每一个输入元素x,确定小于x的元素个数。利用这个性质就可以直接把x放到指定输出数组的位置上了。适用于数组中的元素都小于数组中的最大元素的情况。
实现
//===========计数排序==============
//函数输入为:input输入数组、n是输入和输出数组的长度、k是数组中的最大数
//================================
void countSort(int input[], int n, int k){
int* counter = new int[k+1];
int* output = new int[n];
//初始化计数器
for(int i = 0; i <= k;i++){
counter[i] = 0;
}
//记录输入有多少个当前counter下标的值
for(int i = 0;i < n;i++){
counter[input[i]]++;
}
//对每一个下标记录与之前的求和,求和的结果就是下标在输出数组的位置
for(int i = 1; i <= k;i++){
counter[i] = counter[i]+counter[i-1];
}
//通过计数器寻址,对输出赋值
for(int i = n-1;i >=0;i--){
//个数还需要减1,成为下标
output[counter[input[i]]-1] = input[i];
counter[input[i]]--;
}
//将输出给输入
for(int i = 0;i < n;i++){
input[i] = output[i];
}
}
5.基数排序
算法分析
- 基数排序是对每一个数的每一位进行排序,运行时间为n,比快排的n*lg(n)要好。
实现
//=========基数排序===============
//先求出所有数中最大数字的位数
//再根据键值的每一位数字,来分配一个桶
//===============================
int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
int maxData = data[0]; ///< 最大数
/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
for (int i = 1; i < n; ++i)
{
if (maxData < data[i])
maxData = data[i];
}
int d = 1;
int p = 10;
while (maxData >= p)
{
//p *= 10; // Maybe overflow
maxData /= 10;
++d;
}
return d;
}
void radixsort(int data[], int n) //基数排序
{
int d = maxbit(data, n);
int *tmp = new int[n];
int *count = new int[10]; //计数器
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) //进行d次排序
{
for(j = 0; j < 10; j++)
count[j] = 0; //每次分配前清空计数器
for(j = 0; j < n; j++)
{
k = (data[j] / radix) % 10; //统计每个桶中的记录数
count[k]++;
}
for(j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
{
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
for(j = 0; j < n; j++){
data[j] = tmp[j];
}
radix = radix * 10;
}
delete []tmp;
delete []count;
}