c语言 - 排列一个数组,另一个数组执行相同的操作



对不起,如果英语不好,但我有问题请: 我有这个数组[数量],我将对其进行排序:

int main() {
int numberOfFruits[] = {3, 5, 2, 4, 1};
//I do sorting algorithm
}

但我也希望其他数组做同样的事情并匹配。

char fruits[] = {"apple", "orange", "bannana", "peach", "watermelon"};

所以在algrithm之后,它将是:

int numberOfFruits[] = {1, 2, 3, 4, 5};
char fruits[] = {"watermelon", "banana", "apple", "peach", "orange"};

我该怎么做? 谢谢。

首先,类型char的变量无法保存字符串(除非它保存值 0,否则它是一个空字符串)。字符串是char序列,其中最后一个char的值为 0。通常,字符串存储在数组中,或者使用指针char *指向存储在其他位置的字符串。在您的情况下,水果的名称存储在字符串文本中,因此您可以使用char *。既然你想要一个字符串数组,你应该声明一个char *数组:

char *fruits[] = {.....};

现在谈谈你的实际问题:

当您有多个属于一起的事物时,可以通过声明一个struct将它们聚合在一起:

struct fruit
{
int number;
char *name;
};

您可以定义一个struct fruit数组,如下所示:

struct fruit fruits[] = 
{ 
{1, "watermelon"},
{2, "banana"},
{3, "apple"},
{4, "peach"},
{5, "orange"}
}

当你现在交换两个元素fruits时,你可以numbername一起交换:

/* Swap the i-th and j-th element in fruits */
struct fruit tmp = fruits[i];
fruits[i] = fruits[j];
fruits[j] = tmp;

我对HAL9000的答案投了赞成票,因为它提出了比并行数组更好的数据结构,但它实际上并没有回答标题问题:如何对并行数组进行排序?

并行阵列

OP提出了一个并行阵列的例子。我会把我最喜欢的水果加到他的身上:

int    fruit_IDs  [] = { 3,       5,        2,        4,       1              10,           -7           };
char * fruit_names[] = { "apple", "orange", "banana", "peach", "watermellon", "grapefruit", "strawberry" };

请注意,ID 与列表的任何排序或索引无关。这只是另一条信息。这是另一个并行数组结构:

char * first_names[] = { "Harry",  "Ron",     "Hermione", "Severus" };
char * last_names [] = { "Potter", "Weasley", "Granger",  "Snape"   };
int    year_born  [] = { 1980,     1980,      1979,       1960      };

那么,我们如何在不破坏它们的索引关联的情况下对它们进行排序呢?

索引数组

最简单的方法是使用一个额外的数组,该数组仅用作并行数组的索引。坚持水果:

int fruit_indexes[NUM_FRUITS] = { 0, 1, 2, 3, 4, 5, 6 };

现在,我们在并行阵列中有了一层间接层。要打印数组,我们可以这样写:

for (int n = 0;  n < NUM_FRUITS;  n++)
printf( "%d %sn", fruit_IDs[fruit_indexes[n]], fruit_names[fruit_indexes[n]] );

可以想象,如果我们对索引数组的元素重新排序,我们也会对并行数组的查看方式进行重新排序。例如,要按相反顺序查看水果:

int fruit_indexes[NUM_FRUITS] = { 6, 5, 4, 3, 2, 1, 0 };

您应该立即注意到这个额外的数组会产生后果:

  • 从好的方面来说,我们不再需要在并行数组中移动东西来重新排序它们。
  • 在负侧,我们有一个额外的数组要处理。

让我们qsort()索引数组!

首先,我们需要一个水果 ID 的比较器,通过索引数组通过间接访问:

int compare_indexed_fruits_by_ID( const void * a, const void * b )
{
// A and b are pointers to elements of the fruit_indices[] array
int ai = *(const int *)a;
int bi = *(const int *)b;
// We use those indices to compare fruit IDs
if (fruit_IDs[ai] < fruit_IDs[bi]) return -1;
if (fruit_IDs[ai] > fruit_IDs[bi]) return  1;
return 0;
}

按水果名称比较的工作方式相同,只是现在我们比较名称而不是 ID:

int compare_indexed_fruits_by_name( const void * a, const void * b )
{
// A and b are pointers to elements of the fruit_indices[] array
int ai = *(const int *)a;
int bi = *(const int *)b;
// Use those indices to compare fruit names
return strcmp( fruit_names[ai], fruit_names[bi] );
}

