【问题标题】:Grouping array of Strings C分组字符串数组 C
【发布时间】:2016-09-08 03:51:47
【问题描述】:

我已经制作了一个字符串数组,我正在尝试将一个字符串数组分组。

到目前为止,我的代码如下所示:

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

int
main(int argc, char *argv[]) {
    char *results[] = {"Canada", "Cycling", "Canada", "Swimming", "India", "Swimming", "New Mexico",
                       "Cycling", "New Mexico", "Cycling", "New Mecico", "Swimming"};



    int nelements, i, country_count;

    nelements = sizeof(results) / sizeof(results[0]);

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

    return 0;
}

打印出这个:

Canada
Cycling
Canada
Swimming
India
Swimming
New Mexico
Cycling
New Mexico
Cycling
New Mexico
Swimming

但我正在尝试将运动与各个国家/地区的相应计数一起分组,我希望看起来像这样:

Canada
    Cycling  1
    Swimming 1

India
    Swimming 1

New Mexico
    Cycling  2
    Swimming 1

我正在考虑使用数组中的每个 i+2 元素对国家/地区进行分类,并使用 strcmp 删除重复的国家/地区字符串,但我不知道如何使用每个运动的计数来执行此操作国家。

我只是不知道该怎么做。任何形式的帮助将不胜感激。

【问题讨论】:

  • 对于初学者,您可以使用char *results[][2] 进行简化。
  • 在c++中不能使用map这样的数据结构吗?

标签: c arrays algorithm function data-structures


【解决方案1】:

解决方案取决于您要采用哪种方法。保留单个字符数组(代码中的结果*)不会使您的数据动态化。本质上,您会希望使用存储(如果需要,嵌套)对的字典数据结构。在 C 语言中,我会使用结构来使其模块化。

首先,您需要一个结构来存储运动及其计数(比如奖牌计数)

struct sport {
  char *sport_name;
  int medal_count;
  //Any other details you want to store
};

然后,一个国家可以参加多项运动。因此我们需要制定国家结构。

struct Country{
  char *country_name;
  struct sport* results;
  //Any other details you want to store
};

现在让我们创建一个国家数据数组。

#define NO_OF_COUNTRIES 3  //You may fix this or make it dynamic
struct Country country_data[NO_OF_COUNTRIES]; 

您现在可以相应地填写数据。希望这会有所帮助。

