Удалить дубликаты из отсортированного массива

Qus: удаление дубликатов из отсортированного массива. Для отсортированного массива удалите дубликаты на месте, чтобы каждый элемент появлялся только один раз и возвращал новую длину.

Обратите внимание, что даже если мы хотим, чтобы вы вернули новую длину, не забудьте также изменить исходный массив на месте

Не выделяйте дополнительное пространство для другого массива, вы должны сделать это на месте с постоянной памятью.

Я пробовал следующий код, может ли кто-нибудь помочь, где я ошибаюсь?

   #include<iostream>
    #include<vector>
    using namespace std;

    int removeDuplicates(vector<int> &A) {
        int m=A.size();
        if(m<=1) return m;
        vector<int> :: iterator i=A.begin();
        vector<int> :: iterator j=A.begin()+1;
        vector<int> :: iterator temp;
        while(i!=A.end() && j!=A.end())
        {
            while(j!=A.end() && *i == *j)
            {
                temp=j;
                j++;
                A.erase(temp);
            }
            i=j;
            j++;
        }
        return A.size();
    }

    int main()
    {
        vector<int> vec={0,0,0,0,0,0,0,4,4,7,7,7,7,9};
        cout<<"ans="<<removeDuplicates(vec);
        return 0;
    }

person Naveen Kumar    schedule 01.06.2016    source источник
comment
И каков результат работы программы? Каков ожидаемый результат? Вы пробовали пройтись по коду построчно в отладчике, чтобы увидеть, что происходит?   -  person Some programmer dude    schedule 01.06.2016
comment
Не изобретайте велосипед. Используйте std::unique и _ 2_. Ссылка на std::unique даже показывает, как это делается.   -  person NathanOliver    schedule 01.06.2016
comment
После вызова erase все итераторы вектора стали недействительными. Лучше работай с индексами   -  person Semyon Burov    schedule 01.06.2016
comment
@ Joachim Pileborg: Я старался изо всех сил отлаживать, вставляя так много операторов печати туда и сюда, но все еще не мог определить, где я ошибаюсь !!, Для публикации кода я удалил комментарии, чтобы сделать его более понятным   -  person Naveen Kumar    schedule 01.06.2016
comment
@NathanOliver: Я тебя не понял?   -  person Naveen Kumar    schedule 01.06.2016
comment
@ Семен Буров: спасибо, попробую, но все равно с помощью itertors ??   -  person Naveen Kumar    schedule 01.06.2016
comment
@ user6038386 Чего вы не понимаете? Вместо того, чтобы пытаться сделать это самостоятельно, используйте встроенные стандартные функции, которые сделают это за вас.   -  person NathanOliver    schedule 01.06.2016
comment
@NathanOliver, я полагаю, это задание (или вопрос на собеседовании).   -  person SergeyA    schedule 01.06.2016
comment
@SergeyA Ничего из предложенного мной не нарушает ограничений в вопросе.   -  person NathanOliver    schedule 01.06.2016
comment
@NathanOliver, согласен. Но, возможно, противоречит духу :) На самом деле это хороший вопрос для собеседования, я думаю, я использовал его пару раз. Я бы не принял std::unique в качестве ответа (но кандидат получит плюс за знание этого)   -  person SergeyA    schedule 01.06.2016
comment
@SergeyA: вы правы, конечно std::unique было дополнением, которое я знаю, но это потенциальный вопрос на собеседовании, где это не поможет !!   -  person Naveen Kumar    schedule 02.06.2016


Ответы (4)


Когда вы увеличиваете j, затем erase элемент там, элементы, начинающиеся с j + 1, перемещаются вниз. Вы пропускаете элемент, увеличивая его.

Лучшим подходом было бы просто скопировать неповторяющиеся элементы из одного итератора в другой и установить новую длину в конце основного цикла. Ваш текущий подход потенциально O (n ^ 2), слишком медленный для практического использования.