此后,对 QSort 的调用非常简单 — 传递正确的比较函数并对索引数组进行排序:

qsort( fruit_indexes, NUM_FRUITS, sizeof(*fruit_indexes), &compare_indexed_fruits_by_ID );

打印我们的水果将为您提供:

-7 草莓
1 个水梅
2 香蕉
3 苹果
4 桃
子 5 橙
子 10 葡萄柚

我们可以很容易地按名称排序:

qsort( fruit_indexes, NUM_FRUITS, sizeof(*fruit_indexes), &compare_indexed_fruits_by_name );

生产:


苹果3个 香蕉2根 葡萄

10个 橙子5个 桃

-7个草莓
1个西

动态数据、静态数组

索引数组的好处之一是,在向其他数组添加和删除数据时,您仍然可以使用它。请记住,要设置静态数组,您需要这样的数据结构:

#define MAX_NUM_PEOPLE 1000
char * first_names[MAX_NUM_PEOPLE];
char * last_names [MAX_NUM_PEOPLE];
int    indices    [MAX_NUM_PEOPLE];
int    num_people = 0;

添加新数据,只需像往常一样将其推到现有数组的背面,当然要检查您是否有空间:

if (num_people < MAX_NUM_PEOPLE)
{
first_names[num_people] = new first name;
last_names [num_people] = new last name;
indices    [num_people] = num_people;
num_people += 1;

新元素自然会乱序,因此您将不得不再次对索引数组进行排序。

qsort( indices, ... );
}

删除一个元素,你只需要在再次对索引数组进行排序之前将其从末尾交换掉。不要忘记检查您的数组边界!

if (index_to_delete > 0 && index_to_delete < num_people)
{
first_names[indices[index_to_delete]] = first_names[num_people-1];
last_names [indices[index_to_delete]] = last_names [num_people-1];
indices[index_to_delete] = indices[num_people-1];
num_people -= 1;
qsort( indices, ... );
}
动态

数据,动态数组

这的工作方式与静态数组版本非常相似,只有您的数组是动态分配的。

char ** first_names = NULL;
char ** last_names  = NULL;
int   * indices     = NULL;
int     num_people  = 0;
...
first_names = malloc( new_num_people * sizeof(*first_names) );
last_names  = malloc( new_num_people * sizeof(*last_names) );
indices     = malloc( num_num_people * sizeof(*indices) );
if (!first_names || !last_names || !indices)
{
free( indices );
free( last_names );
free( first_names );
fooey();
}
num_people = new_num_people;

使用realloc()增加数组的大小也像往常一样工作,但增加了复杂性,您必须检查每个数组是否已成功重新分配。

消除索引数组

该索引数组可能有点令人讨厌。排序后摆脱它会很好。而且,当然,你可以!

在排序之前动态创建它,像往常一样将其初始化为0, 1, 2, ...,使用数组中介进行排序,然后花时间使用新顺序重新排列其他数组。

void sort_fruits_by( void (*comparitor)(const void *, const void *) )
{
int indices[num_fruits];
for (int n = 0;  n < num_fruits;  n += 1)
indices[n] = n;
qsort( indices, num_fruits, sizeof(*indices), comparitor );
reorder_people_by_index( indices );
}

这种重新排列位需要一些时间和空间:

void reorder_people_by_index( int * indices )
{
char * temp_names[num_people];
memcpy( temp_names, first_names, num_people*sizeof(char*) );
for (int n = 0;  n < num_people;  n += 1)
first_names[indices[n]] = temp_names[n];
memcpy( temp_names, last_names, num_people*sizeof(char*) );
for (int n = 0;  n < num_people;  n += 1)
last_names[indices[n]] = temp_names[n];
}

对于人们来说,我们可以临时重用char *,但对于水果,我们需要一个临时int和一个char *——每种类型的并行阵列一个。

现在并行数组本身的排序方式不同。但是您失去了仅对整数数组进行排序的优势。

结论

您可以看到,维护并行阵列确实不太困难。悲伤在于细节 — 您正在维护多个数组!

这就是为什么拥有单个结构化数据数组变得更加方便的原因。

但是,如果您必须使用并行数组来做到这一点,则可以完成。

相关内容

最新更新