【问题标题】:How to sort an array of string alphabetically (case sensitive, nonstandard collation)如何按字母顺序对字符串数组进行排序(区分大小写,非标准排序规则)
【发布时间】:2012-09-20 17:39:11
【问题描述】:

我需要一个 c 语言代码来对一些字符串进行排序,它应该区分大小写,对于大小写相同的字母,小写必须在前。例如以下字符串的排序结果:

eggs
bacon
cheese
Milk
spinach
potatoes
milk
spaghetti

应该是:

bacon
cheese
eggs
milk
Milk
potatoes
spaghetti
spinach

我已经写了一个代码,但我得到的结果是:

Milk
bacon
cheese
eggs
milk
potatoes
spaghetti
spinach

我不知道如何改进这一点,我已经搜索了很多。谁能帮我解决这个问题?

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

int main(){
    char c;
    char name[20][10], temp[10];
    int count_name = 0;
    int name_index = 0;
    int i, j;

    while ((c = getchar()) != EOF){
        if (c == 10){
            name[count_name][name_index] = '\0';
            count_name++;
            name_index = 0;
        } else {
            name[count_name][name_index] = c;
            name_index++;
        }
    }

    for(i=0; i < count_name-1 ; i++){
        for(j=i+1; j< count_name; j++)
        {
            if(strcmp(name[i],name[j]) > 0)
            {
                strcpy(temp,name[i]);
                strcpy(name[i],name[j]);
                strcpy(name[j],temp);
            }
        }
    }

    for (i = 0; i < count_name; i++){
        printf("%s\n", name[i]);
    }
}

【问题讨论】:

  • "我搜索了很多。" ——我很难相信这一点。这是关于高效排序技术的大量可通过网络访问的废话。
  • @JimBalter 我确实搜索了很多。我不知道你有什么问题。这是我的一个任务,它是关于用 c 语言以特定方式排序的。我无法给出任何解决方案。请把我当成白痴给我一个解决方案链接?
  • '我不知道你有什么问题。' -- 哦,不错。
  • 这里有很多答案。他们中只有一个人认识到问题的转折点,即排序规则不标准。 OP 想要小写优先于大写。 OP 的老师很聪明。这是一项伟大的任务。
  • 分配?我每天都会遇到这个现实世界的问题。按照我想要的方式对我的购物清单进行排序是一个真正的 PITA..

标签: c sorting case-sensitive alphabetical


【解决方案1】:

把相似的词放在一起...

对于单词列表,将“相同”单词组合在一起通常更有用(即使它们的大小写不同)。例如:

Keeping things together:          Simple "M after m":
------------------------          -------------------
mars                              mars
mars bar                          mars bar
Mars bar                          milk
milk                              milk-duds
Milk                              milky-way
milk-duds                         Mars bar
milky-way                         Milk
Milky-way                         Milky-way

如果您希望单词像第一列那样排列,我提供了三种方法:

  • strcasecmp()strcmp() 结合使用。
  • 使用isalpha()tolower()isupper() 跟踪字符类型的单通道实现。
  • 利用整理表的单通道实现。

最后我讨论了两种选择:

  • 使用整理表建立任意排序。
  • 将语言环境设置为使用基于语言环境的整理。

使用可用的库函数

如果可能,请避免重新发明轮子。在这种情况下,我们可以使用 POSIX 函数 strcasecmp() 通过不区分大小写的比较来查看它们是否相等,并在它们相等时返回 strcmp()

int alphaBetize (const char *a, const char *b) {
    int r = strcasecmp(a, b);
    if (r) return r;
    /* if equal ignoring case, use opposite of strcmp() result to get
     * lower before upper */
    return -strcmp(a, b); /* aka: return strcmp(b, a); */
}

(在某些系统上,不区分大小写的比较函数称为stricmp()_stricmp()。如果您不可用,下面提供了一个实现。)

#ifdef I_DONT_HAVE_STRCASECMP
int strcasecmp (const char *a, const char *b) {
    while (*a && *b) {
        if (tolower(*a) != tolower(*b)) {
            break;
        }
        ++a;
        ++b;
    }
    return tolower(*a) - tolower(*b);
}
#endif

