排序算法的C语言实现(上 比较类排序:插入排序(3)

在以下的实现方法中,data包含size个无序元素,并存放在单块连续的存储空间中,快速排序不需要额外的存储空间,所以所有分割过程都在data中完成。当qksort返回时,data就是一个有序的数据集了。

快速排序的关键部分是如何分割数据。这部分工作由函数partition完成。函数分割data中处于i和k之间的元素(i小于k)。

首先,用前面提到的中位数法选取和个分割值。一旦选定分割值,就将k往data的左边移动,直到找到一个小于或等于分割值的元素。这个元素属于左边分区。接下来,将i往右边移动,直到找到一个大于或等于分割值的元素。这个元素属于右边分区。一旦找到的两个元素处于错误的位置,就交换它们的位置。重复这个过程,直到i和k重合。一旦i和k重合,那么所有处于左边的元素将小于等于它,所有处于右边的元素将大于等于它。

qksort中处理递归的过程在初次调用qksort时,i设置为0,k设置为size-1。首先调用partition将data中处于i和k之间的元素分区。当partition返回时,把j赋于分割点的元素。接下来,递归调用qksort来处理左边的分区(从i到j)。左边的分区继续递归,直到传入qksort的一个分区只包含单个元素。此时i不会比k小,所以递归调用终止。同样,分区的右边也在进行递归处理,处理的区间是从j+1至k。总的来说,以这种递归的方式继续运行,直到首次达到qksort终止的条件,此时,数据就完全排好了。

排序算法的C语言实现(上 比较类排序:插入排序

排序算法的C语言实现(上 比较类排序:插入排序

围绕其平均情况下的性能分析是快速排序的重点,因为一致认为平均情况是它复杂度的度量。虽然在最坏情况下,其运行时间O(n2)并不比插入排序好,但快速排序的性能一般能比较有保障地接近其平均性能O(nlgn),其中n为要排序的元素个数。

快速排序在平均情况下的时间复杂度取决于均匀分布的情况,即数据是否分割为平衡或不平衡的分区。如果使用中位数法,那么此平衡分区将有保障。在这种情况下,当不断分割数组,在图3中用树(高度为(lgn)+1)的方式直观地表示出来。由于顶部为lgn层的树,因此必须遍历所有n个元素,以形成新的分区,这样快速排序的运行时间为O(nlgn)。快速排序不需要额外的存储空间,因此它只使用无序数据本身的存储空间即可。

/*qksort.c*/
#include <stdlib.h>
#include <string.h>

#include "sort.h"

/*compare_int  比较函数*/
static int compare_int(const void *int1, const void *int2)
{
    /*对比两个整数的大小(用于中位数分区)*/
    if(*(const int *)int1 > *(const int *)int2)
        return 1;
    else if(*(const int *)int1 < *(const int *)int2)
        return -1;
    else
        return 0;
}

/*partition 分割函数*/
static int partition(void *data, int esize, int i, int k, int (*compare)(const void *key1, const void *key2))
{
    char *a=data;
    void *pval,
        *temp;
    int  r[3];
   
    /*为分割值和交换值变量分配空间*/
    if((pval = malloc(esize)) == NULL)
        return -1;
   
    if((temp = malloc(esize)) == NULL)
    {
        /*如果为交换变量分配空间失败,则将分割变量的空间一起释放掉*/
        free(pval);
        return -1;
    }
   
    /*用中位数法找到分割值*/
    r[0] = (rand()%(k-i+1))+i;
    r[1] = (rand()%(k-i+1))+i;
    r[2] = (rand()%(k-i+1))+i;
    /*调用插入排序函数对三个随机数排序*/
    issort(r,3,sizeof(int),compare_int);
    /*把排好序的三个数的中间值复制给分割值*/
    memcpy(pval,&a[r[1]*esize,esize);
   
    /*围绕分割值把数据分割成两个分区*/
    /*准备变量范围,使i和k分割超出数组边界*/
    i--;
    k++;
   
    while(1)
    {
        /*k向左移动,直到找到一个小于或等于分割值的元素,这个元素处于错误的位置*/
        do
        {
            k--;
        }
        while(compare(&a[k*esize],pval)>0);
       
        /*i向右移动,直到找到一个大于或等于分割值的元素,这个元素处于错误的位置*/
        do
        {
            i++;
        }
        while(compare(&a[i*esize],pval)<0);
       
        /*直到i和k重合,跳出分区,否则交换处于错误位置的元素*/
        if(i >= k)
        {
            break;
        }
        else
        {
            memcpy(temp, &a[i*esize], esize);
            memcpy(&a[i*esize], &a[k*esize], esize);
            memcpy(&a[k*esize], temp, esize);
        }
    }
   
    /*释放动态分配的空间*/
    free(pval);
    free(temp);
   
    /*返回两个分区中间的分割值*/
    return k;
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/4c44cb3108d7558c7fdff59fb643a7ba.html