排序算法的实现

  • 冒泡排序

重复的走访过要排序的数列。一次比较两个元素,如果他们的顺序错误,就把他们交换过来。走访数列的工作是重复的进行,直到没有再需要交换的元素,即该序列已经排序完成。

                            

void Swap(int* a,int* b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void BubbleSort(int arr[],size_t size){
    if(size <= 1)
        return;
    size_t bound = 0;//[0,bound)表示有序元素,[bound,size)表示待排序元素
    for(;bound < size;bound++){
        size_t cur = size-1;
        for(;cur > bound;cur--){
            if(arr[cur] < arr[cur-1])
                Swap(&arr[cur],&arr[cur-1]);
        }
    }
    return;
}

  • 选择排序

每一趟从待排序的数据元素中选出最大或最小的一个元素,顺序放在已经排好序的数列的最后,直到全部待排序的数据元素排完。

                            

void Swap(int* a,int* b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void SelectSort(int arr[],size_t size){
    if(size <= 1)
        return;
    size_t bound = 0;
    for(;bound < size;bound++){
        size_t cur = bound+1;
        for(;cur < size;cur++){
            if(arr[bound] > arr[cur])
                Swap(&arr[bound],&arr[cur]);
        }
    }
    return;
}

  • 插入排序

有一个已经有序的数据序列,在这个已经排好序的数据序列中插入一个数,要求插入此数后,该数据序列仍然有序。

                                

void InsertSort(int arr[],size_t size){
    if(size <= 1)
        return;
    size_t bound = 1;
    //[0,bound)表示有序元素,[bound,size)表示待排序元素
    //插入排序将前面的有序区间当成一个线性表,然后将bound位置的元素插入到线性表中的合适位置
    for(;bound < size;bound++){
        int bound_value = arr[bound];
        size_t cur = bound;
        for(;cur > 0;cur--){
            if(arr[cur-1] > bound_value)//进行搬运
                arr[cur] = arr[cur-1];
            else//说明已找到合适的位置
                break;
    }
        arr[cur] = bound_value;
    }
}

  • 堆排序

利用堆这种数据结构所设计的一种排序算法,是选择排序的一种。

堆是具有以下性质的完全二叉树:每个节点的值均大于等于其左右孩子结点的值,称为大堆;每个节点的值均小于等于其左右孩子结点的值,称为小堆。

在数组的升序排序中,需要使用的是大堆;在数组的降序排序中,需要使用的是小堆。

堆排序的基本思想是:将待排序序列构成一个大堆(假设升序排序),此时整个序列中的最大值即为堆顶元素的值。将堆顶元素的值与末尾元素的值进行交换,此时末尾元素的值即为最大值。然后将剩余的n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值。如此反复执行,即可得到一个有序序列了。

                                

void AdjustDown(int arr[],size_t size,size_t index){
    size_t parent = index;
    size_t child = 2*parent+1;
    while(child < size){//升序排序,建立大堆
        if(child+1 < size && arr[child+1] > arr[child])
            child += 1;//child指向了左右子树中较大的
        if(arr[parent] < arr[child])
            Swap(&arr[parent],&arr[child]);
        parent = child;
        child = 2*parent+1;
    }
    return;
}


void HeapCreate(int arr[],size_t size){
    if(size <= 1)
        return;
    size_t i = (size-1-1)/2;
    for(;i > 0;i--)
        AdjustDown(arr,size,i);
    AdjustDown(arr,size,0);
    return;
} 


void HeapPop(int arr[],size_t heap_size){
    if(heap_size <= 1)
        return;
    Swap(&arr[0],&arr[heap_size-1]);
    AdjustDown(arr,heap_size-1,0);
    return;
}

void HeapSort(int arr[],size_t size){
    if(size <= 1)
        return;
    HeapCreate(arr,size);//创建堆的函数
    size_t i = 0;
    for(;i < size;i++)
        HeapPop(arr,size-i);
    //size-i表示数组中哪部分区间符合堆的规则
    //即第i次删除前,[0,size-i)都是堆
    return;
}

  • 希尔排序

希尔排序是插入排序的一种,又称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。本质上是一种分组插入的方法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

        (1)插入排序在对几乎已经排好序的数据操作时,效率高;

        (2)插入排序在对元素个数较少的数据操作时,效率高。

                            

void ShellSort(int arr[],int64_t size){
    int64_t gap = size/2;//使用希尔序列
    for(;gap > 0;gap /= 2){//生成步长序列
        int64_t bound = gap;
        for(;bound < size;bound++){//进行插入排序
            int bound_value = arr[bound];
            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;
        }
    }
    return;
}

  • 归并排序

归并排序是建立在归并操作上的一种有效的排序算法。它将已有序的子序列合并,得到完全有序的序列。即先使每个子序列有序,再使子序列段间有序,最后使得整个序列有序。


归并排序递归版本:

void _MergeArray(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){
        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++];
    memcpy(arr+beg,tmp+beg,sizeof(int)*(end-beg));
    return;
}


void _MergeSort(int arr[],int64_t beg,int64_t end,int *tmp){
    //[beg,end)为当前要处理的数组
    if(end - beg <= 1)
        return;
    int64_t mid = beg + (end - beg) / 2;
    //将数组分成两个区间[0,mid),[mid,end)
    _MergeSort(arr,beg,mid,tmp);
    _MergeSort(arr,mid,end,tmp);
    _MergeArray(arr,beg,mid,end,tmp);
    return;
}


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);
    return;
}