避免两次遍历字符串

有时,现有功能的性能不够好,您必须做一些其他事情才能使事情变得更快。以下函数以大致相同的方式在一次通过中进行比较,并且不使用strcasecmp()strcmp()。但是,它将所有非字母字符视为小于字母。

int alphaBetize (const char *a, const char *b) {
    int weight = 0;
    do {
        if (*a != *b) {
            if (!(isalpha(*a) && isalpha(*b))) {
                if (isalpha(*a) || isalpha(*b)) {
                    return isalpha(*a) - isalpha(*b);
                }
                return *a - *b;
            }
            if (tolower(*a) != tolower(*b)) {
                return tolower(*a) - tolower(*b);
            }
            /* treat as equal, but mark the weight if not set */
            if (weight == 0) {
                weight = isupper(*a) - isupper(*b);
            }
        }
        ++a;
        ++b;
    } while (*a && *b);
    /* if the words compared equal, use the weight as tie breaker */
    if (*a == *b) {
        return weight;
    }
    return !*b - !*a;
}

使用此比较进行排序将使milkMilk 彼此相邻,即使列表中包含milk-duds

使用整理表

这是一种从“配置”动态创建整理表的方法。它用于说明一种对比技术来改变字符串的比较方式。

您可以将字母表中的字母与一种简单的表格进行比较,该表格描述了您希望字母(或除 NUL 字节之外的任何字符)具有的相对顺序:

const char * alphaBetical =
    "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";

通过这个排序,我们可以创建一个查找表来查看两个字母应该如何相互比较。如果表尚未完成,则以下函数初始化表,否则执行表查找。

int alphaBeta_lookup (int c) {
    static int initialized;
    static char table[CHAR_MAX+1];
    if (!initialized) {
        /* leave all non-alphaBeticals in their relative order, but below
           alphaBeticals */
        int i, j;
        for (i = j = 1; i < CHAR_MAX+1; ++i) {
            if (strchr(alphaBetical, i)) continue;
            table[i] = j++;
        }
        /* now run through the alphaBeticals */
        for (i = 0; alphaBetical[i]; ++i) {
            table[(int)alphaBetical[i]] = j++;
        }
        initialized = 1;
    }
    /* return the computed ordinal of the provided character */
    if (c < 0 || c > CHAR_MAX) return c;
    return table[c];
}

有了这个查找表,我们现在可以简化alphaBetize()比较函数的循环体:

int alphaBetize (const char *a, const char *b) {
    int ax = alphaBeta_lookup(*a);
    int bx = alphaBeta_lookup(*b);
    int weight = 0;
    do {
        char al = tolower(*a);
        char bl = tolower(*b);
        if (ax != bx) {
            if (al != bl) {
                return alphaBeta_lookup(al) - alphaBeta_lookup(bl);
            }
            if (weight == 0) {
                weight = ax - bx;
            }
        }
        ax = alphaBeta_lookup(*++a);
        bx = alphaBeta_lookup(*++b);
    } while (ax && bx);
    /* if the words compared equal, use the weight as tie breaker */
    return (ax != bx) ? !bx - !ax : weight;
}

我们能让事情变得更简单吗?

使用整理表,您可以使用简化的比较功能创建许多不同的排序,例如:

int simple_collating (const char *a, const char *b) {
    while (alphaBeta_lookup(*a) == alphaBeta_lookup(*b)) {
        if (*a == '\0') break;
        ++a, ++b;
    }
    return alphaBeta_lookup(*a) - alphaBeta_lookup(*b);
}

使用相同的函数并通过修改alphaBetical 字符串,您可以实现几乎任何您想要的排序(字母顺序、反向字母顺序、元音在辅音之前等)。但是,相似词放在一起的安排需要大写词和小写词穿插,这只能通过忽略大小写的比较来实现。

请注意,使用上面的simple_collating() 函数和我提供的alphaBetical 字符串,Bacon 将位于milk 之前,但Mars 将位于milk 之后和Milk 之前。

如果您想根据您的语言环境进行排序。

如果您想使用已经为您的语言环境定义的排序顺序,您可以设置语言环境并调用排序比较函数:

