Итак, у вас есть список n
городов, идентифицированных по их названиям:
char city[n][20] = {"Amsterdam", "Beijing", "Cairo", ..., "Zurich"};
Обратите внимание, что каждый город также определяется по позиции в массиве: Амстердам равен 0, Брюссель равен 1, и т. д.
Эти города должны быть сгруппированы в непересекающиеся множества. Наборы описаны как деревья. У каждого города есть родитель. Город, родителем которого является сам, является городом root для этого набора. Он идентифицирует набор.
Так что вы должны как-то хранить родителя. Ваша идея состоит в том, чтобы сохранить имя родителя, а затем сравнить имена при обнаружении узла root:
char parent[n][20]; // initialize to same strings as city
Вы можете сделать это, но было бы проще, если бы вы просто сохранили парант index:
int parent[n];
Когда вы соединяете два города, вы должны сначала определить, какие это города. Вы должны определить n
, который вы передаете своим функциям. (Вы передаете n
, который в main
- это число городов и, следовательно, димеизон массива. В C, n
- один за пределами допустимого диапазона индексов массива.)
Наконец, вы получаете свои ранги:
int rank[n];
Теперь вам нужны следующие функции:
Функция для связи строки с индексом. Эта функция может не работать, если город не найден, поэтому она может вернуть неправильный индекс, -1
, чтобы указать на ошибку поиска. Нужно знать количество городов и названия городов. Ему не нужно знать о наборах.
int findCity(char city[][20], int n, const char *name)
{
int i;
for (i = 0; i < n; i++) {
if (strcmp(city[i], name) == 0) {
return i;
}
}
return -1;
}
Функция, чтобы найти набор города, то есть "root город" для набора. Эта функция должна знать только о родителе каждого города. Если массив настроен правильно, т.е. если k
и все возможные parent[k]
являются действительными индексами, и если родительский элемент thr root является самим root, эта функция не может завершиться с ошибкой. Тогда этой функции не нужно знать о количестве городов:
int findSet(int parent[], int k)
{
while (parent[k] != k) {
k = parent[k];
}
return k;
}
Функция, чтобы определить, являются ли два города одинаковыми. Это действительно просто «удобная функция» для findSet
:
int isSameSet(int parent[], int a, int b)
{
return (findSet(parent, a) == findSet(parent, b));
}
Наконец, функция для объединения двух наборов: она должна знать о рангах в дополнение к родителям, но опять же, если все массивы настроены правильно, не нужно указывать количество городов. Это как раз та функция, которую вы написали:
void unionSet(int parent[], int rank[], int a, int b)
{
int s = findSet(parent, a);
int t = findSet(parent, b);
if (s != t) {
if (rank[s] > rank[t]) {
parent[t] = s;
} else {
if (rank[s] == rank[t]) rank[t]++;
parent[s] = t;
}
}
}
См. Пример в действии здесь .
Подводя итог: используйте названия городов только для пользователя интерфейс, когда вы берете ввод и когда вы печатаете вывод. Сделайте лог c наборов по целочисленным индексам.
Дополнительные примечания:
- Лучшим представлением может быть создание структуры города, которая содержит данные для каждого города, включая Родитель и звание. Вместо целочисленных индексов вы можете использовать указатели на эти структуры.
- В настоящее время большая часть времени тратится впустую на поиск городов в массиве. Это нормально для небольшого примера, но в реальной программе вам придется использовать хотя бы двоичный поиск для поиска имен.