数据结构 --- 排序算法(2)

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值