Итак, я пытаюсь написать алгоритм быстрой сортировки на С ++ с векторами.

template <typename T>
T qsort(vector<T> arr) 
{
    int mid_idx = round(arr.size() / 2);
    int root_el = arr[mid_idx];
    
    vector<T> smaller;
    vector<T> bigger;
    vector<T> equals;
    for (T i: arr)
    {
        if (arr[i] > root_el)
            bigger.push_back(arr[i]);
        else if (arr[i] < root_el)
            smaller.push_back(arr[i]);
        else
            equals.push_back(arr[i]);
    }
    return (qsort(smaller) + equals + qsort(bigger));
}

И я получаю эту ошибку:

invalid operands to binary expression ('int' and 'vector<int>') [Semantic Issue]

Так ты можешь сказать мне, что не так?

-2
Willman 11 Ноя 2020 в 13:42

1 ответ

Лучший ответ

Есть несколько проблем с этим кодом C ++.

Функция шаблона qsort явно намеревается вернуть вектор из инструкции return в конце. Но он объявлен как возвращающий единственный объект типа T.

Кроме того, аргумент вектора передается с помощью значения , что дорого для больших векторов (дублирование). Я предлагаю вместо этого передать его по ссылке.

Итак, мы могли бы изменить верхнее объявление на это:

template <typename T>
vector<T>  qsort(vector<T>& arr) 
{

Более фундаментальная проблема: из кода кажется, что каждый вызов qsort рекурсивно и безоговорочно вызывает еще два вызова qsort. Если функция написана таким образом, программа не может завершиться. Или, скорее, операционная система убьет его, когда он исчерпает пространство стека и, таким образом, сделает незаконный доступ к памяти.

Каждый рекурсивный алгоритм требует некоторого условия завершения. В нашем случае делать нечего, если размер массива меньше 2, так как он, очевидно, уже отсортирован.

Итак, начало функции qsort можно было бы записать так:

#include  <vector>
#include  <cmath>
#include  <iostream>

using  std::vector;

template <typename T>
vector<T>  qsort(vector<T>& arr) 
{
    if (arr.size() < 2)
        return arr;  // so small it is already sorted !

Кроме того, в цикле for переменная i используется как целочисленный индекс, но объявляется как объект типа T. Вместо этого цикл можно было бы записать следующим образом:

    for (int i = 0; i < arr.size(); i++)
    {
        if (arr[i] > root_el)
            bigger.push_back(arr[i]);
        else if (arr[i] < root_el)
            smaller.push_back(arr[i]);
        else
            equals.push_back(arr[i]);
    }

Теперь мы подошли к последней инструкции return:

    return (qsort(smaller) + equals + qsort(bigger));

В приведенной выше строке кода очевидно предполагается, что для векторов оператор «+» означает конкатенацию векторов. Но на самом деле это не определено в стандарте C ++. Таким образом, нет определения для вектора «+» в области видимости, даже после включения файла заголовка <vector>.

Более того, если бы вопрос стандартизации «+» для векторов когда-либо встал бы перед международным комитетом по стандартизации C ++, возникло бы огромное давление, особенно со стороны сообщества вычислительной физики, в пользу определения его традиционным способом как в обычных учебниках математики и физики.

То есть значение [10,20,30] + [2,4,6] должно быть [12,24,36], а не [10,20,30,2,4,6], как, по-видимому, подразумевает ваш код.

Таким образом, мы должны выполнить эту конкатенацию векторов вручную , например, как это, используя вставить метод STL:

    auto  lts = qsort(smaller);
    auto  gts = qsort(bigger);

    // concatenate sorted vectors:
    lts.insert(lts.end(),  equals.begin(),  equals.end());
    lts.insert(lts.end(),  gts.begin(),     gts.end());

    return lts;

В целом следующая версия кода работает должным образом:

template <typename T>
vector<T>  qsort(vector<T>& arr) 
{
    if (arr.size() < 2)
        return arr;  // so small it is already sorted !

    int  mid_idx  =  arr.size() / 2;
    T    root_el  =  arr[mid_idx];

    vector<T>  smaller;
    vector<T>  bigger;
    vector<T>  equals;

    for (int i = 0; i < arr.size(); i++)
    {
        if (arr[i] > root_el)
            bigger.push_back(arr[i]);
        else if (arr[i] < root_el)
            smaller.push_back(arr[i]);
        else
            equals.push_back(arr[i]);
    }

    auto  lts = qsort(smaller);
    auto  gts = qsort(bigger);

    // concatenate sorted vectors:
    lts.insert(lts.end(),  equals.begin(),  equals.end());
    lts.insert(lts.end(),  gts.begin(),     gts.end());

    return lts;
}

Тестовый код:

int main()
{

    vector<int>  v1 { 609,396,620,173, 742,996,880,125,
                      478,745,206,798, 998,124,960,175  };

    vector<int>  v2 = qsort(v1);

    for (auto m : v2) {
        std::cout << m << ' ';
    }
    std::cout << std::endl;

    return EXIT_SUCCESS;
}

Выход программы:

$ ./q64784877.x
124 125 173 175 206 396 478 609 620 742 745 798 880 960 996 998 
$ 

Примечания по эффективности:

Ваш код создает несколько временных векторов и, таким образом, динамически выделяет значительный объем памяти. Если вы посмотрите на традиционные версии Quicksort, код не выделяет дополнительной памяти. Он выполняет всю свою работу в исходном массиве, но это немного усложняет алгоритм.

Кроме того, приведенный выше код использует рекурсию до самого нижнего конца , останавливаясь только тогда, когда подвекторы уменьшаются до размера 1. На практике, вероятно, более эффективно переключиться на нерекурсивный, более простой такой алгоритм, как Сортировка вставкой, когда размер подмассива меньше 10 или 15. Для тестирования на вашей платформе для точного оптимального порогового значения.

1
jpmarinier 11 Ноя 2020 в 22:47