【问题标题】:sort String by Character in C Programming在 C 编程中按字符排序字符串
【发布时间】:2016-01-22 12:33:00
【问题描述】:

我想问一下,如何按字符对某组字符串进行排序。 例如:“Baba”、“Cece”、“Caca”、“Zaza”、“Lala”


我想把它整理成这样 爸爸 卡卡 切切 拉拉 扎扎

如何对这些单词进行排序? 我们是否比较每个单词的 ASCII 码? 如果它是一个数字,我们可以交换它(使用插入排序等) 我们也可以使用相同的方法吗?

【问题讨论】:

  • 每个字符(char)都有一个介于 0-127(基本 ascii 值)之间的值,并且可以进行相应的排序。一般来说,像qsort 这样的简单排序路由可以毫不费力地使用。对于短字符串,您几乎可以使用您选择的任何排序例程,而不会降低效率,但对于较长的字符串(1000 个字符),快速排序、堆排序或与合并排序的组合可提供最佳性能。 (见Ascii Table Values
  • @dietbacon:我认为另一个问题是这个问题的更复杂的版本,但不是重复的。这个问题基本上是“帮助我以最基本的方式使用qsort”。

标签: c string sorting character


【解决方案1】:

您是在问如何对字符串集合进行排序?或者如何对一个字符串中的字符进行排序?

在单个 ASCII 字符串中,这非常容易。字符串只是一个字节数组,因此您可以使用任何与对整数数组进行排序相同的算法对其进行排序。 C 的char 类型是单字节整数类型,可以进行比较、加法、减法等操作。(可以有符号也可以无符号:依赖于实现)

使用 utf8,单个字符可以占用多个字节 (chars) 进行编码,因此对字节进行排序会破坏事情。

多年前,我为字谜词典生成器的字符串编写了这个简单的就地选择排序函数:

void sort_chars(char *string, int len) // selection sort because words are short
{
   char *end_p, *search, *max_p; // *end_p == last char before '\0'
   char max_val;

   for(end_p = string + len - 1; end_p > string ; end_p--){
      for(search = max_p = string, max_val = *max_p ; search <= end_p ; search++){
         if(*search > max_val){ max_p = search; max_val = *max_p; }
      }
      // max_p now points to largest element, so swap
      *max_p = *end_p;
      *end_p = max_val;
   }

}

给定单词的所有字谜都具有相同的字符,并且按字符排序是这些字符的规范表示。因此,您可以制作一个字谜字典,存储/usr/share/dict/words 中每个单词的按字符排序表示,以及原始单词。

您可以轻松修改此函数以从头到尾排序,并查找尾随 '\0' (NUL) 字节,而不需要明确的长度。

【讨论】:

  • 我在问如何对字符串集合进行排序
【解决方案2】:

对字符串中的字符进行排序(下面对字符串进行排序)

根据注释,您可以像在 C 中对任何其他 type 进行排序一样对字符串进行排序。您可以使用标准库提供的 qsort 函数,也可以使用您自己的函数(例如气泡排序、堆排序等)

使用qsort 时,您唯一的任务是编写一个compare 函数以传递给qsort 函数,该函数告诉qsort 如何对您的数据进行排序。 qsort 的基本声明是:

void qsort (void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

qsort 接受 4 个参数:(1) 指向要排序的列表/数组的指针,(2) 要排序的元素数量,(3) 每个元素的 类型大小 (例如sizeof int)和(4)最后一个指向您的比较函数的指针。注意:您的 compare 函数将 2 个通用指针(void 类型)作为其输入。

对于字符排序,您正确地认识到您将按 ascii 值进行排序。因此,您的比较函数需要做的就是返回两个字符中的哪一个:(a) 一个大于另一个,或者 (b) 它们相等。

compare 函数的示例如下:

int compare_char (const void *a, const void *b) {
    if (*(char *)a != *(char *)b)
        return *(char *)a - *(char *)b;

    return 0;
}

通常,对于新的 C 程序员来说,唯一困难的部分是弄清楚如何将参数的 void 类型转换为您需要比较的类型

compare 函数中,由于您传递了一个指向类型void 的指针,因此您需要将其转换为您的类型,然后才能取消引用它以获取指针地址处的值.在这里将void * 转换为char * 就是您所需要的。然后在进行比较之前,您需要使用 '*' 取消引用它。 (对于例如*(char *)a 的最终转换/取消引用)。将它们放在一个简短的示例中:

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

#define MAXS 32

/* simple compare function for qsort, returns 
   less than 0 if char 'b' is larger than char 'a',
   a positiver number if 'a' is larger than 'b', and
   zero if they are equal (note: you can return 
   'b' - 'a' to sort in reverse order.)
*/
int compare_char (const void *a, const void *b) {
    if (*(char *)a != *(char *)b)
        return *(char *)a - *(char *)b;

    return 0;
}

int main (void) {

    char word[MAXS]   = {0};
    char sorted[MAXS] = {0};

    printf ("\n Enter a string: ");         /* read word from stdin   */
    if (!scanf ("%31[^\n]%*c", word)) {
        printf ("  [ctrl+d] received\n\n");
        return 1;
    }

    /* copy word to sorted before sort */
    strncpy (sorted, word, sizeof word);

    /* sort the chars in sorted[] */
    qsort (sorted, strlen (word), sizeof *word, compare_char);

    /* print results */
    printf ("\n word   : %s\n sorted : %s\n\n", word, sorted);

    return 0;
}

编译

$ gcc -Wall -Wextra -Ofast -o bin/str_sort_char str_sort_char.c

使用/输出

$ ./bin/str_sort_char

 Enter a string: aghibw

 word   : aghibw
 sorted : abghiw

对字符串数组进行排序

上面所有关于按char 排序的解释,也适用于对字符串数组进行排序。这还可以很好地了解您使用qsort 的方式。在上面的第一个示例中,当我们使用qsort 进行处理时,我们必须回答这个问题:“我在对type 进行排序吗?” 上面我们将每个char 排序为null -终止的字符数组(或string)。

字符串是如何引用的? 通过一个指向字符串开始字符的指针。所以在这里,我们不是在 字符数组 中对每个 char 进行排序,而是在字符串数组中排序 strings (或者正确地一个数组char * 类型的指针)就qsort 而言,唯一的区别是compare 函数(和输入......)。我们需要对字符串进行排序,所以compare 函数必须提供一种比较字符串的方法。在 C 中,标准的字符串比较函数是 strcmp(或 strncmp 以限制比较的字符数)。

例如,使用strcmp 对字符串数组进行排序,您将使用类似于:

/* string compare function */
int compare_strings (const void *a, const void *b) {
    return strcmp(*(char **)a, *(char **)b);
}

如果您查看compare_strings 中的比较,您只是返回了strcmp 函数的结果。基本上,只需更改比较函数(和输入),您就可以使用qsort 对字符串数组进行排序,如下所示:

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

/* string compare function */
int compare_strings (const void *a, const void *b) {
    return strcmp(*(char **)a, *(char **)b);
}

int main (void) {

    char *strings[] = {"Baba", "Cece" , "Caca" , "Zaza" , "Lala"};
    size_t i;

    /* sort strings */
    qsort (strings, sizeof strings/sizeof *strings, 
           sizeof *strings, compare_strings);

    /* output sorted arrray of strings */
    for (i = 0; i < sizeof strings/sizeof *strings; i++)
        printf (" strings[%2zu] : %s\n", i, strings[i]);

    return 0;
}

输出

$ ./bin/qsort_strings_static
 strings[ 0] : Baba
 strings[ 1] : Caca
 strings[ 2] : Cece
 strings[ 3] : Lala
 strings[ 4] : Zaza

【讨论】:

  • 在您的compare_char 函数中,您可以只返回*a - *b。如果它们相等,则为零,因此特殊情况只会使编译器更加努力地优化 if
  • 彼得,我不确定我明白你在说什么?你不能只返回*a - *b 而不取消引用void。我明白你在说return *(char *)a - *(char *)b; 可能只是return *a - *b; 吗?
  • 不,你仍然需要演员,但你可以放弃if。 (您可以编写函数以获取 const char* 参数,然后强制转换函数指针,但这不是我之前所说的。)int compare_char (const void *a, const void *b) { return *(char *)a - *(char *)b; } 在所有情况下都应返回与您的版本相同的值。
  • 好的,现在我明白你在说什么,是的,我同意。目的是强调您构建了比较函数来处理 3 种情况(&gt;&lt;=)简单的 return *(char *)a - *(char *)b; 就是这样做的(这是您通常使用的),但它忽略您正在处理所有 3 个案例的事实。好点子。
  • 如果字符串大小不同怎么办?还能用吗?
【解决方案3】:

您可以从 2 个指针和一个循环开始,1 个指针指向 string1,另一个指向 string2,取消引用两者并检查谁的字符更大,或者它们是否相等。并相应地进行交换.这是实现这种方法的代码。这是一个完整的功能,您可以使用。

#include <stdio.h>

int s_bubblesort(int argc,char **argv);


int main(void)
{
    char *str[] = { "zzz","zhz","ydc","acb","zzb","zua","abc","zuaaa","xzz" };

    s_bubblesort(9,str);

    for( int n = 0 ; n < 9 ; n++ )
    {
        printf("%s\n",str[n]);
    }
    printf("\n\n");

    char *words[] = {"Baba","Caca" , "Cece","Lala","Babaaaaaa", "L"};

    s_bubblesort(6,words);

    for( int n = 0 ; n < 6 ; n++ )
    {
        printf("%s\n",words[n]);
    }
}

int s_bubblesort(int argc,char **argv)
{
    int i , j = 0;

    char *p_1 , *p_2 , *tmp;

    while( j < argc )
    {
        for( i = 0 ; i < argc - j - 1 ; i++ )
        {
            p_1 = argv[i] , p_2 = argv[i+1];

            while( *p_1 && *p_2 )
            {
                if( *p_1 > *p_2 || ( ! *(p_2 + 1) && ( *p_1 == *p_2 ) ) )
                {
                    tmp = argv[i];

                    argv[i] = argv[i+1];

                    argv[i+1] = tmp;

                    break;
                }
                else if( *p_1 < *p_2 )
                {
                    break;
                }
                p_1++;
                p_2++;
            }
        }
        j++;
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 2015-01-16
    • 1970-01-01
    相关资源
    最近更新 更多