/*
 * To change the collating locale, use (for example):
       setlocale(LC_COLLATE, "en.US");
 */
int iso_collating (const char *a, const char *b) {
    return strcoll(a, b);
}

现在,通过更改区域设置,排序顺序将基于标准化的整理顺序。

【讨论】:

  • strcasecmp 是非标准库&lt;strings.h&gt; 下的非标准函数,例如我个人不能使用。自己也给出了答案,对别人的答案进行抹黑批评感觉有点不对,但是来吧……
  • 不,我根本没有声称这一点。我是(或者更确切地说)只是说它使用了一个函数strcasecmp,该函数在任何 C 标准库中都找不到。对于标准库,我指的是任何 C 编译器所具有的标准库,按照标准。但是,很抱歉,我觉得我对此有点草率,没有意识到在第二个代码块中根本没有使用它。
  • 如果有的话,那对我来说就不那么标准了。我不在符合 POSIX 标准的机器上工作,我得到的所有信息都来自互联网。一个看似可靠的消息来源告诉我,&lt;strings.h&gt; 是必需的,而不仅仅是一个特定的消息来源告诉我。然而,您已经向我展示了它也位于其他一些库中,或者 &lt;string.h&gt; 在此类机器上可能在其自身中包含 &lt;strings.h&gt;。无论如何,strcasecmp 不是标准函数,ideone.com 并不能证明是这样。
  • @ThoAppelsin:我已经为你提供了一个实现。问候!
  • 优先考虑现有库函数而不是自定义比较的解决方案不仅可以防止“重新发明轮子”,而且可以轻松地与本地化例程进行交换,例如 Windows 的CompareStringEx
【解决方案2】:

您可以编写自定义比较函数进行排序。

首先看一下默认的strcmp排序顺序:

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

const char *tgt[]={
    "bacon", "Bacon", "mIlk", "Milk", "spinach", "MILK", "milk", "eggs"
};
int tgt_size=8;

static int cmp(const void *p1, const void *p2){
    return strcmp(* (char * const *) p1, * (char * const *) p2);
}

int main(int argc, char *argv[]) {
    printf("Before sort:\n\t");
    for(int n=0; n<tgt_size; n++)
        printf("%s ", tgt[n]);

    qsort(tgt, tgt_size, sizeof(char *), cmp);

    printf("\nAfter sort:\n\t");
    for(int n=0; n<tgt_size; n++)
        printf("%s ", tgt[n]);

    return 0;
}

strcmp 按 ASCII 字符码排序;即,它先排序 A-Z 然后 a-z 所以所有大写的 A-Z 都在任何带有小写字母的单词之前:

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    Bacon MILK Milk bacon eggs mIlk milk spinach 

我们可以编写自己的比较函数,用于cmp,用于qsort,忽略大小写。看起来像这样:

int mycmp(const char *a, const char *b) {
    const char *cp1 = a, *cp2 = b;

    for (; toupper(*cp1) == toupper(*cp2); cp1++, cp2++)
        if (*cp1 == '\0')
            return 0;
    return ((toupper(*cp1) < toupper(*cp2)) ? -1 : +1);
} 

请务必将cmp 更改为:

static int cmp(const void *p1, const void *p2){
    return mycmp(* (char * const *) p1, * (char * const *) p2);
}

现在打印忽略大小写的版本:

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    bacon Bacon eggs Milk MILK milk mIlk spinach 

这与使用 POSIX 函数 strcasecmp 得到的输出相同。

函数mycmp首先按正常顺序按字典顺序比较[a|A]-[z|Z]。这意味着您将获得类似字母的单词,但您获得bacon, Bacon 的可能性与获得Bacon, bacon 的可能性相同。这是因为 qsort 不是 stable sort 并且 'Bacon' 比较等于 'bacon'。

现在我们想要的是,如果比较为 0,而忽略大小写(即,像“MILK”和“牛奶”这样的同一个词)现在比较包括大小写并颠倒顺序:

