(一)插入排序
算法时间复杂度:O(N2)
算法实现步骤(设需要排序的数组为a[]):
1.构造一个新数组copy[],内容为空;
2.从前向后取出a[]中的元素,取出的元素记为a[i];
3.每取出一次元素,将a[i]与copy[]中的元素(i之前的位置,这里新开了一个变量index=i用来在copy[]中移位)进行比较,从copy[index-1]开始比较,如果小于的话则copy[index-1]向后移一位,空出index-1的位置,然后index自减,下一次的比较则是a[i]和copy[i-2],而copy[i-1]留空,copy[i]存入原先的copy[i-1](max值);
4.直到a[i]大于copy[]中的某个元素,此时将a[i]插入上一次比较留下的空位copy[index];
5.对所有a[]中的数遍历一遍即可在copy[]中完成排序;
/**
*@name Sort_insert_array:使用插入排序对数组进行排序
*@param1 a:需要排序的数组名
**/
void Sort::Sort_insert_array(int* a,int N)
{
int copy[100000]={0};
int temp;
int index;
copy[0]=a[0];
for (int i=1;i<N;i++)
{
temp=a[i];
index=i;
//默认copy在i之前的序列都已排序完毕
//将a[i]从copy的i-1开始向前比较,如果小于,则copy在该位置后移,留出空位
while((index>0)&&(temp<copy[index-1]))
{
//必须先移位再自减
copy[index]=copy[index-1];
index--;
}
copy[index]=temp;
}
for (int i=0;i<N;i++)
{
a[i]=copy[i];
}
}
注:
1.必须先移位再自减,因为自减是针对下一次操作的。
(二)归并排序
算法时间复杂度:O(Nlog(N))
算法简介:合并两个已排序的表
算法实现步骤:
1.利用递归实现分治,将前半部分数据和后半部分数据各自进行归并排序;
2.然后将排序完成的两部分数据进行合并;
3.合并的过程:
(1)用一个指针指向前半部分数据头部,另一个指针指向后半部分数据头部;
(2)对两指针分别指向的数据进行比较,小的数据放入存放排序结果的数组(这里需要一个index来记录存放数据的位置,即已排序的长度),对应的指针自加;
(3)如果一指针已将某一部分的数据全部遍历完全,则直接将另一部分剩下的数据全部导入新数组的剩余位置;
/**
*@name merge:对数组的[start]-[end]经过排序后合并
*@param1 a:传入的需要排序的数组
*@param2 copy:中间量的数组
*@param3 start:需要合并的起始位置
*@param4 midden:需要合并的中间位置
*@param5 end:需要合并的末端位置
**/
void merge_array(int* a,int *copy,int start,int midden,int end)
{
int ptr_i=start;
int ptr_j=midden+1;
int ptr_k=start;
int len=end-start;
//首先先从start和midden+1开始比较,直到满足某一序列已全部进入copy
while((ptr_i<=midden)&&(ptr_j<=end))
{
if(a[ptr_i]<a[ptr_j])
copy[ptr_k++]=a[ptr_i++];
else
copy[ptr_k++]=a[ptr_j++];
}
//将start-midden序列中剩下的元素全部丢入copy
while(ptr_i<=midden)
{
copy[ptr_k++]=a[ptr_i++];
}
//将midden+1-end序列中剩下的元素全部丢入copy
while(ptr_j<=end)
{
copy[ptr_k++]=a[ptr_j++];
}
//将copy中的元素反赋值给a
for(int i=start;i<=end;i++)
{
a[i]=copy[i];
}
}
/**
*@name Sort_mergesort:归并排序
*@param1 a:传入的需要排序的数组
*@param2 copy:作为中间量的数组
*@param3 start:需要合并的起始位置
*@param4 midden:需要合并的中间位置
*@param5 end:需要合并的末端位置
**/
void Sort::Sort_mergesort_array(int* a,int *copy,int start,int midden,int end)
{
int len=end-start;
//只有end>=start时,才需要分治
if(len>=1)
{
//将start-end的序列分成两段,然后合并
Sort_mergesort_array(a,copy,start,(midden+start)/2,midden);
Sort_mergesort_array(a,copy,midden+1,(midden+1+end)/2,end);
merge_array(a,copy,start,midden,end);
}
else
return;
}
注:
1.个人认为,midden不一定要纯中间,稍微偏一点也无伤大雅
(三)快速排序
算法时间复杂度:最坏O(N2),平均O(Nlog(N))
算法简介:
分治:选取中间枢纽,比其小的构成一个序列,比其大的也构成一个序列,利用递归可完成各自的排序
合并:直接按小序列-中间值-大序列的顺序构成排序完成的新数组
算法实现步骤:
1.选取中间枢纽,将其与数组的首个元素交换;
2.通过与中间枢纽比较大小,将数组元素分成两个序列,具体步骤:
(1)选取中间枢纽,将其换到数组的首元素位置;
(2)新建两个指针,一个从头开始扫(用i表示),一个从尾部开始扫(用j表示);
(3)j从尾部开始扫,直到扫到小于中间枢纽的元素时停下,i从头部开始扫,直到扫到大于中间枢纽的停下;
(4)如果i<j,证明还没有扫过头,此时交换i和j指向的元素;
(5)直到i>=j时,此时将数组首元素与j指向的元素交换即可得到小序列、中间枢纽、大序列的组合(此时j指向的是小于中间枢纽的元素,故和中间枢纽交换即可得到上述组合);
3.利用递归,对小序列和大序列分别再使用快速排序;
/**
*@name Sort_quicksort_array:快速排序(数组)
*@name a:需要排序的数组
*@start:起始位置
*@end:终止位置
**/
void Sort::Sort_quicksort_array(int*a,int start,int end)
{
if(start>end)
{
return;
}
int i=start;
int j=end;
Swap(&a[start],&a[(start+end)/2]);
int cmp=a[start];
//取基准元 并将序列分成左右两个序列
//左边小于基准元,右边大于基准元
while(i<j)
{
//找到右边开始的第一个小于cmp的元素 如果加上= 就会很慢。。。
while((a[j]>=cmp)&&(i<j))
{
j--;
}
//找到左边第一个大于cmp的元素
while((a[i]<=cmp)&&(i<j))
{
i++;
}
if(i<j)
{
//交换 小于cmp的元素从右边序列到左边序列 大于cmp的元素从左边序列到右边序列
Swap(&a[i],&a[j]);
}
}
//将基准元放到中间
Swap(&a[j],&a[start]);
//对左边序列进行快排
Sort_quicksort_array(a,start,j-1);
//对右边序列进行快排
Sort_quicksort_array(a,j+1,end);
}
注:
1.快排确实比归并快(在release模式下)
2.起始位置一定从start开始,否则j只能到start+2的位置,会存在排序错误
3.交换完了不需要自加和自减,针对两者中间仅有一个元素的情况下会出错
(四)堆排序
算法时间复杂度:O(Nlog(N))
算法简介:利用数组建立一个二叉堆(大根堆),然后利用最大元素永远出现在root的特性,不断作删除堆(下滤,放弃最大元素,使其沉底),重建堆的操作(对剩下的元素重建堆),最终堆中元素仅剩1个,数组的结果即排序结果。
算法实现步骤:
1.从最后一个节点开始,建立(max)堆,具体步骤:
(1)确定开始的节点位置,将其作为分析节点,找到其两个子节点中较大的那个;
(2)如果开始节点的值较小的话与子节点中较大的那个交换,此时分析节点变为交换到较小值的那个子节点(分析节点对应的元素是较小值);
(3)一直顺着树向下走,直到分析节点的子节点的值都小或者分析节点没有子节点为止;
2.删除root,堆中节点数-1,再重建堆,具体步骤:
(1)将数组的最后一个元素与首元素交换位置;
(2)以数组首元素作为分析节点,重建大小-1的堆(目的是将置换而来的元素沉下去);
(3)直到堆的大小为1时完成排序;
/**
*@name Build_Heap:构建大根堆(顺着一个节点,将小节点往下放)
*@param1 a:传入需要建立堆的数组
*@param2 i:输入需要操作的节点
*@param3 len:数组的长度,防止越界,越界意味着没有子节点
**/
void Build_Heap(int* a,int i,int len)
{
int Child;
int temp;
//只要存在儿子就继续向下比较
while((2*i)<=len)
{
Child=2*i;
//取左右儿子节点大的那个
if(((Child+1)<=len)&&(a[Child]<a[Child+1]))
Child++;
//如果儿子比父亲大的话就交换,同时索引节点变为儿子
//否则的话就证明顺着该节点的次序是对的
if(a[i]<a[Child])
{
Swap(&(a[Child]),&(a[i]));
i=Child;
}
else
break;
}
}
/**
*@name Sort_heapsort_array:堆排序
*@param1 a:传入需要堆排序的数组
*@param2 N:数组的大小
**/
void Sort::Sort_heapsort_array(int* a,int N)
{
//对每一个节点进行下滤(小的节点往下走)
//得到一个大根堆
for (int i=N;i>=0;i--)
{
Build_Heap(a,i,N);
}
//从最后一个位置开始,与根交换后再对前半部分重新建立堆
//效果即成功排序
for (int i=N;i>0;i--)
{
Swap(&(a[0]),&(a[i]));
Build_Heap(a,0,i-1);
}
}
注:
1.如果从0开始建立二叉堆的话,子节点对应的索引应为(2×i+1)和(2×i+2)
用链表做排序真是坑。。。
(五)基数排序
算法时间复杂度:O(k(N+r)),其中k最高位的位数,N是排序的个数,r是进制对应的数
算法简介:按低位优先级或者高位优先级进行排序
算法实现步骤:
1.根据进制r新建一个二维数组,一维代表十进制对应的0-9,另一位用于存放在比较过程中相应的数;
2.取第一个数的最低位,根据该位的值将该数放入二维数组对应的位值,找到存放数据的那一维中第一个零的位置,放入该数;
3.对数组中的所有数执行上述操作;
4.此时根据二维数组中的数据存储形式,从0-9取出数据重新放入原数组;
5.对二维数组清零;
6.然后再对所有数的其他位作相同操作(由低到高,代表最高位优先级最高);
/**
*@name Radix_sort:基数排序
*@param1 a:需要排序的数组
**/
void Radix_sort(int* a)
{
int a_Radix[10][20]={0};
int temp;
int count=0;
for (int i=0;i<3;i++)
{
//放入桶
for (int j=0;j<5;j++)
{
//取出第i位 从后向前
temp=(a[j]/(int)pow(10.0,i))%10;
for (int k=0;k<20;k++)
{
//每一位可以最多挂20个在该位相同的数
if(a_Radix[temp][k]==0)
{
//检测到该位没有挂数时就挂上,否则自加到下一个位置
a_Radix[temp][k]=a[j];
break;
}
}
}
//从桶中取出
count=0;
//遍历桶 对某一位从0-9检索排序
for (int j=0;j<10;j++)
{
//将所有在该位相同的数从桶中取出,放回数组a中
for (int k=0;k<20;k++)
{
if(a_Radix[j][k]==0)
break;
a[count]=a_Radix[j][k];
count++;
}
}
//将桶清零,方便下一次放入
for (int j=0;j<10;j++)
{
for (int k=0;k<20;k++)
a_Radix[j][k]=0;
}
}
}