归并排序非递归版本:

void MergeSortByLoop(int arr[],size_t size){
    if(size <= 1)
        return;
    int *tmp = (int*)malloc(sizeof(int)*size);
    size_t gap = 1;//gap表示步长,相当于每次合并两个长度为gap的有序区间
    for(;gap < size;gap *= 2){
        size_t i = 0;
        for(;i < size;i += 2*gap){//辅助完成所有长度为gap的区间的合并
        size_t beg = i;
        size_t mid = i + gap;
        size_t end = i + 2*gap;
        if(mid > size)
            mid = size;
        if(end > size)
            end = size;
        _MergeArray(arr,beg,mid,end,tmp);
        }
    }
    return;
}

  • 快速排序

快速排序是对冒泡排序的一种改进。它的基本思想是:经过一趟排序,将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序的过程可以递归进行,以此达到整个数据变成有序序列的目的。

                                        

快速排序的递归版本:

int64_t Partion(int arr[],int64_t beg,int64_t end){//快速排序方法一之交换法
    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;//从左往右找到一个大于key的元素
        while(left < right && arr[right] >= key)
            --right;//从右往左找到一个小于key的元素
        if(left < right){
            Swap(&arr[left],&arr[right]);
        }
    }
    Swap(&arr[left],&arr[end-1]);
    return left;
}


int64_t Partion2(int arr[],int64_t beg,int64_t end){//快速排序方法二之挖坑法
    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指向了一个大于基准值的元素
        //就可以将这个元素填到刚才right所指向的坑里,
        //一旦赋值完成,left自身就变成了一个坑
        if(left < right)
            arr[right--] = arr[left];
        while(left < right && arr[right] >= key)
            --right;//循坏退出就意味着right指向了一个小于基准值的元素
        //就可以将这个元素填到刚才left所指向的坑里,
        //一旦赋值完成,right自身就变成了一个坑
        if(left < right)
            arr[left++] = arr[right];
    }
    //一旦left与right重合,说明整个区间整理完毕,只剩一个坑,将基准值填到left指向的坑里即可
    arr[left] = key;
    return left;
}


void _QuickSort(int arr[],int64_t beg,int64_t end){
    if(end - beg <= 1)
        return;
    int64_t mid = Partion2(arr,beg,end);//[beg,mid)左半区间,[mid,end)右半区间
    //左半区间中的元素一定小于等于右半区间中的元素
    _QuickSort(arr,beg,mid);
    _QuickSort(arr,mid,end);
    return;
}


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

快速排序的非递归版本(基于栈实现):

void QuickSortByLoop(int arr[],size_t size){
    if(size <= 1)
        return;
    SeqStack stack;
    SeqStackInit(&stack);
    int beg = 0;
    int end = size;
    SeqStackPush(&stack,beg);
    SeqStackPush(&stack,end);
    while(1){
        int ret = GetTop(&stack,&end);
        if(ret == 0)//栈为空,说明快速排序结束
            break;
        SeqStackPop(&stack);
        GetTop(&stack,&beg);//[beg,end)相当于是即将进行快速排序,进行整理的区间
        if(end - beg <= 1)
            continue;
        int64_t mid = Partion(arr,beg,end);
        SeqStackPush(&stack,beg);
        SeqStackPush(&stack,mid);
        SeqStackPush(&stack,mid+1);
        SeqStackPush(&stack,end);
    }
    return;
}

  • 对比




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值