int mycmp(const char *a, const char *b) {
    const char *cp1 = a, *cp2 = b;
    int sccmp=1;

    for (; toupper(*cp1) == toupper(*cp2); cp1++, cp2++)
        if (*cp1 == '\0')
            sccmp = 0;
    if (sccmp) return ((toupper(*cp1) < toupper(*cp2)) ? -1 : +1);

    for (; *a == *b; a++, b++)
        if (*a == '\0')
            return 0;
    return ((*a < *b) ? +1 : -1);
}

最终版本打印:

Before sort:
    bacon Bacon mIlk Milk spinach MILK milk eggs 
After sort:
    bacon Bacon eggs milk mIlk Milk MILK spinach 

不幸的是,这种方法对于 UNICODE 来说变得笨拙。对于复杂的排序,请考虑使用带有stable sort 的映射或多步排序。

对于复杂和位置感知的字母排序规则,请考虑Unicode collations。例如,在不同的位置,字母的字母顺序不同:

Swedish                      z < ö
                             y == w
German                       ö < z
Danish                       Z < Å
Lithuanian                   i < y < k
Tr German                    ä == æ
Tr Spanish                   c < ch < d   
German Dictionary Sort:      of < öf
German Phonebook Sort:       öf < of

默认 Unicode 排序规则元素表 (DUCET) 中捕获了这些区别的默认值,该表为 UNICODE 排序规则和字符串比较提供了默认映射。您可以修改默认值以捕获字典排序和电话簿排序之间的区别、不同的位置或不同的大小写处理。在 Unicode 通用区域设置数据存储库 (CLDR) 中主动跟踪各个位置变化。

多级排序的推荐是分层的:

Level   Description           Examples
L1      Base characters       role < roles < rule
L2      Accents               role < rôle < roles
L3      Case/Variants         role < Role < rôle
L4      Punctuation           role < “role” < Role
Ln      Identical             role < ro□le < “role”

广泛使用的 Unicode 排序规则实现在 ICU Library 中。几个示例的默认 DUCET 排序规则是:

b < B < bad < Bad < bäd
c < C < cote < coté < côte < côté 

您可以浏览 ICU 库并使用 ICU Explorer 更改位置和目标

如果你想实现你自己的 DUCET 版本,你可以按照Python script 中使用的通用方法进行操作。这不是压倒性的,但也不是微不足道的。

【讨论】:

  • 有趣。该链接提到了“转换为小写”,但没有提到之后两个字符串相等时会发生什么,我希望它恢复到“通常的”strcmp 行为:Milk 之前的milk。这是否记录在其他地方?
  • ...确实,我的系统(OSX,gcc)上的strcasecmp 返回似乎是一个 random 相同字符串的顺序。在两个重复的单词中,只有首字母不同,一个大写出现在小写之前,另一个出现在小写之后。此外,role-your-own 链接直接完全忽略了大小写。
  • 检查你的输出 ;) Milk 出现在 milk 之前,违反了“对于大小写相同的字母,小写必须在前”(尽管奇怪的是, milkmIlk 之前,baconBacon 之前)。
  • @Jongware:我修好了。感谢您指出了这一点。这实际上是一个非常有趣的问题......
  • 这个“修复”基本上是在窃取我的贡献,而不是对您原始解决方案的修复。
【解决方案3】:

OP代码的关键是使用函数strcmp()比较两个字符串。
所以,我将首先用另一个标准函数替换这个标准函数,如下所示:

  // We assume that the collating sequence satisfies the following rules:
  // 'A' < 'B' < 'C' < ...
  // 'a' < 'b' < 'c' < ...
  // We don't make any other assumptions.

  #include <ctype.h>      
  int my_strcmp(const char * s1, const char * s2)
  {
      const char *p1 = s1, *p2 = s2;
      while(*p1 == *p2 && *p1 != '\0' && *p2 != '\0')
          p1++, p2++;  /* keep searching... */

      if (*p1 == *p2)
         return 0;
      if (*p1 == '\0')
         return -1;
      if (*p2 == '\0')
         return +1;

      char c1 = tolower(*p1),      c2 = tolower(*p2);
      int  u1 = isupper(*p1) != 0, u2 = isupper(*p2) != 0;
      if (c1 != c2)
        return c1 - c2;  // <<--- Alphabetical order assumption is used here 
      if (c1 == c2)
        return u1 - u2;
  }

