排序数组下标都是从1开始的
一、插入类排序
直接插入排序
时间复杂度:n^2 稳定性:稳定 辅助空间: O(1)
void InsertSort()
{
int i,j;
for(i=2;i<=n;i++)
{
a[0]=a[i];
j=i-1;
while(a[0]<a[j]&&j>0) //a[i]是到第i个数为止的排序
{
a[j+1]=a[j];
j--;
}
a[j+1]=a[0]; //大于他的是后是插在他后面,所以是j+1
}
}
折半插入排序
时间复杂度 n^2 稳定性:稳定 辅助空间: O(1)
优化了一些
void BInsertSort()
{
int i,j;
for(i=2;i<=n;i++)
{
a[0]=a[i];
int left=1;
int right=i-1;
while(left<=right)
{
int mid=(left+right)/2;
if(a[0]>a[mid])
left=mid+1;
else
right=mid-1;
}
for(j=i-1;j>=left;j--)
a[j+1]=a[j];
a[left]=a[0];
}
}
希尔排序
时间复杂度:n^1.5 稳定性: 不稳定 辅助空间: O(1)
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN 1000
int n;
int a[MAXN];
void ShellInsert(int delta)
{
int i,j;
for(i=1+delta;i<=n;i++)
{
a[0]=a[i];
j=i-delta;
while(a[0]<a[j]&&j>0)
{
a[j+delta]=a[j];
j-=delta;
}
a[j+delta]=a[0];
}
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
int delta=floor(n/2);
while(delta>0)
{
ShellInsert(delta);
delta/=2;
}
for(i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
二、交换类排序
冒泡排序
时间复杂度:n^2 稳定性: 稳定 辅助空间: O(1)
void BubbleSort()
{
for(j=1;j<n;j++) // j是比较次数
for(i=1;i<=n-j;i++) //i是确保存在倒数第二个a[],i,j都是从1开始的,反正就是小的数。
if(a[i]>a[i+1]) //大的沉底就从小到大排序,小的沉底就由大到小排序
{
t=a[i];
a[i]=a[i+1];
a[i+1]=t;
}
}
快速排序
时间复杂度:nlog2n 稳定性:不稳定 辅助空间: O(log2n)
void QuickSort(int left,int right)
{
int i,j,t;
if(left<right)
{
i=left+1; //i是从left+1开始的
j=right;
while(1)
{
while(i<=n&&a[i]<a[left])
{
i++;
}
while(j>=1&&a[j]>a[left])
{
j--;
}
if(i>=j)
break;
else
{
t=a[i];
a[i]=a[j];
a[j]=t;
i++,j--;
}
}
t=a[left],a[left]=a[j],a[j]=t; //本来ij都可以,但考虑不交换的情况,只有j吻合
QuickSort(left,j-1);
QuickSort(j+1,right);
}
}
快速排序的另一种方法
int QKPass(int low,int high)
{
int x=a[low];
while(low<high)
{
while(low<high&&a[high]>=x) high--;
if(low<high) {a[low]=a[high]; low++;}
while(low<high&&a[low]<x) low++;
if(low<high) {a[high]=a[low]; high--;}
}
a[low]=x;
return low;
}
void QuickSort(int low,int high)
{
int pos;
if(low<high)
{
pos=QKPass(low,high);
QuickSort(low,pos-1);
QuickSort(pos+1,high);
}
}
三、选择类排序
直接选择排序
时间复杂度:n^2; 稳定性: 不稳定 辅助空间: O(1)
void SelectSort()
{
int i,j;
for(i=1;i<=n-1;i++) //到倒数第二个数就好
{
int k=i; //只需要一个临时空间存这个待比较的数
for(j=i+1;j<=n;j++)
{
if(a[j]<a[k])
k=j;
}
if(k!=i) //其实这个判断不必要,感觉,写起来只不过看的严谨些
{
int t;
t=a[i];
a[i]=a[k];
a[k]=t;
}
}
}
树形排序
时间复杂度: nlog2n
struct Node
{
int Data;
int Index;
int Active; //Active 用来出里空结点与不能再参与比较的结点
}Tree[MAXN];
int n;
int cnt; //记录层数
int a[MAXN];
//更新不能用初次构建的那种方法,两者不一样
void UpdateTree(int id)
{
int j;
//从叶子结点的调整
if(id%2==1)
Tree[id/2]=Tree[id-1]; //id是奇数在右边
else
Tree[id/2]=Tree[id+1];
id=id/2;
//叶子结点上层的调整
while(id) //直到 i==0
{
if(id%2==1)
j=id-1;
else
j=id+1;
//存在Active==0的情况
if(Tree[id].Active==0||Tree[j].Active==0)
{
if(Tree[id].Active==1)
Tree[id/2]=Tree[id]; //i可参选, i上
else
Tree[id/2]=Tree[j]; //否则, j上
}
else //两方都可参选
{
if(Tree[id].Data<=Tree[j].Data )
Tree[id/2]=Tree[id]; //关键码小者上
else
Tree[id/2]=Tree[j];
}
id=id/2;
}
}
void TreeSort()
{
int BottomRowSize=pow(2,1.0*(cnt-1)); //利用层数算出底层叶节点个数
int TreeSize=2*BottomRowSize-1; //总结点个数
int LoadIndex= BottomRowSize-1; //内结点个数
int i;
int j=1;
//初始化
for(i=LoadIndex+1;i<=TreeSize;i++)
{
Tree[i].Index=i;
if(j<=n)
{
Tree[i].Active=1;
Tree[i].Data=a[j++]; //已知的叶子结点 1.赋值 2.Active 标记为1
}
else
Tree[i].Active=0;
}
//第一次选出胜者
i=LoadIndex+1; //进行初始比较选择最小的项
while(i)
{
j=i;
while(j<2*i) //这里是小于2*i,<或<=每一次都套下验证
{
if(Tree[j+1].Active==0||Tree[j].Data<=Tree[j+1].Data)
Tree[j/2]=Tree[j]; //这种是三个属性一起转移的
else
Tree[j/2]=Tree[j+1];
j+=2;
}
i=i/2; //i每次只记录最左边那个位置。
}
a[1]=Tree[1].Data;
for(i=2;i<=n;i++)
{
Tree[Tree[1].Index].Active=0; //失去参选资格
UpdateTree(Tree[1].Index); //进行剩下的n-1次更新
a[i]=Tree[1].Data; //这样就是从小到大排序了
}
}
int main()
{
scanf("%d",&n);
cnt=ceil(log2(n))+1; //cnt用来记录层数
int i;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
TreeSort();
for(i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
堆排序
时间复杂度:nlog2n 稳定性:不稳定 辅助空间: O(1)
void Sift(int k,int m) //没次筛的是以a[k]为根的完全二叉树
{
int t=a[k];
int i=k; //k是不能改变的
int j=2*i;
int flag=0;
while(j<=m&&flag==0)
{
if(j+1<=m&&a[j]<a[j+1]) //j+1<=n是判断有无右子树,另一个是挑左右子树大的那个
j+=1;
if(t>=a[j])
flag=1;
else
{
a[i]=a[j];
i=j;
j=2*i; //因为要不断向下延伸,能不*2吗?
}
}
a[i]=t;
}
void Create_Heap()
{
int i;
for(i=n/2;i>=1;i--)
Sift(i,n);
}
void HeapSort()
{
int i;
Create_Heap();
for(i=n;i>=2;i--) //最后一个元素不用再调
{
int t=a[1];
a[1]=a[i];
a[i]=t;
Sift(1,i-1);
}
}
四、归并排序
时间复杂度:nlog2n 稳定性:稳定 辅助空间: O(n)
void Merge(int l,int mid,int r)
{
int i=l,j=mid+1,k=l;//i是最左边的那个数,j是中间偏右的第一个数,这样也是对称比较 明白两个数,三个数的交换就行。
while(i<=mid&&j<=r) //像两链表合并的感觉
{
if(a[i]<=a[j])
{
rrr[k]=a[i];
k++;
i++;
}
else
{
rrr[k]=a[j];
k++;
j++;
}
}
while(i<=mid)
rrr[k]=a[i],k++,i++;
while(j<=r)
rrr[k]=a[j],k++,j++;
for(int i=l;i<=r;i++)
a[i]=rrr[i]; //重新赋值的操作
}
void MergeSort(int l,int r)
{
if(r==l) return;
int mid=(l+r)/2;
MergeSort(l,mid);
MergeSort(mid+1,r);
Merge(l,mid,r);
}
五、分配类排序
基数排序
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 10010
int a[MAXN];
int bit_max=0;
int n;
void get_bit()
{
int i;
for(i=1;i<=n;i++)
{
int cnt=1;
int temp=a[i];
while(temp/10!=0)
{
temp/=10;
cnt++;
}
bit_max=max(bit_max,cnt);
}
}
void radixsort()
{
get_bit();
int num=1;
int i,j,k,p,q;
int Count[MAXN];
int temp[10][MAXN];
for(i=1;i<=bit_max;i++)
{
memset(Count,0,sizeof(Count));
memset(temp,0,sizeof(temp));
for(j=1;j<=n;j++)
{
k=(a[j]/num)%10;
Count[k]++;
temp[k][Count[k]]=a[j];
}
p=1;
for(j=0;j<=9;j++)
{
if(Count[j]>0)
{
for(q=1;q<=Count[j];q++)
{
a[p]=temp[j][q];
p++;
}
}
}
num*=10;
}
}
int main()
{
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
radixsort();
for(i=1;i<n;i++) printf("%d ",a[i]);
cout<<a[i]<<endl;
return 0;
}