插入排序
稳定排序
O(N^2)
将未排序的头元素插入到已排序的元素中
特点:高速处理顺序较平整的数组,希尔排序的基础
void InsertSort(int a[], int N)
{
for(int i=1; i<N; i++)#i循环变量,表示未排序的开头元素下标
{
int v = a[i];#保存当前元素的值
int j= i-1;
while(j>=0 && a[j]>v)#j循环变量,用于在已排序部分寻找v的插入位置
{
a[j+1] = a[j];#相应元素后移
j--;
}
a[j+1] = v;#插入到合适位置
}
}
冒泡排序
稳定排序
O(N^2)
每一轮在未排序区比较相邻元素,筛选出最小元素到排序区尾
void BubbleSort(int a[], int N)
{
int flag = 1, i = 0;#flag标记当前轮是否存在交换,若没有则结束排序。i表示未排序区的开头元素标记,从数组开头向末尾移动
while(flag)
{
flag = 0;
for(int j=N-1; j>i; j--)#对于未排序部分中相邻元素两两比较,从N-1开始,减少到i+1结束
{
if(a[j]<a[j-1])#每轮比较相邻元素
{
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
flag = 1;
Count++;
}
}
i++;
}
}
选择排序
不稳定排序
O(N^2)
在未排序区选出最小元素,放入已排序区尾
void SelectSort(int a[], int N)
{
for(int i=0; i<N-1; i++)#i表示未排序部分的开头元素,从数组开头向末尾移动
{
int minj = i;#在各轮循环处理中,未排序区的最小元素的下标
for(int j=i+1; j<N; j++)#用来查找未排序部分中最小值的位置
{
if(a[j] < a[minj])
minj = j;
}
if(i!=minj)#可以交换
{
int temp = a[i];
a[i] = a[minj];
a[minj] = temp;
Count++;
}
}
}
希尔排序
O(N^1.25)
不同间隔的插入排序,最后一定以1为间隔来一次插入排序
特点:比较次数多,交换次数少
vector<int> G;#间隔序列
void InsertSort(int a[], int N, int g)#以g为间隔的插入排序
{
for(int i=g; i<N; i+=g)
{
int v = a[i];
int j= i-g;
while(j>=0 && a[j]>v)
{
a[j+g] = a[j];
j-=g;
cnt++;
}
a[j+g] = v;
}
}
void ShellSort(int a[], int N)
{
//生成G序列
int h = 1;
while(h<N)
{
G.push_back(h);
h = h*3+1;
}
for(int i=G.size()-1; i>=0; i--)#重复G序列次
{
InsertSort(a, N, G[i]);
}
}
归并排序
O(Nlog(N)) (O(log(N))为分割,O(N)为归并)
稳定
特点:高效且稳定,但要占用额外的内存空间(除了输入数据)。
void Merge(int S[], int left, int right, int mid)//归并(归并时比较排序)
{
int n1 = mid-left, n2 = right-mid;//以mid为界限,分割为两个数组
for(int i=0; i<n1; i++)
{
a[i] = S[i+left];
//Count++;
}
for(int i=0; i<n2; i++)
{
b[i] = S[i+mid];
//Count++;
}
a[n1] = M, b[n2] = M;//确保最后一个数较大,利于以下排序(M超出了S中元素的范围)
int k=left, ka=0, kb=0;
//Count++;
while(k<right)
{
if(a[ka]>b[kb])//升序排列,一定为大于,保证了稳定排序
{
S[k++] = b[kb++];
}
else
{
S[k++] = a[ka++];
}
Count++;//计数
}
}
void MergeSort(int S[], int left, int right)//用于分割数组,分别排序(分治)
{
if(left+1<right)//判断是否可以分割
{
int mid = (left+right)/2;
MergeSort(S, left, mid);
MergeSort(S, mid, right);
Merge(S, left, right, mid);//归并
}
}
快速排序
O(Nlog(N))(平均)O(N^2)(最坏)
不稳定
特点:内部排序,不占用额外内存。技巧在于基准x的选择(随机选择、任选几个取其平均)。
void swap(int *x, int *y)//交换函数
{
int temp = *x;
*x = *y;
*y = temp;
}
int Partition(int A[], int p, int r)//分割函数(每次排好一个元素的位置)
{
int x = A[r];//分割的标准,大于x 的为一部分,其余的为另一部分
int i=p-1;//下标小于等于i的元素不大于x
for(int j=p; j<r; j++)
{
if(A[j].n<=x.n)
{
i++;
swap(A[i], A[j]);
}
}
swap(A[i+1], A[r]);//将A[r]即x移为分割位置,此即其排序的位置
return i+1;
}
void quickSort(Card A[], int p, int r)//快排
{
if(p<r)
{
int q = Partition(A, p, r);
quickSort(A, p, q-1);
quickSort(A, q+1, r);
}
}
计数排序
O(n+k)(n是排序数组的长度,k是计数数组的长度,即排序数组中最大值)
稳定
特点:线性时间排序,所用时间和空间均与原数组的最大值成正比。
int A[MaxA], B[MaxB], C[MaxC];
//A原数组。C计数数组m,C[i]表示小于等于i的A中元素的个数。B排序后的数组。
void CountingSort(int A[], int n, int k)
{
memset(C, 0, sizeof(C));//初始化为0
for(int i=0; i<n; i++)//计算值为A[i]的元素的个数
{
C[A[i]]++;
}
for(int i=1; i<=k; i++)//计算值小于等于i的元素的个数
{
C[i] += C[i-1];
}
for(int i=n-1; i>=0; i--)//倒序(保证稳定排休)
{
B[C[A[i]]] = A[i];//注意:输出B时,要从1到n输出,因为C[A[i]](1-n)表示小于等于A[i]的元素的个数,其值一定大于0
C[A[i]]--;
}
}