1.希尔排序
希尔排序又称缩小增量排序,是对直接插入排序的优化;
希尔排序三重循环:第一重,生成步长序列,这里采用希尔序列,如:N/2、N/4、N/8、…..、1;第二重循环,处理每组的数据,注意竖着处理,即先处理第一行的第一个元素,在处理第二行第一个元素……;第三重循环,进行搬运,找到合适的位置,将值插入加进去(结合插入排序的思想);
希尔排序的时间复杂度:取决于步长序列,对于希尔序列为O(N^) , 如果选择一个最优序列,可能达到O(N^1.3)
空间复杂度:O(1)
稳定性:不稳定排序
代码实现:
void ShellSort(int array[],int64_t size){
if(size <= 1){
return;
}
int64_t gap = size/2;
for(;gap > 0;gap /= 2){
int64_t bound = gap;
for(;bound < size;bound++){
int bound_value = array[bound];
int64_t cur = bound;
for(;cur >= gap;cur -= gap){
if(array[cur - gap] > bound_value){
array[cur] = array[cur - gap];
}else{
//找到了插入的位置
break;
}
}
array[cur] = bound_value;
}
}
return;
}
2.归并排序
递归版本:
如上图为完整的归并过程;
归并排序的时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:稳定排序算法
代码实现:
void _MergeArray(int array[],int64_t beg, int64_t mid,
int64_t end,int* tmp){
int64_t cur1 = beg;
int64_t cur2 = mid;
int64_t tmp_index = beg;
while(cur1 < mid && cur2 < end){
if(array[cur1] < array[cur2]){
tmp[tmp_index++] = array[cur1++];
}else{
tmp[tmp_index++] = array[cur2++];
}
}
while(cur1 < mid){
tmp[tmp_index++] = array[cur1++];
}
while(cur2 < end){
tmp[tmp_index++] = array[cur2++];
}
//把tmp中的内容拷贝到array中
//进行归并的时候,处理的区间是array[beg,end)
//对应的会把这部分元素填充到 tmp[beg,end)区间
//此时最后一步拷贝回array时,要保证结果放回正确的位置
memcpy(array + beg,tmp + beg,sizeof(int) * (end - beg));
return;
}
void _MergeSort(int array[],int64_t beg,int64_t end,int* tmp){
if(end - beg <= 1){
return;
}
int64_t mid = beg + (end - beg)/2;
_MergeSort(array,beg,mid,tmp);
_MergeSort(array,mid,end,tmp);
_MergeArray(array,beg,mid,end,tmp);
return;
}
void MergeSort(int array[],size_t size){
//此时创建一个临时空间用来存放合并后的元素
int* tmp = (int*)malloc(sizeof(int) * size);
_MergeSort(array,0,size,tmp);//合并区间[0,size)
free(tmp);
}
非递归版本:
我们可以直接取到beg,mid,end的位置,在调用归并函数即可完成;
代码如下:
void MergerSortByLoop(int array[],size_t size){
if(size <= 1){
return;
}
int* tmp = (int*)malloc(sizeof(int) * size);
//定义一个步长,初始值为1,相当于每次合并步长为gap的有序区间
size_t gap = 1;
for(;gap < size; gap *= 2){
//在当前的gap下,使用i辅助我们完成所有长度为gap的区间的合并
size_t i = 0;
for(;i < size;i += 2 * gap){
//[beg,mid),[mid,end)
size_t beg = i;
size_t mid = i + gap;
if(mid > size){
mid = size;
}
size_t end = i + 2 * gap;
if(end > size){
end = size;
}
_MergeArray(array,beg,mid,end,tmp);
}
}
free(tmp);
return;
}
3.快速排序
递归版本:
有两种快速排序的方法:
a)交换法
思想:定义一个基准,这里将最后一个元素定为基准,然后从前往后找第一个大于基准值的元素,在从后往前找第一个小于基准值的元素,将两者进行交换,直到前后两者相遇,最后一步,将基准值赋值给相遇点的位置元素;
b)挖坑法
思想:与交换法相似,将最后一个元素定义为基准值,并且最后一个为坑,从前往后找,第一个大于基准值的元素,然后将其填入到坑中,一旦赋值完成,此时的这个位置元素就变为坑,在从后往前找第一个小于基准值的元素,将其填入到坑中,此时,这个小于基准值的元素就变为坑,如此循环即可;
快速排序的时间复杂度:平均情况O(N*logN) 最坏情况O(n^2)
空间复杂度:O(1)
稳定性:不稳定排序
代码实现:
//1.交换法
int64_t Partion(int array[],int64_t beg,int64_t end){
if(end - beg <= 1){
return beg;
}
int64_t left = beg;
int64_t right = end - 1;
//取数组的最后一个元素作为基准值
int key = array[right];
while(left < right){
while(left < right && array[left] <= key){
++left;
}
while(left < right && array[right] >= key){
--right;
}
if(left < right){
Swap(&array[left],&array[right]);
}
}
Swap(&array[left],&array[end - 1]);
return left;
}
//2.挖抗法
int64_t Partion2(int array[],int64_t beg,int64_t end){
if(end - beg <= 1){
return beg;
}
int64_t left = beg;
int64_t right = end - 1;
//key为基准,right的位置为第一个坑
int key = array[right];
while(left < right){
while(left < right && array[left] <= key){
++left;
}
//说明此时left所指向的值,大于key,进行填坑
//循环退出意味着,left就指向了一个大于基准值的元素
//就可以把这个值填到刚才right指向的坑里
//一旦赋值操作完成,left自身也就成了一个坑
if(left < right){
array[right--] = array[left];
}
while(left < right && array[right] >= key){
--right;
}
//循环退出,意味着right就指向了一个小于基准值的元素
//就可以把这个值填到刚才的left指向的坑里
//一旦赋值操作完成,right自身又成了一个坑
if(left < right){
array[left++] = array[right];
}
}
//循环结束之后还有一个坑,用key来填
array[left] = key;
return left;
}
void _QuickSort(int array[],int64_t beg,int64_t end){
if(end - beg <= 1){
return;
}
int64_t mid = Partion(array,beg,end);
_QuickSort(array,beg,mid);
_QuickSort(array,mid + 1,end);
return;
}
void QuickSort(int array[],size_t size){
if(size <= 1){
return;
}
_QuickSort(array,0,size);
return;
}
非递归版本:
说明:将即将要排序的区间入栈即可;
void QuickSortByLoop(int array[],size_t size){
if(size <= 1){
return;
}
SeqStack stack;
SeqStackInit(&stack);
int64_t beg = 0;
int64_t end = size;
SeqStackPush(&stack,beg);
SeqStackPush(&stack,end);
while(1){
int ret = SeqStackTop(&stack,&end);
if(ret == 0){
//栈为空说明快速排序就结束了
break;
}
SeqStackPop(&stack);
SeqStackTop(&stack,&beg);
//[beg,end)相当于是即将要进行的快速排序,进行整理的区间
if(end - beg <= 1){
continue;
}
int64_t mid = Partion(array,beg,end);
//[beg,mid)
//[mid + 1,end)
SeqStackPush(&stack,beg);
SeqStackPush(&stack,mid);
SeqStackPush(&stack,mid + 1);
SeqStackPush(&stack,end);
}
}