【讨论】:

    【解决方案2】:

    考虑使用城市和国家列表而不是字符串数组。

    下面的代码解释了最简单的实现,它有两种结构和两种方法——添加新元素和搜索元素。

    试试这个代码,然后学习它:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct city
    {
        struct city * next;
        char * cityName;
        int counter;
    };
    
    struct country
    {
        struct country * next;
        char * coutryName;
        struct city * cities;
    };
    
    struct country * findCountry(struct country * coutries, char * country)
    {
        struct country * searchResult = NULL;
        while (coutries != NULL)
        {
            if (strcmp(country, coutries->coutryName) == 0)
            {
                searchResult = coutries;
                break;
            }
            coutries = coutries->next;
        }
        return searchResult;
    }
    
    struct country * addCountry(struct country * coutries, char * country)
    {
        struct country * newCountry = malloc(sizeof(struct country));
        newCountry->next = coutries;
        newCountry->coutryName = country;
        newCountry->cities = NULL;
        return newCountry;
    }
    
    struct city * findCity(struct city * cities, char * city)
    {
        struct city * searchResult = NULL;
        while (cities != NULL)
        {
            if (strcmp(city, cities->cityName) == 0)
            {
                searchResult = cities;
                break;
            }
            cities = cities->next;
        }
        return searchResult;
    }
    
    struct city * addCity(struct city * cities, char * city)
    {
        struct city * newCity = malloc(sizeof(struct city));
        newCity->cityName = city;
        newCity->next = cities;
        newCity->counter = 0;
        return newCity;
    }
    
    int main(void) 
    {
        char *results[] = { "Canada", "Cycling", "Canada", "Swimming", "India", "Swimming", "New Mexico",
            "Cycling", "New Mexico", "Cycling", "New Mexico", "Swimming" };
    
        struct country * countries = NULL;
        int nelements = sizeof(results) / sizeof(results[0]);
        // filling list of countries with sublists of cityes
        int i;
        for (i = 0; i < nelements; i+=2)
        {
            struct country * pCountry = findCountry(countries, results[i]);
            if (!pCountry)
            {
                countries = addCountry(countries, results[i]);
                pCountry = countries;
            }
            struct city * pCity = findCity(pCountry->cities, results[i+1]);
            if (!pCity)
            {
                pCountry->cities = addCity(pCountry->cities, results[i + 1]);
                pCity = pCountry->cities;
            }
            pCity->counter++;
        }
    
        // reading cities from all countries
        struct country * pCountry = countries;
        while (pCountry != NULL)
        {
            printf("%s\n",pCountry->coutryName);
            struct city * pCity = pCountry->cities;
            while (pCity != NULL)
            {
                printf("    %s %d\n", pCity->cityName, pCity->counter);
                pCity = pCity->next;
            }
            printf("\n");
            pCountry = pCountry->next;
        }
    
        return 0;
    }
    

    注意:在您的代码中,最后一个 "New Mexico" 就像 "New Mecico",在我的代码中,此错误类型已修复。

    更新

    注意 2:因为我在列表的开头添加元素,国家和城市的顺序与它们在源数组中首次提及的顺序相反。

    如果顺序很重要,您有两种选择:

    1) 重写我的代码以将新项目添加到列表末尾(这是很长的路)

    2) 在main 中重写for-loop 只是为了从末尾读取初始数组(这是最简单的方法):

    // filling list of countries with sublists of cityes
    int i;
    for (i = nelements-2; i >=0 ; i -= 2)
       {
       . . .
    

    【讨论】:

      【解决方案3】:

      给定您的数组,我可以看到该国家/地区名称可供选择。如果这是可用的格式数据,则可以按照以下代码进行操作。

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      int main(int argc, char *argv[])
      {
         char *results[] = {"Canada", "Cycling", "Canada", "Swimming", "India","Swimming", "New Mexico",
                     "Cycling", "New Mexico", "Cycling", "New Mexico", "Swimming"};
      
      
      
         int nelements, i, sport_count=0,country_change =0;
         char country[50];char sport[50];
         strcpy(country,results[0]);
         printf("%s\n", country);
         strcpy(sport,results[1]);
         nelements = sizeof(results) / sizeof(results[0]);
      
         for (i = 1 ; i < nelements; i++) 
         {
            if(((i%2)==0) && (strcmp(country,results[i])))
            {
               //sport_count++;
               printf("\t%s %d\n", sport,sport_count);
               country_change =1;
               strcpy(country,results[i]);
               printf("%s\n", country);
            }
            else if((i%2)==1)
            {
                if(country_change)
                {
                   strcpy(sport,results[i]);
                   country_change = 0;
                   sport_count = 0;
                }
      
                if(!strcmp(sport,results[i]))
                {
                    sport_count++;
                }
                else
                {
                    printf("\t%s %d\n", sport,sport_count);
                    strcpy(sport,results[i]);
                    sport_count = 1;
                }
                   //strcpy(country,results[i]);
             }
      
          }
          printf("\t%s %d\n", sport,sport_count);
      
       return 0;
      }
      

      基本上这就是我在这里尝试做的:

      1. 将第一个索引存储在变量中。
      2. 比在每个偶数迭代中检查国家名称是否等于存储的名称。如果不更新名称。
      3. 在每个奇数迭代中,您只需打印出名称即可。
      4. 运动名称存储在一个变量中,一个 int 变量sports_count 保存计数。
      5. 如果出现新国家/地区,请先打印运动名称,然后强制更新运动名称和相关变量。
      6. 最后的运动名称打印在循环之外。

        Output
        
        Canada
                Cycling 1
                Swimming 1
        India
                Swimming 1
        New Mexico
                Cycling 2
                Swimming 1
        

      【讨论】:

        【解决方案4】:

        我会使用一个 struct(如果你不熟悉,我总是在需要时使用 myStruct.c 提醒自己)和两个 arrays 作为数据成员,像这样:

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        
        #define COUNTRY_LENGTH 15
        #define MAX_SPORTS 5
        
        enum sport_name { CYCLING, SWIMMING };
        
        typedef struct Record {
          char country[COUNTRY_LENGTH];
          int sports[MAX_SPORTS];
        } Record;
        
        // return index of 'country' in 'array' if the 'country'
        // is found inside 'array', else -1
        int exists(char country[], Record* array, int size) {
            int i;
            for(i = 0; i < size; ++i)
                if(!strcmp(array[i].country, country))
                    return i;
            return -1;
        }
        
        int find_sport_index(char sport[]) {
            if(!strcmp(sport, "Cycling"))
                return CYCLING;
            if(!strcmp(sport, "Swimming"))
                return SWIMMING;
            printf("I couldn't find a sport index for %s\n!!! Do something...Undefined Behavior!", sport);
            return -1;
        }
        
        char* find_sport_string(int sport) {
            if(sport == CYCLING)
                return "Cycling";
            if(sport == SWIMMING)
                return "Swimming";
            printf("I couldn't find a sport string for sport index %d\n!!! Do something...", sport);
            return NULL;
        }
        
        int main(int argc, char *argv[]) {
            // you had a typo, New Mecico, I corrected it..Also you could have used a struct here... ;)
            char *results[] = {"Canada", "Cycling", "Canada", "Swimming", "India", "Swimming", "New Mexico",
                               "Cycling", "New Mexico", "Cycling", "New Mexico", "Swimming"};
        
        
        
            int nelements, i, j;
        
            nelements = sizeof(results) / sizeof(results[0]);
        
            const int records_size = nelements/2;
        
            Record record[records_size];
            for(i = 0; i < records_size; i++) {
                for(j = 0; j < COUNTRY_LENGTH; j++) 
                    record[i].country[j] = 0;
                for(j = 0; j < MAX_SPORTS; j++)
                    record[i].sports[j] = 0;
            }
        
            int country_index, records_count = 0;
            for(i = 0; i < nelements; ++i) {
                // results[i] is a country
                if(i % 2 == 0) {
                    country_index = exists(results[i], record, records_size);
                    if(country_index == -1) {
                        country_index = records_count++;
                        strcpy(record[country_index].country, results[i]);
                    }
                } else {
                    // result[i] is a sport
                    record[country_index].sports[find_sport_index(results[i])]++;
                }
            }    
        
        
            for(i = 0; i < records_size; ++i) {
                if(strlen(record[i].country)) {
                    printf("%s\n", record[i].country);
                    for(j = 0; j < MAX_SPORTS; j++) {
                        if(record[i].sports[j] != 0) {
                            printf("    %s %d\n", find_sport_string(j), record[i].sports[j]);
                        }
                    }
                }    
            }
        
            return 0;
        }
        

        输出:

        C02QT2UBFVH6-lm:~ gsamaras$ ./a.out 
        Canada
            Cycling 1
            Swimming 1
        India
            Swimming 1
        New Mexico
            Cycling 2
            Swimming 1
        

        这个想法是:

        1. 结构体Record保存着奥运会的记录,相关的 运动。
        2. Record.country 包含国家名称(我假设它 最多为 14 个字符,NULL 终止符为 +1,因此我 定义它为15)。
        3. Record.sports 是一个大小为 MAX_SPORTS 的数组 - 大小为 等于奥运会中的所有运动项目,但我假设它是 5。这个数组的每个位置都是一个计数器(每个国家在一项运动中获得的奖牌。例如,Record.sports[1] = 2 表示这个国家有 2 枚游泳奖牌. 但是我怎么知道它是游泳?我先验地决定,作为一个程序员,第一个计数器连接到 Cycling,第二个连接到游泳等等。我使用enum 使其更具可读性,而不是使用 神奇的数字。 (注意:您可以使用列表而不是数组,但这将是一个 该应用程序的矫枉过正。但是如果你想为了好玩(因为内存少一点),你 可以使用我们的List (C))。
        4. 你以一种奇怪的方式定义results[],因为你真的应该 为此使用了一个结构,但我使用了你的代码......所以我 需要一个Records 的数组,它的大小应该等于 国家数量,即results[] 大小的一半。 请注意,因为您将 results[] 定义为包含隐式 对乡村运动,除以二就足够了 确定Records 数组的大小。
        5. 我使用计数器循环遍历results[] 以填充record[] 中命名为i。当i 为偶数时,result[i] 包含一个国家,否则它包含一项运动。我使用模块 运营商 (%) 轻松确定。
        6. 如果record[]中不存在国家,那么我插入它,否则我 不要再次插入。在这两种情况下,我都想记住它的索引 record[],以便在下一次迭代中,我们将处理 运动,我们现在应该看record[]的哪个位置 进入并采取相应的行动。
        7. 现在,当我处理一项运动时,我想增加它的计数器 运动,但仅适用于相应的国家(请记住,我有 存储了我在上一次迭代中处理的国家/地区索引)。
        8. 然后我只打印,就是这样! :)

        【讨论】:

          【解决方案5】:

          此解决方案的想法是构建地图 - 表格,其中行对应于国家,列对应于体育赛事(或运动名称)。

          最大可能映射的内存(大小为 nelements/2 x nelements/2)使用 calloc 分配,但实际上如果 char *results[] 未更改,则它可以只是 int[6][6]

          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          
          int main(void) 
          {
              char *results[] = { "Canada", "Cycling", "Canada", "Swimming", "India", "Swimming", "New Mexico",
                  "Cycling", "New Mexico", "Cycling", "New Mexico", "Swimming" };
              int nelements = sizeof(results) / sizeof(results[0]);
              int i;
              // making empty map
              int ** map = calloc(nelements/2, sizeof(int*));
              for (i = 0; i < nelements / 2; i++)
                  map[i] = calloc(nelements/2, sizeof(int));
              char ** rowNames = calloc(nelements / 2, sizeof(char*));
              int usedRows = 0;
              char ** colNames = calloc(nelements / 2, sizeof(char*));
              int usedCols = 0;
          
              // filling the map
              // the outer loop for countries
              int c;
              for (c = 0; c < nelements; c+=2) {
                  int row = -1;
                  // Find country in the map (loop for rows)
                  for (i = 0; i < usedRows; i++) 
                  {
                      if (strcmp(results[c], rowNames[i]) == 0)
                      {
                          row = i;
                          break;
                      }
                  }
                  // or add if it is new country
                  if (row < 0)
                  {
                      row = usedRows;
                      rowNames[usedRows] = results[c];
                      usedRows++;
                  }
                  // Find sport in the map (loop for columns)
                  int col = -1;
                  for (i = 0; i < usedCols; i++)
                  {
                      if (strcmp(results[c+1], colNames[i]) == 0)
                      {
                          col = i;
                          break;
                      }
                  }
                  // or add if it is new sport
                  if (col < 0)
                  {
                      col = usedCols;
                      colNames[usedCols] = results[c+1];
                      usedCols++;
                  }
                  // Just count sport event in the current country
                  map[row][col]++;
              }
          
              // print results from map
              // the outer loop for countries (loop for rows in map)
              for (c = 0; c < usedRows; c++) {
                  printf("%s\n", rowNames[c]);
                  // the inner loop for sport
                  for (i = 0; i < usedCols; i++)
                      if (map[c][i])
                          printf("   %s %d\n", colNames[i], map[c][i]);
                  printf("\n");
              }
          
              return 0;
          }
          

          所以当map,以及rowNames(带国家)和colNames(带运动)被填满时,我们可以以任何方式输出数据。

          【讨论】:

            【解决方案6】:

            从答案的数量可以看出,有多种方法可以完成这项任务。对于国家或事件(但不是两者),您需要的一个元素是一个简单的查找表,其中包含国家条目或事件条目,以允许您区分 results 中的值是国家名称还是事件名称.一个简单的国家/地区查找(这里是全局的,但也可以是函数范围),例如以下工作:

            char *countries[] = { "Canada", "India", "New Mexico" }; /* countries lookup */
            

            您可以采取的另一个捷径是识别结果中的指针具有定义的函数范围,因此无需复制或分配内存来保存它们——它们已经存在于只读内存中。

            另一个有用的结构元素是记录与国家相关的事件,例如eventcnt。每次在国家/地区下添加事件时,该值都会增加。您可以使用类似于以下的国家/事件结构:

            typedef struct {
                char *country;
                char *event[MAXE];
                int eventcnt;
            } host;
            

            MAXE 是一个简单的常量,表示所涉及的最大事件,允许您对结构数组使用自动存储。(可以轻松更改为根据需要分配/重新分配存储)

            然后,您需要简单地循环遍历 results 数组一次,了解事件总是跟随在它们之前的国家。使用多个嵌套循环可将您遍历results 的次数保持为一次。本质上,您循环遍历results 中的每个指针,确定它是否指向国家/地区名称,如果它是国家/地区名称,则将其添加为您的host.country 值之一,或者跳过它如果是(无需更新指针以指向最后一次出现的国家/地区名称)

            由于涉及嵌套循环,一个简单的goto 提供了您需要确定何时处理country 名称或何时处理event 名称的所有控制,并允许您采取所需的操作每个案例。

            然后,只需打印/使用您想要的结果,这些结果现在包含在结构数组中,hidx(主机索引)包含所涉及的唯一主机总数。

            将各个部分放在一起,您可以执行类似以下的操作:

            #include <stdio.h>
            #include <string.h>
            
            /* constants max(countries, events) */
            enum { MAXC = 8, MAXE = 16 };
            
            char *countries[] = { "Canada", "India", "New Mexico" }; /* countries lookup */
            
            typedef struct {
                char *country;
                char *event[MAXE];
                int eventcnt;
            } host;
            
            int main (void) {
            
                char *results[] = { "Canada", "Cycling", "Canada", "Swimming", 
                                    "India", "Swimming", "New Mexico", "Cycling", 
                                    "New Mexico", "Cycling", "New Mexico", "Swimming"};
                host hosts[MAXC] = {{ .country = NULL }};
                int hidx = 0, i, j, country_count, current = 0, nelements;
            
                country_count = sizeof countries/sizeof *countries;
                nelements = sizeof results / sizeof *results;
            
                for (i = 0 ; i < nelements; i++) {          /* for each element */
                    for (j = 0; j < country_count; j++) {   /* check if country */
                        if (strcmp (results[i], countries[j]) == 0) { /* if so */
                            int k;
                            for (k = 0; k < hidx &&  /* check if already assigned */
                                strcmp (hosts[k].country, countries[j]); k++) {}
                            if (!hosts[k].country) { /* if not, assign ptr, increment */
                                hosts[hidx++].country = results[i];
                                current = hidx - 1;;
                            }
                            goto nextc; /* skip event adding */
                        }
                    } /* results[i] is not a country, check if event exists for host */
                    if (hosts[current].eventcnt < MAXE) {   /* if it doesn't, add it */
                        int k;
                        for (k = 0; k < hosts[current].eventcnt; k++)
                            if (strcmp (results[i], hosts[current].event[k]) == 0)
                                goto nextc;  /* already exists for host, skip add */
                        hosts[current].event[hosts[current].eventcnt++] = results[i];
                    }
                    nextc:;
                }
            
                for (i = 0; i < hidx; i++) {    /* output countries & events for each */
                    printf (" %s\n", hosts[i].country);
                    for (j = 0; j < hosts[i].eventcnt; j++)
                        printf ("     %s\n", hosts[i].event[j]);
                }
            
                return 0;
            }
            

            使用/输出示例

            $ ./bin/events
             Canada
                 Cycling
                 Swimming
             India
                 Swimming
             New Mexico
                 Cycling
                 Swimming
            

            查看所有答案。包含很多优点。如果您有任何问题,请告诉我。

            【讨论】:

              【解决方案7】:

              我会枚举运动和地点,添加 NUM_x 作为最后一个元素,这样枚举可以在以后轻松附加...

              typedef enum _sport_t
              {
                CYCLING,
                SWIMMING,
                NUM_SPORTS
              } sport_t;
              
              typedef enum _location_t
              {
                CANADA,
                INDIA,
                NEW_MEXICO,
                NUM_LOCATIONS
              } location_t;
              

              现在,您可以定义要打印出名称时使用的字符串数组...

              char* sports_name[NUM_SPORTS] = {"Cycling", "Swimming"};
              char* location_name[NUM_LOCATIONS] = {"Canada", "India", "New Mexico"};
              

              这种方法会稍微减少存储空间并提高效率,因为您在对列表进行分类时将比较枚举(整数)而不是字符串。

              您可能还想考虑使用所有位置和所有运动的二维布尔数组,指示所述位置是否有所述运动。

              typedef enum _bool_t
              {
                FALSE,
                TRUE
              } bool_t;
              
              bool_t sports_array[NUM_LOCATIONS][NUM_SPORTS] =
              { 
                {TRUE,TRUE},  // Canada
                {TRUE,FALSE}, // India
                {TRUE,TRUE},  // New Mexico
              };
              

              所以,你的循环应该是这样的......

              location_t l;
              sport_t s;
              
              for (l = (location_t)0; l < NUM_LOCATIONS; l++)
              {
                printf( " %s\n", location_name[l] );
                for (s = (sport_t)0; s < NUM_SPORTS; s++)
                {
                  if (sports_array[l,s])
                  {
                    printf( "     %s\n", sport_name[s] );
                  }
                }
              }
              

              【讨论】:

                猜你喜欢
                • 2012-06-27
                • 2012-02-19
                • 1970-01-01
                • 1970-01-01
                • 2016-04-01
                • 2013-10-11
                • 1970-01-01
                • 2011-02-02
                • 1970-01-01
                相关资源
                最近更新 更多