希尔排序、归并排序、快速排序、计数排序、基数排序、睡眠排序、猴子排序

        本文将介绍七大排序算法的三种,其中另外四种见博客冒泡排序、选择排序、插入排序、堆排序,其中本文用到的两个数的交换函数也在该博客中有介绍。

1.希尔排序

(1)实现思想:


(2)时间复杂度:取决于步长序列,若使用希尔序列:N/2,N/4,N/8.......1,时间复杂度为O(n^2);若选择一个最优序列,可达到O(n^1.3)

(3)空间复杂度:O(1)

(4)稳定性:不稳定排序

(5)实现代码:

//5.希尔排序:分组式的插入排序
//定义一个初始步长(下标间隔)gap,按步长将待排序数列分组
//将分好的组,每组插入排序,然后gap--
//重复以上步骤,直至gap=0
//N/2,N/4,N/8...希尔序列,时间复杂度为O(N)
void ShellSort(int arr[], size_t size)
{
    if(size <= 1)
        return;
    int64_t gap = size/2;//使用希尔序列
    //1.第一重循环,生成步长序列
    for(; gap>0; gap /= 2)
    {
        //2.第二重循环,进入插入排序
        //此循环的执行顺序,相当于先处理第一组的第一个
        //再处理第二组的第一个....
        //再第二组的第一个....
        int64_t bound = gap;//此处相当于插入排序中的bound=1
        for(; bound<size;++bound)
        {
            int bound_value = arr[bound];//待插入元素
            //3.第三重循环,线性表的搬运(一边找一边搬运)
            int64_t cur = bound;
            for(; cur>=gap;cur -= gap)//cur-=gap是为了找到同组元素的上一个元素
            {
                if(arr[cur-gap] > bound_value)//进行搬运
                    arr[cur] = arr[cur-gap];
                else//找到了合适的插入位置
                    break;                                                                                                                            
            }
            arr[cur] = bound_value;
        }
    }
}

2. 归并排序

(1)实现思想:


(2)时间复杂度:O(n^logn)

(3)空间复杂度:O(n)

(4)稳定性:稳定排序

(5)实现代码:

1)递归实现:

//6.归并排序
//(1)递归版本
void _MergeArr(int arr[], 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)//有一个子区间比较结束了,循环结束,可将另一个子区间剩下的元素一次性全放进去,因为每个子区间本身有序
    {//将数据按从小到大存放到tmp空间中
        if(arr[cur1] < arr[cur2])
            tmp[tmp_index++] = arr[cur1++];
        else
            tmp[tmp_index++] = arr[cur2++];
    }
    while(cur1 < mid)
        tmp[tmp_index++] = arr[cur1++];
    while(cur2 < end)
        tmp[tmp_index++] = arr[cur2++];
    //把tmp中的内容拷贝到数组,进行归并处理时的区间是arr[beg,end)
    memcpy(arr+beg, tmp+beg, sizeof(int)*(end-beg));
    return;
}


void _MergeSort(int arr[], int64_t beg, int64_t end, int* tmp)
{//[beg,end)为当前要处理的子区间
    //size_t类型作减法很危险,所以这里用int64_t
    if(end - beg <= 1)//最多一个元素或非法区间
        return;                                                                                                                                       
    int64_t mid = beg + (end-beg)/2;
    //此时有两个区间[beg,mid)、[mid,end)
    _MergeSort(arr, beg, mid, tmp);
    _MergeSort(arr, mid, end, tmp);
    _MergeArr(arr, beg, mid, end, tmp);
}

void MergeSort(int arr[], size_t size)
{
    if(size <= 1)
        return;
    //创建临时空间,用于辅助归并元素
    int* tmp = (int*)malloc(sizeof(int)*size);
    _MergeSort(arr, 0, size, tmp);
    free(tmp);
}

2)非递归实现:

//(2)非递归版本
void MergeSortByLoop(int arr[], size_t size)
{
    if(size <= 1)
        return;
    int* tmp = (int*)malloc(sizeof(int)*size);
    //定义一个初始步长gap=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超过数组最大范围
                mid = size;
            size_t end = i+2*gap;
            if(end > size)//防止end超过数组最大范围
                end = size;
            _MergeArr(arr, beg, mid, end, tmp);
        }
    }
    free(tmp);                                                                                                                                        
}