person Mark Ransom    schedule 01.06.2016
comment
все, что вы сказали, совершенно верно ... это сработает, если я поставлю j = A.erase (j) вместо temp = j; j ++; A.erase (temp); ..... но я все еще не мог понять, как я пропускаю элемент, увеличивая - person Naveen Kumar; 02.06.2016
comment
потенциально, чем j=A.erase(j) отличается от temp=j; j++; A.erase(temp); ?? - person Naveen Kumar; 02.06.2016

Думаю, это то, что вам нужно. Эта функция перебирает массив от хвоста к голове и считает одинаковые значения. Затем выполняет сдвиг уже уникальных значений на уникальное. Это не меняет фактический размер вектора, поскольку это, вероятно, потребует перераспределения памяти внутри вектора.

int removeDuplicates(vector<int> &vec) {
    int currentVal = vec.size() - 1;
    int uniqueNumber = 0;

    while (currentVal >= 0) {
        ++uniqueNumber;
        int sameVal = currentVal;
        while (sameVal > 0 && vec[sameVal - 1] == vec[currentVal])
            --sameVal;

        int shiftSize = uniqueNumber;
        for (int k = 1; k < shiftSize; ++k) {
            vec[sameVal + k] = vec[currentVal + k];
        }

        currentVal = sameVal - 1;
    }
    return uniqueNumber;
}
person Semyon Burov    schedule 01.06.2016
comment
В этом есть небольшая ошибка. Если максимальное значение std::vector::size_type больше максимального значения int, тогда возможно, что currentVal = vec.size() - 1; может быть UB при переполнении currentVal. Это также верно, если передан вектор размера 0. - person NathanOliver; 01.06.2016
comment
Рассматриваемая задача выглядит как домашнее задание, поэтому я использовал больше учеников типа int. Изменение int на std::ptrdiff_t должно решить эту проблему. - person Semyon Burov; 01.06.2016

Вас просят использовать массив. Хотя вектор во многом похож, это не одно и то же. Взгляните на приведенный ниже пример кода.

Вдобавок вас просят оставить выделенную память таким же. Вы не можете гарантировать, что при использовании вектора его размер может увеличиваться / уменьшаться после добавления / удаления элементов, а при удалении элемента данные в массиве за вектором будут перераспределены и перезаписаны.

int main()
{

    int arr[14] = {0,0,0,0,0,4,4,4,4,5,5,5,7,9};
    int last_non_duplicate_index = 0;
    int current_index = 0;
    int size = 14;
    int new_size = size;
    bool is_last_value = false;

    // you can use for interchangeably
    while(!is_last_value)
    {
        current_index++; // move one position ahead
        if(current_index < size) // out of bonds check
        {
            if(arr[last_non_duplicate_index] != arr[current_index]) // value at position of current index is different
            {
                last_non_duplicate_index++; // increase index of the last value which was not a duplicate by one
                arr[last_non_duplicate_index] = arr[current_index]; // write at that index 
                // e.g. if last index was 0 -> increase it to 1 and rewrite whatsever under arr[1] (current index)
            }
            else // values are the same
            { 
                new_size--; // devrease the size
            }
        }
        else
        {
            is_last_value = true; // current_index >= size -> out of bonds
        }
    }

    for (int i = 0; i < new_size; i++)
    {
        std::cout << "arr[" << i << "]" << " = " << arr[i] << std::endl;
    }
    std::cout << "New size: " << new_size << std::endl;
    return 0;
}
person Plump Worm    schedule 01.06.2016

Вы можете сделать это с помощью таких итераторов:

#include<iostream>
#include<vector>

using namespace std;

int removeDuplicates(vector<int> &A) {
    int m = A.size();
    if(m <= 1) return m;

    for (auto it1 = A.begin(); it1 != A.end(); it1++) {
        for (auto it2 = it1 + 1; it2 != A.end();) {
            if (*it1 == *it2) {
                it2 = A.erase(it2);
            } else {
                it2++;
            }
        }
    }

    return A.size();
}

int main()
{
    vector<int> vec = { 0, 0, 0, 0, 0, 0, 0, 4, 4, 7, 7, 7, 7, 9 };
    cout << "ans=" << removeDuplicates(vec);
    return 0;
}
person Viktor Simkó    schedule 01.06.2016