Согласно условиям задачи, требуется отсортировать массив структур. Данные должны быть отсортированы следующим образом: при сравнении двух участников тот, кто решил больше задач, будет выше. Если количество решённых задач одинаково, то сначала идёт участник с меньшим штрафом. Если штрафы также равны, то первым будет тот, чей логин стоит раньше в лексикографическом порядке. Сортировка должна выполняться по методу Хоара. Алгоритм работает, если я сравниваю только числа, но при использовании компаратора для некоторых данных возникает ошибка “Segmentation fault (core dumped)”. Я не могу понять, что я делаю не так?
Мой код показан ниже:
// https://ru.wikipedia.org/wiki/Быстрая_сортировка
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct Participants {
std::string login;
int complited_tasks;
int fine;
Participants(std::string l, int c, int f) {
login = l;
complited_tasks = c;
fine = f;
}
};
void print_arr(std::vector<Participants> a);
void quick_sort(std::vector<Participants>& arr, int indx_left, int indx_right);
int partition(std::vector<int>& arr, int indx_left, int indx_right);
void get_participants(std::vector<Participants>& p);
bool is_better(Participants p1, Participants p2);
void get_test_data(std::vector<Participants>& p);
int main() {
std::vector<Participants> p;
// get_participants(p);
get_test_data(p);
quick_sort(p, 0, p.size() - 1);
print_arr(p);
return 0;
}
// создаёт вектор данных, при которых возникает ошибка
void get_test_data(std::vector<Participants>& p) {
p.push_back(Participants("tufhdbi", 76, 58));
p.push_back(Participants("rqyoazgbmv", 59, 78));
p.push_back(Participants("qvgtrlkmyrm", 35, 27));
p.push_back(Participants("tgcytmfpj", 70, 27));
p.push_back(Participants("xvf", 84, 19));
p.push_back(Participants("jzpnpgpcqbsmczrgvsu", 30, 3));
p.push_back(Participants("evjphqnevjqakze", 92, 15));
p.push_back(Participants("wwzwv", 87, 8));
p.push_back(Participants("tfpiqpwmkkduhcupp", 1, 82));
p.push_back(Participants("tzamkyqadmybky", 5, 81));
p.push_back(Participants("amotrxgba", 0, 6));
p.push_back(Participants("easfsifbzkfezn", 100, 28));
p.push_back(Participants("kivdiy", 70, 47));
}
// функция чтения данных
void get_participants(std::vector<Participants>& p) {
std::string inp;
std::getline(std::cin, inp);
int n = std::stoi(inp);
for (int i = 0; i < n; i++) {
std::vector<std::string> split_inp;
std::string t;
std::getline(std::cin, inp);
for (unsigned int j = 0; j < inp.size(); j++) {
if (inp[j] == ' ') {
split_inp.push_back(t);
t.clear();
continue;
}
t += inp[j];
}
split_inp.push_back(t);
p.push_back(Participants(split_inp[0],
std::stoi(split_inp[1]),
std::stoi(split_inp[2])));
}
}
void print_arr(std::vector<Participants> a) {
for (unsigned int i = 0; i < a.size(); i++) {
std::cout << a[i].login << '\n';
}
}
int partition(std::vector<Participants>& arr, int indx_left, int indx_right) {
Participants pivot = arr[rand() % arr.size()];
int i = indx_left - 1, j = indx_right + 1;
while (true) {
do {
i++;
} while (is_better(arr[i], pivot));
do {
j--;
} while (!is_better(arr[j], pivot));
if (i >= j)
return j;
std::swap(arr[i], arr[j]);
}
}
// функция сравнения двух элементов
// возвращает true, если первый элемент лучше второго
bool is_better(Participants p1, Participants p2) {
bool p1_better = false;
if (p1.complited_tasks > p2.complited_tasks) {
p1_better = true;
}
else if (p1.complited_tasks == p2.complited_tasks) {
if (p1.fine < p2.fine)
p1_better = true;
else if (p1.fine == p2.fine) {
if (std::lexicographical_compare(p1.login.begin(), p1.login.end(),
p2.login.begin(), p2.login.end()))
p1_better = true;
}
}
return p1_better;
}
void quick_sort(std::vector<Participants>& arr, int indx_left, int indx_right) {
if (indx_left < indx_right) {
int stop_indx = partition(arr, indx_left, indx_right);
quick_sort(arr, indx_left, stop_indx);
quick_sort(arr, stop_indx + 1, indx_right);
}
return;
}
С входными данными в виде
input:
5
alla 4 100
gena 6 1000
gosha 2 90
rita 2 90
timofey 4 80
output:
gena
timofey
alla
gosha
rita
или
input:
5
alla 0 0
gena 0 0
gosha 0 0
rita 0 0
timofey 0 0
output:
alla
gena
gosha
rita
timofey
Всё работает корректно, но возникает ошибка при этом тесте:
input:
13
tufhdbi 76 58
rqyoazgbmv 59 78
qvgtrlkmyrm 35 27
tgcytmfpj 70 27
xvf 84 19
jzpnpgpcqbsmczrgvsu 30 3
evjphqnevjqakze 92 15
wwzwv 87 8
tfpiqpwmkkduhcupp 1 82
tzamkyqadmybky 5 81
amotrxgba 0 6
easfsifbzkfezn 100 28
kivdiy 70 47
output:
easfsifbzkfezn
evjphqnevjqakze
wwzwv
xvf
tufhdbi
tgcytmfpj
kivdiy
rqyoazgbmv
qvgtrlkmyrm
jzpnpgpcqbsmczrgvsu
tzamkyqadmybky
tfpiqpwmkkduhcupp
amotrxgba
Как i
, так и j
могут выйти за пределы вектора в этих циклах:
do {
i++;
} while (is_better(arr[i], pivot));
do {
j--;
} while (!is_better(arr[j], pivot));
Когда это происходит, возникает неопределённое поведение — возможно, сегментация.
Причина, по которой i
или j
могут выйти за границы, двояка:
-
Вы выбрали опорный элемент случайным образом из всего массива, в то время как вам нужно выбрать его из текущей партиции. В результате вы можете выбрать опорный элемент, который больше, чем все элементы в текущей партиции, или меньше, чем все они. В алгоритме Хоара очень важно, чтобы циклы встретили опорный элемент в какой-то момент. Если это не гарантировано, алгоритм может сломаться.
-
Ваш второй внутренний цикл продолжается, когда
arr[j]
равен опорному элементу, но в этом случае он должен завершиться. Если вы позволите циклу продолжаться в этом случае, вы рискуете, что условиеwhile
никогда не станет истинным. Для алгоритма жизненно важно, чтобы цикл завершался, когда встречается опорный элемент.
Вот ваша функция partition
с минимальными изменениями, чтобы исправить эти две проблемы:
int partition(std::vector<Participants>& arr, int indx_left, int indx_right) {
// Опорный элемент должен выбираться из текущей партиции:
Participants pivot = arr[indx_left + rand() % (indx_right + 1 - indx_left)];
int i = indx_left - 1, j = indx_right + 1;
while (true) {
do {
i++;
} while (is_better(arr[i], pivot));
do {
j--;
// инвертируем аргументы и не отрицательно, чтобы цикл завершился, когда равен
} while (is_better(pivot, arr[j]));
if (i >= j)
return j;
std::swap(arr[i], arr[j]);
}
}
Ответ
Чтобы исправить ошибку сегментации при реализации алгоритма быстрой сортировки Хоара для структуры, содержащей информацию о участниках, давайте сначала рассмотрим проблему в вашем коде.
Проблема
Ошибка "Segmentation fault (core dumped)" возникает из-за выхода за границы векторов. В вашем коде переменные индексов i
и j
могут выйти за пределы массива, если не будет должной проверки. Это связано с тем, что вы выбираете опорный элемент случайным образом из всего массива вместо текущего подмассива, и условия цикла не гарантируют, что i
и j
останутся в пределах допустимых значений.
Решение
Для устранения этой проблемы вам нужно изменить два момента в функции partition
:
- Выбор опорного элемента нужно производить из текущего подмассива, а не всего массива.
- Внутренний цикл для
j
должен выходить, если элемент равен опорному, чтобы предотвратить зацикливание.
Исправленный код
Вот исправленный вариант вашей функции partition
:
int partition(std::vector<Participants>& arr, int indx_left, int indx_right) {
// Опорный элемент выбирается из текущего подмассива
Participants pivot = arr[indx_left + rand() % (indx_right + 1 - indx_left)];
int i = indx_left - 1, j = indx_right + 1;
while (true) {
// Ищем элемент, который больше опорного
do {
i++;
} while (is_better(arr[i], pivot));
// Ищем элемент, который меньше опорного
do {
j--;
} while (is_better(pivot, arr[j]));
// Если индексы пересеклись, возвращаем j
if (i >= j)
return j;
// Меняем местами элементы
std::swap(arr[i], arr[j]);
}
}
Дополнительные рекомендации
-
Проверка границ массива: Каждую итерацию цикла не забудьте проверять, не вышли ли
i
иj
за границы. Это можно сделать с помощью дополнительного условия, хотя в нашем случае после исправления основной проблемы это уже не должно происходить. -
Случайный выбор опорного элемента: Использование случайного выбора опорного элемента может привести к неоптимальным случаям. Тем не менее, в вашем случае это было применимо.
- Логика сравнения: Логика в функции
is_better
тоже может быть улучшена, например, с помощью кода:
bool is_better(const Participants& p1, const Participants& p2) {
return (p1.complited_tasks > p2.complited_tasks) ||
(p1.complited_tasks == p2.complited_tasks && p1.fine < p2.fine) ||
(p1.complited_tasks == p2.complited_tasks && p1.fine == p2.fine && p1.login < p2.login);
}
Заключение
После внесенных исправлений ваш алгоритм быстрой сортировки должен корректно работать и не вызывать ошибки сегментации. Попробуйте протестировать его с данными, которые вы указали, и убедитесь, что вывод соответствует ожиданиям. Если возникнут дополнительные вопросы, не стесняйтесь задавать их.