如何对存储姓名的二维字符串数组排序以展示出现次数Top5?
如何在禁用<string.h>时,按姓名出现次数降序排序二维字符串数组并输出TOP5
我的需求是对存储姓名的二维字符串数组,按姓名出现次数从多到少排序,最终输出格式如下:
TOP 5 PLAYERS 1. GEORGE - WINS: X 2. HENRY - WINS: X 3. JOHN - WINS: X 4. MARRY - WINS: X 5. JULIA - WINS: X
其中X是姓名在数组中的出现次数。
限制条件:不能使用<string.h>库,仅允许调用<stdio.h>中的函数。
目前我已经实现了从scores.txt读取姓名存入二维数组并展示的功能,但缺少排序和统计逻辑,当前只能输出文件中读取的前5个原始姓名,现有代码如下:
#include <stdio.h> #define MAX_USERNAME_CHARS 20 // 假设MAX_USERNAME_CHARS是已定义的宏 void leaderboard(FILE *scores) { int totalNames = 4; int row; // 注意:这里未初始化,会导致未定义行为 char tempName[MAX_USERNAME_CHARS-1]; char namesArray[500][MAX_USERNAME_CHARS-1]; scores = fopen("scores.txt","r"); while (fscanf(scores,"%s",tempName) == 1) { int strlength; for (strlength = 0; tempName[strlength] != '\0';strlength++); for (int i=0; i <= strlength; i++) { namesArray[row][i] = tempName[i]; } row++; } //Insert Sorting Code Here printf("\nTOP 5 PLAYERS\n"); for (int i=0;i<=totalNames;i++) { printf("%d. ",i+1); for (int j=0;j<=MAX_USERNAME_CHARS-2;j++) { printf("%c",namesArray[i][j]); } printf(" - WINS: X\n"); } }
现在需要补充统计和排序的逻辑,完成需求。
解决方案
要完成需求,需要分三步:统计每个姓名的出现次数、按次数降序排序、输出TOP5,以下是完整实现:
1. 自定义字符串工具函数(替代<string.h>接口)
因为不能用<string.h>,自己实现字符串相等判断和复制功能:
// 判断两个字符串是否相等,相等返回1,不等返回0 int strEquals(const char *a, const char *b) { int i = 0; while (a[i] != '\0' && b[i] != '\0') { if (a[i] != b[i]) { return 0; } i++; } // 两个字符串同时结束才算相等 return (a[i] == '\0' && b[i] == '\0'); } // 复制字符串到目标地址(替代strcpy) void strCopy(char *dest, const char *src) { int i = 0; while (src[i] != '\0') { dest[i] = src[i]; i++; } dest[i] = '\0'; // 手动添加字符串结束符 }
2. 定义结构体存储姓名与次数
用结构体关联姓名和对应的出现次数,方便后续排序操作:
typedef struct { char name[MAX_USERNAME_CHARS-1]; int count; } PlayerStats;
3. 完整修改后的leaderboard函数
#include <stdio.h> #define MAX_USERNAME_CHARS 20 typedef struct { char name[MAX_USERNAME_CHARS-1]; int count; } PlayerStats; // 自定义字符串相等判断函数 int strEquals(const char *a, const char *b) { int i = 0; while (a[i] != '\0' && b[i] != '\0') { if (a[i] != b[i]) { return 0; } i++; } return (a[i] == '\0' && b[i] == '\0'); } // 自定义字符串复制函数 void strCopy(char *dest, const char *src) { int i = 0; while (src[i] != '\0') { dest[i] = src[i]; i++; } dest[i] = '\0'; } void leaderboard(FILE *scores) { int row = 0; // 初始化row,修复原代码的未定义行为 char tempName[MAX_USERNAME_CHARS-1]; char namesArray[500][MAX_USERNAME_CHARS-1]; PlayerStats stats[500]; int statsCount = 0; scores = fopen("scores.txt", "r"); if (!scores) { // 添加文件打开失败的判断 printf("Failed to open scores.txt\n"); return; } // 读取所有姓名到二维数组 while (fscanf(scores, "%s", tempName) == 1) { int strlength = 0; while (tempName[strlength] != '\0') strlength++; for (int i = 0; i <= strlength; i++) { namesArray[row][i] = tempName[i]; } row++; } fclose(scores); // 读取完成后关闭文件 // 统计每个姓名的出现次数 for (int i = 0; i < row; i++) { int found = 0; // 检查当前姓名是否已经统计过 for (int j = 0; j < statsCount; j++) { if (strEquals(namesArray[i], stats[j].name)) { stats[j].count++; found = 1; break; } } // 如果没统计过,添加到stats数组 if (!found) { strCopy(stats[statsCount].name, namesArray[i]); stats[statsCount].count = 1; statsCount++; } } // 按出现次数降序排序(冒泡排序,适合小规模数据) for (int i = 0; i < statsCount - 1; i++) { for (int j = 0; j < statsCount - 1 - i; j++) { if (stats[j].count < stats[j+1].count) { // 交换两个PlayerStats元素 PlayerStats temp = stats[j]; stats[j] = stats[j+1]; stats[j+1] = temp; } } } // 输出TOP5玩家 printf("\nTOP 5 PLAYERS\n"); int topCount = (statsCount < 5) ? statsCount : 5; for (int i = 0; i < topCount; i++) { printf("%d. %s - WINS: %d\n", i+1, stats[i].name, stats[i].count); } }
关键修改点说明
- 修复了原代码中
row未初始化的问题,避免未定义行为 - 添加了文件打开失败的判断,增强代码鲁棒性
- 实现了自定义的字符串比较和复制函数,完全避开
<string.h> - 使用结构体存储姓名和次数,完成去重统计
- 用冒泡排序实现按次数降序排序(数据量小,冒泡排序足够高效)
- 处理了总玩家数不足5的情况,避免数组越界
内容的提问来源于stack exchange,提问作者Khy J
相关产品推荐
相关产品推荐

