三向字符串快速排序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//三向字符串快速排序

//基于快速排序的思想,依据键值进行比较,划分为小于当前
//键值、等于当前键值、大于当前键值的3个子组,之后采用递
//归方法分别对3个子组再次进行排序。由于等于当前键值的子
//组所有键都已经相同,所以无需再比较当前键值,而选取下
//一列索引字符做为键值。
//
//如下示例所示,最初仅有一个排序组,有6个组成员,此时选
//择每组成员的索引0进行排序,当前选择了第1个成员的索引0
//字符h做为比较键进行比较,在第一次排序后,将小于h键的
//成员分为一子组、等于h键的成员分为一子组、大于h键的成
//员分为一子组,之后采用递归方法,分别对每个子组进行排序
//处理,第一个小于h的子组仍然使用索引0为键进行排序,第二
//个等于h的子组,由于已知当前索引0的键都已经相同,所以取
//下一列索引为键进行排序,第三个大于h的子组仍然使用索引
//0为键进行排序。
//
// 原始数据:           第一次排序后:
// http.hello.com       bos.test.com
// www.test.com         ftp.hello.com
// bos.test.com         ^
// www.hello.com
// ftp.hello.com        http.test.com
// http.test.com        http.hello.com
// ^                     ^
//
//                      www.test.com
//                      www.hello.com
//                      ^


//查找提指定索引处char的值,如果不存在则返回-1
char charAt(const char *pStr, const int index)
{
    const char *pOldStr = pStr;

    if ( pStr == NULL )
    {
        return -1; 
    }

    while ( *pStr != '\0' )
    {
        if ( (pStr-pOldStr) == index )
        {
            return *pStr; 
        }

        pStr++;
    }

    return -1;
}

//字符串数组成员交换
void arrayExch(char* strArray[], int x, int y)
{
    char *pTmp = strArray[x];
    strArray[x] = strArray[y];
    strArray[y] = pTmp;
}

//核心算法
void sort(char* strArray[], int lo, int hi, int d)
{
    //  最终处理完成后的图示:
    //  *******@@@@@@@@@#########
    //         ^       ^
    //         |       |
    //         lt     gt/i
    //
    //如上图示,lt表示小于比较值的下一个存储索引,在算法
    //不断处理过程中,如果找到小于比较值的项,则将该项与
    //lt索引所在项进行交换,同时递增lt,从而使得lt索引左
    //边的部分一定都小于比较值,而gt表示大于比较值的下一
    //存储索引,在算法不断处理过程中,如果找到大于比较值
    //的项,则将该项与gt索引所在项进行交换,同时递减gt,
    //从而使得gt索引右边的部分一定都大于比较值,最终结果
    //如示例所示,lt-1左边都小于比较值,gt+1右边都大于比
    //较值,lt与gt之间都等于比较值。
    int lt = lo;
    int gt = hi;
    //同普通快速排序法相同,采用简化方法选择字符串数组第
    //开始索引项数据做为比较值。
    //当前i为一个游标,i值从左向右移动,依次选择i索引下
    //的数据与比较值进行比较,由于当前选择数组开始索引做
    //为比较值,所以i的初始取值则是在开始索引上加1
    int i = lo + 1;
    int cmpKey = charAt(strArray[lo], d);

    //当前为递归函数,这里做为是否继续递归的判断条件。
    if ( lo >= hi )
    {
        return; 
    }

    //i游标从左向右不断移动,当i值与gt相遇时,即表示本轮
    //排序比较已经完成。
    while ( i <= gt )
    {
        int iKey = charAt(strArray[i], d);

        //如果发现当前i索引下的字符串KEY小于比较值,则将当
        //前值与lt索引值进行交换,即把小于比较值的项都移到
        //lt索引的左边,此时将lt向右移动用于存储下一个小于
        //比较值的项。i值也继续向右移动,因为i值就是从左向
        //右方向移动过来的,所以此时交换过来的lt索引的项信
        //息早就知道了(不是小于比较值,就是等于比较值)
        if ( iKey < cmpKey ) 
        {
            arrayExch(strArray, i, lt); 
            lt++;
            i++;
        }
        //如果发现当前i索引下的字符串KEY大于比较值,则将当
        //前值与gt索引值进行交换,即把大于比较值的项都移到
        //gt索引值的右边,此时将gt向左移动用于存储下一个大
        //于比较值的项,但i值并不移动,因为i值是从左向右移
        //动的,此时把gt索引位置的数据移到i的位置,当前并
        //不清楚此时这个i位置的新值与比较值的关系,所以i都
        //不能变,需要在下一轮循环中检查该位置的值。
        else if ( iKey > cmpKey )
        {
            arrayExch(strArray, i, gt); 
            gt--;
        }
        //如果发现当前i索引下的字符串KEY等于比较值,则不需
        //要做交换处理,将i向右移动,继续检查下一个索引项
        //的值。
        else
        {
            i++; 
        }
    }

    //经过上述排序后,当前字符串组分为了3个子组,lo~lt-1索引
    //位置为小于比较值的一组,lt~gt索引位置为等于比较值的一
    //组,gt+1~hi索引位置为大于比较值的一组,此时对这3个子组
    //进行递归排序处理。

    sort(strArray, lo, lt-1, d);


    //当做为比较值的字符串长度不满足处理时,cmpKey为-1,即该
    //字符串查找已经到了末尾,此时必须加此限制,否则会出现死
    //循环的情况,如下示例所示,如果比较的两个字符串是相同的,
    //会出现i值不断后向移动,当i移动到字符c之后时,两个字符串
    //的之后cmpKey永远恒等于-1。
    //
    //str1: abc
    //str2: abc
    if ( cmpKey >= 0 )
    {
        //当前等于比较值的子组由于键已知都相同,所以选择下一
        //列键(d + 1)进行处理,其它2个子组仍然使用原来的键
        //进行处理。
        sort(strArray, lt, gt, d + 1);
    }

    sort(strArray, gt+1, hi, d);
}

void dumpArray(char *strArray[], int count)
{
    int i = 0;

    printf("****************************\r\n");

    for ( i = 0; i < count; i++ )
    {
        printf("[%02d] %s\r\n", i, strArray[i]); 
    }
}

int main()
{
    char *strArray[] = {
        "edu.princeton.cs",
        "com.apple",
        "edu.princeton.cs",
        "com.cnn",
        "com.goole",
        "edu.uva.cs",
        "edu.princeton.cs",
        "edu.princeton.cs.www",
        "edu.uva.cs",
        "edu.uva.cs",
        "edu.uva.cs",
        "com.adobe",
        "edu.princeton.ee"
    };

    int arrayCount = sizeof(strArray) / sizeof(strArray[0]);

    dumpArray(strArray, arrayCount);

    sort(strArray, 0, arrayCount-1, 0);

    dumpArray(strArray, arrayCount);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值