3. 快速排序

(1)实现思想:


(2)时间复杂度:平均情况O(n^logn),最坏时为O(n^2)

(3)空间复杂度:O(1)

(5)实现代码:

1)递归实现:

//7.快速排序
//(1)递归版本

int64_t Partion(int arr[], int64_t beg, int64_t end)//1.交换法
{
    if(end-beg <= 1)
        return beg;
    int64_t left = beg;
    int64_t right = end-1;//前闭后开区间
    int key = arr[right];//最后一个元素作为基准值
    while(left < right)
    {
        //从左到右找一个大于key的元素
        while(left < right && arr[left] <= key)
            ++left;
        //从右到左找一个小于Key的元素
        while(left < right && arr[right] >= key)
            --right;
        if(left < right)
            Swap(&arr[left], &arr[right]);
    }
    Swap(&arr[left], &arr[end-1]);
    return left;
}

int64_t Partion1(int arr[], int64_t beg, int64_t end)//1.挖坑法
{
    if(end-beg <= 1)
        return beg;
    int64_t left = beg;
    int64_t right = end-1;//前闭后开区间                                                                                                              
    int key = arr[right];//最后一个元素作为基准值
    while(left < right)
    {
        while(left < right && arr[left] <= key)
            ++left;
        //此时left指向一个大于基准值Key的元素
        //可以把该值填到right所指的坑,赋值完成后,left所指又变成一个坑
        if(left < right)
            arr[right--] = arr[left];
        while(left < right && arr[right] >= key)
            --right;
        if(left < right)
            arr[left++] = arr[right];
    }
    //left与right重合,整个区间整理完毕,只剩下left=right位置为坑,将基准值填入即可
    arr[left] = key;
    return left;
}

void _QuickSort(int arr[], int64_t beg,int64_t end)
{
    if(end-beg <= 1)//当前区间内最多只有一个元素
        return;
    //[beg,mid)左半部分区间,[mid+1, end)右半部分区间,左半部分元素一定小于等于右半部分区间元素
    //int64_t mid = Partion(arr, beg, end);
    int64_t mid = Partion1(arr, beg, end);
    _QuickSort(arr, beg, mid);
    _QuickSort(arr, mid+1, end);
    return;
}

void QuickSort(int arr[], size_t size)
{
    _QuickSort(arr, 0, size);                                                                                                                         
    return;
}

2)非递归实现:

//(2)非递归版本实现
void QuickSortByLoop(int arr[], 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 = SeqStackGetTop(&stack, &end);
        if(ret == 0)//栈空,说明快排结束
            break;
        SeqStackPop(&stack);
        SeqStackGetTop(&stack, &beg);
        //[beg,end)相当于即将要进行快排整理的区间
        if(end-beg <= 1)
            continue;
        //int64_t mid = Partion(arr, beg, end);
        int64_t mid = Partion1(arr, beg, end);
        
        //[beg, mid),[mid+1, end)
        SeqStackPush(&stack, beg);
        SeqStackPush(&stack, mid);
        SeqStackPush(&stack, mid+1);                                                                                                                  
        SeqStackPush(&stack, end);
    }
}

快排的改进方法:

(1)可以调整选取基准值的方法:头、尾、中间,三个元素取值为中间大的数为基准值;

(2)当递归到一定深度时,就不再递归,而是将排序算法改为堆排序

(3)当递归到一定程度时,子区间的元素较少时,把排序算法改为插入排序


下面介绍几种不常用的排序算法,这里只做简单介绍,并无实现。

4. 计数排序

(1)实现思想


(2)只适用于正整数

(3)时间复杂度:O(n)

(4)空间复杂度:O(n)

5. 基数排序

(1)实现思想:


(2)适用于解决数据范围大

6. 睡眠排序

实现思想:

        借助线程,每读一个待排序元素,都创建一个线程,并让它sleep元素值得秒数,按照醒来时间,我们即可排序。

7. 猴子排序

(1)实现思想:

        一直排(没有方法的排),直至整个有序,排序结束。

(2)时间复杂度:无穷大

猜你喜欢

转载自blog.csdn.net/lycorisradiata__/article/details/80540221