最后几行可以这样压缩:

     return (c1 != c2)? c1 - c2: u1 - u2;

现在,通过将strcmp() 替换为my_strcmp(),您将获得所需的结果。

在排序算法中,最好分别考虑它的三个主要方面:

  • 比较函数。
  • 我们将使用的抽象排序算法。
  • 当必须交换两个项目时,该数据在数组中的“移动”方式。

这些方面可以独立优化。
因此,例如,一旦您很好地确定了比较函数,下一个优化步骤可能是将 double for 排序算法替换为更有效的排序算法,例如 quicksort
尤其是标准库&lt;stdlib.h&gt;的函数qsort()为您提供了这样的算法,您无需关心编程。
最后,用于存储数组信息的策略可能会对性能产生影响。
存储“char 指针数组”之类的字符串比“char 数组数组”更有效,因为交换指针比交换两个完整的 char 数组更快。

Arrays of pointers

补充说明:前三个if()实际上是多余的,因为以下句子的逻辑暗示了*p1*p2为0的情况下的预期结果。但是,通过保留那些if(),代码变得更具可读性。

【讨论】:

    【解决方案4】:

    在这里,如果我没记错的话,你想要的东西我会描述如下:

    不区分大小写的排序,在 tie 下,将使用 tiebreaker 条件“小写优先”。

    所以是这样的:

    1. earlier_letter_in_the_alphabet &lt; later_letter_in_the_alphabet忽略大小写

    2. lowercase &lt; uppercase

    3. shorter_word &lt; wider_word
      • 这个没提到,我是从字典顺序借来的,字典里的那个
      • 只需将'\0' 作为比较中的最低值即可使用

    仅当 1 没有区分任何内容时才执行第 2 步。步骤 3 已经用 1 进行检查。所有这些都将逐个字母地完成,这意味着您应该在相应字符之间获得平局时立即切换到 2,而不仅仅是当整个字符串平局时。


    假设这是正确的,我们现在需要做的就是编写一个函数,为我们对任何给定的两个字符串进行比较。

    #include <ctype.h>  // for tolower and islower
    
    int my_character_compare(const char a, const char b)
    {
        int my_result;
    
        my_result = tolower(a) - tolower(b);
        // unless it is zero, my_result is definitely the result here
        // Note: if any one of them was 0, result will also properly favour that one
    
    
        if (my_result == 0 && a != b)
        // if (could not be distinguished with #1, but are different)
        {
            // means that they are case-insensitively same
            // but different...
            // means that one of them are lowercase, the other one is upper
            if (islower(a))
                return -1;  // favour a
            else
                return 1;   // favour b
        }
    
    
        // regardless if zero or not, my_result is definitely just the result
        return my_result;
    }
    
    int my_string_compare(const char * a, const char * b)
    {
        int my_result;
    
        my_result = my_character_compare(*a, *b);
        // unless it is zero, my_result is definitely the result here
    
        while (my_result == 0 && *a != 0)
        // current characters deemed to be same
        // if they are not both just 0 we will have to check the next ones
        {
            my_result = my_character_compare(*++a, *++b);
        }
    
        // whatever the my_result has been:
        //   whether it became != zero on the way and broke out of the loop
        //   or it is still zero, but we have also reached the end of the road/strings
        return my_result;
    }
    

    根据约定/规则,比较函数应该返回负值以支持第一个参数在前面,负值表示支持第二个参数,如果无法区分它们,则返回零。只是您使用strcmp 的方式可能已经知道的附加信息。

    就是这样!在这里用my_string_compare 替换代码中的strcmp,同时将我们所做的这些定义放在上面应该会提供正确的结果。实际上,它为有问题的示例输入提供了预期结果。

    当然可以缩短定义,我把它们写得很长,以便更容易理解发生了什么。例如,我可以将其归结为以下内容:

    #include <ctype.h>
    
    int my_string_compare(const char * a, const char * b)
    {
        int my_result;
    
        while (*a || *b)
        {
            if ((my_result = tolower(*a) - tolower(*b)))
                return my_result;
            if (*a != *b)
                return (islower(*a)) ? -1 : 1;
            a++;
            b++;
        }
    
        return 0;
    }
    

    与另一个基本相同,你可以使用任何你喜欢的;甚至更好,写一个。

    【讨论】:

    • @dawg 按milk / mIlk / Milk 的顺序对它们进行排序,遵循所讨论的排序规则,特别是粗体部分。
    • @dawg 您可以在此处检查它是否有效:ideone.com/ZtpC2H 我很想听听它实际上 不起作用的示例。并且 不工作 表示,做了一些与所描述的内容相矛盾的事情;不是 Unicode 或任何其他人的意义。
    【解决方案5】:

    我迟到了这个讨论,并没有特别期待参加并获得惊人的奖品,但没有看到使用我首先寻找的成语的解决方案,以为我会插话。

    阅读问题规范时,我的第一个想法是某种形式的自定义整理顺序,我基本上是在@jxh 的使用整理表 概念中找到的。我不认为不区分大小写是核心概念,只是奇怪的排序。

    因此,我提供以下代码纯粹作为替代实现。它特定于 glibc - qsort_r(3) 被使用 - 但感觉像是一种轻量级的方法,并且在运行时支持许多整理序列。但它经过了轻微的测试,我很可能会遗漏各种弱点。其中:我没有特别关注 Unicode 或一般的宽字符世界,并且为避免负数组下标而强制转换为 unsigned char 让人感到怀疑。

    #include <stdio.h>
    #include <limits.h>
    
    /*
     * Initialize an index array associated with the collating
     * sequence in co. The affected array can subsequently be
     * passed in as the final client data pointer into qsort_r
     * to be used by collating_compare below.
     */
    int
    collating_init(const char *co, int *cv, size_t n)
    {
        const unsigned char *uco = (const unsigned char *) co;
        const unsigned char *s;
        size_t i;
    
        if (n <= UCHAR_MAX) {
            return -1;
        }
        for (i = 0; i < n; i++) {
            /* default for chars not named in the sequence */
            cv[i] = UCHAR_MAX;
        }
        for (s = uco; *s; s++) {
            /*
             * the "collating value" for a character's own
             * character code is its ordinal (starting from
             * zero) in the collating sequence. I.e., we
             * compare the values of cv['a'] and cv['A'] -
             * rather than 'a' and 'A' - to determine order.
             */
            cv[*s] = (s - uco);
        }
    
        return 0;
    }
    
    static int
    _collating_compare(const char *str1, const char *str2, int *ip)
    {
        const unsigned char *s1 = (const unsigned char *) str1;
        const unsigned char *s2 = (const unsigned char *) str2;
    
        while (*s1 != '\0' && *s2 != '\0') {
            int cv1 = ip[*s1++];
            int cv2 = ip[*s2++];
    
            if (cv1 < cv2) return -1;
            if (cv1 > cv2) return 1;
        }
    
        if (*s1 == '\0' && *s2 == '\0') {
            return 0;
        } else {
            return *s1 == '\0' ? -1 : 1;
        }
    }
    
    int
    collating_compare(const void *v1, const void *v2, void *p)
    {
        return _collating_compare(*(const char **) v1,
                                  *(const char **) v2,
                                  (int *) p);
    }
    

    前面的代码接近于可以放在单独的模块或库中的代码,但缺少自己的头文件(或头文件中的条目)。我自己的测试只是将上面和下面的代码连接到一个名为 custom_collat​​e_sort.c 的文件中,并使用

    gcc -DMAIN_TEST -Wall -o custom_collate_sort custom_collate_sort.c
    

    ...编译它。

    #if defined(MAIN_TEST)
    
    /* qsort_r is a GNU-ey thing... */
    
    #define __USE_GNU
    #include <stdlib.h>
    #include <string.h>
    
    #define NELEM(x) (sizeof x / sizeof 0[x])
    
    static int
    cmp(const void *v1, const void *v2)
    {
        return strcmp(*(const char **) v1, *(const char **) v2);
    }
    
    static int
    casecmp(const void *v1, const void *v2)
    {
        return strcasecmp(*(const char **) v1, *(const char **) v2);
    }
    
    int
    main(int ac, char *av[])
    {
        size_t i;
    
        int cval[256], ret;
        int cval_rev[256], rret;
    
        char *tosort[] = {
            "cheeSE", "eggs", "Milk", "potatoes", "cheese", "spaghetti",
            "eggs", "milk", "spinach", "bacon", "egg", "apple", "PEAR",
            "pea", "berry"
        };
    
        ret = collating_init("aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVxXyYzZ",
                             cval, NELEM(cval));
    
        rret = collating_init("ZzYyXxVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa",
                              cval_rev, NELEM(cval_rev));
    
        if (ret == -1 || rret == -1) {
            fputs("collating value array must accomodate an index of UCHAR_MAX\n", stderr);
            return 1;
        }
    
        puts("Unsorted:");
        for (i = 0; i < NELEM(tosort); i++) {
            printf("  %s\n", tosort[i]);
        }
    
        qsort((void *) tosort, NELEM(tosort), sizeof tosort[0], cmp);
    
        puts("Sorted w/ strcmp:");
        for (i = 0; i < NELEM(tosort); i++) {
            printf("  %s\n", tosort[i]);
        }
    
        qsort((void *) tosort, NELEM(tosort), sizeof tosort[0], casecmp);
    
        puts("Sorted w/ strcasecmp:");
        for (i = 0; i < NELEM(tosort); i++) {
            printf("  %s\n", tosort[i]);
        }
    
        qsort_r((void *) tosort, NELEM(tosort), sizeof tosort[0],
                collating_compare, (void *) cval);
    
        puts("Sorted w/ collating sequence:");
        for (i = 0; i < NELEM(tosort); i++) {
            printf("  %s\n", tosort[i]);
        }
    
        qsort_r((void *) tosort, NELEM(tosort), sizeof tosort[0],
                collating_compare, (void *) cval_rev);
    
        puts("Sorted w/ reversed collating sequence:");
        for (i = 0; i < NELEM(tosort); i++) {
            printf("  %s\n", tosort[i]);
        }
    
        return 0;
    }
    
    #endif /* MAIN_TEST */
    

    【讨论】:

      【解决方案6】:

      程序所需的标准头文件:

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

      主程序从这里开始:

      //Global Variables Required
      //=========
      const char *tgt[]={"bacon", "Bacon", "mIlk", 
              "Milk", "spinach", "MILK", "milk", "eggs"};    //Array for sorting
      int tgt_size=8;                                        //Total Number of Records
      int     SortLookupTable[128];                          //Custom sorting table
      typedef int      cmp_t (const void *, const void *);
      
      
      
      
      int main(int argc, char *argv[]) {
          printf("Before sort:\n\n");
          int n=0;
          for(n=0; n<tgt_size; n++)
              printf("%s\n", tgt[n]);
      
          CreateSortTable();
      
          myQsort(tgt, tgt_size, sizeof(char *), &compare);
          printf("\n\n====\n\n");
          for(n=0; n<tgt_size; n++)
              printf("%s\n", tgt[n]);
      
          return 0;
      }
      

      根据需要自定义排序表:

      void CreateSortTable(void){
          int     i;
          for (i = 0; i < 128; i++){
              SortLookupTable[i] = 0;
          }
          char *s;
          s=(char *)malloc(64);
          memset(s, 0, 64);
          strcpy(s, "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ");
      
          i=1;
          for (; *s; s++){
              SortLookupTable[(int) ((unsigned char) *s)]=i;
              i++;
          }
      }
      

      快速排序算法,也可以使用提供的标准库:

      //Some important Definations required by Quick Sort
      =======
      #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
          es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
      
      #define swap(a, b)                          \
          if (swaptype == 0) {                    \
              long t = *(long *)(a);              \
              *(long *)(a) = *(long *)(b);        \
              *(long *)(b) = t;                   \
          } else                                  \
              swapfunc(a, b, es, swaptype)
      
      #define vecswap(a, b, n)    if ((n) > 0) swapfunc(a, b, n, swaptype)
      
      
      #define swapcode(TYPE, parmi, parmj, n) {       \
          long i = (n) / sizeof (TYPE);               \
          register TYPE *pi = (TYPE *) (parmi);       \
          register TYPE *pj = (TYPE *) (parmj);       \
          do {                                        \
              register TYPE   t = *pi;                \
              *pi++ = *pj;                            \
              *pj++ = t;                              \
              } while (--i > 0);                      \
      }
      
      #define min(a, b)   (a) < (b) ? a : b
      
      
      //Other important function
      void swapfunc(char *a, char *b, int n, int swaptype){
          if(swaptype <= 1)
              swapcode(long, a, b, n)
          else
              swapcode(char, a, b, n)
      }
      
      char * med3(char *a, char *b, char *c, cmp_t *cmp){
          if ( cmp(a, b) < 0){
              if (cmp(b, c) < 0){
                  return b;
              }else{
                  if ( cmp(a, c) < 0){
      
                      return c;
                  }else{
                      return a;
                  }
              }
          }else{
              if (cmp(b, c) < 0){
                  return b;
              }else{
                  if ( cmp(a, c) < 0){
                      return a;
                  }else{
                      return c;
                  }
              }
          }
      }
      
      //Custom Quick Sort
      
      void myQsort(void *a, unsigned int n, unsigned int es, cmp_t *cmp){
          char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
          int d, r, swaptype, swap_cnt;
      
      loop:   SWAPINIT(a, es);
          swap_cnt = 0;
          if (n < 7) {
              for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
                  for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es){
                      swap(pl, pl - es);
                  }
              return;
          }
          pm = (char *)a + (n / 2) * es;
          if (n > 7) {
              pl = a;
              pn = (char *)a + (n - 1) * es;
              if (n > 40) {
                  d = (n / 8) * es;
                  pl = med3(pl, pl + d, pl + 2 * d, cmp);
                  pm = med3(pm - d, pm, pm + d, cmp);
                  pn = med3(pn - 2 * d, pn - d, pn, cmp);
              }
              pm = med3(pl, pm, pn, cmp);
          }
          swap(a, pm);
          pa = pb = (char *)a + es;
      
          pc = pd = (char *)a + (n - 1) * es;
          for (;;) {
              while (pb <= pc && (r = cmp(pb, a)) <= 0) {
                  if (r == 0) {
                      swap_cnt = 1;
                      swap(pa, pb);
                      pa += es;
                  }
                  pb += es;
              }
              while (pb <= pc && (r = cmp(pc, a)) >= 0) {
                  if (r == 0) {
                      swap_cnt = 1;
                      swap(pc, pd);
                      pd -= es;
                  }
                  pc -= es;
              }
              if (pb > pc)
                  break;
              swap(pb, pc);
              swap_cnt = 1;
              pb += es;
              pc -= es;
          }
          if (swap_cnt == 0) {  /* Switch to insertion sort */
              for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
                  for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0;
                       pl -= es)
                      swap(pl, pl - es);
              return;
          }
      
          pn = (char *)a + n * es;
          r = min(pa - (char *)a, pb - pa);
          vecswap(a, pb - r, r);
          r = min(pd - pc, pn - pd - es);
          vecswap(pb, pn - r, r);
          if ((r = pb - pa) > es)
              myQsort(a, r / es, es, cmp);
          if ((r = pd - pc) > es) {
              /* Iterate rather than recurse to save stack space */
              a = pn - r;
              n = r / es;
              goto loop;
          }
      }
      

      两个最重要的功能是:

      unsigned char Change(char a){
          return (unsigned char ) SortLookupTable[(int)a];
      }
      
      
      int compare (const void *a, const void *b){
          char *s1= *(char **)a;
          char *s2= *(char **)b;
          int ret, len, i;
          ret=0;
      
          if (strlen((void*)s1) > strlen((void*)s2)){
              len=strlen((void*)s1);
          }else{
              len=strlen((void*)s2) ;
          }
      
          for(i=0; i<len; i++){
              if ( s1[i] != s2[i]){
                  if ( Change(s1[i]) < Change(s2[i]) ){
                      ret=0;
                      break;
                  }else{
                      ret=1;
                      break;
                  }
              }
          }
      
          return ret;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-25
        • 1970-01-01
        • 1970-01-01
        • 2011-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多