2010-08-01 6 views
2

У меня есть массив строк в C и целое число, указывающее, сколько строк в массиве.Как удалить повторяющиеся строки из массива в C?

char *strarray[MAX]; 
int strcount; 

В этом массиве, самый высокий показатель (где 10 выше, чем 0) добавляется самый последний элемент и самый низкий показатель добавлен самый дальний элемент. Порядок элементов в массиве имеет значение.

Мне нужен быстрый способ проверить массив для дубликатов, удалить все, кроме самого высокого индекса, дубликата и свернуть массив.

Например:

strarray[0] = "Line 1"; 
strarray[1] = "Line 2"; 
strarray[2] = "Line 3"; 
strarray[3] = "Line 2"; 
strarray[4] = "Line 4"; 

станет:

strarray[0] = "Line 1"; 
strarray[1] = "Line 3"; 
strarray[2] = "Line 2"; 
strarray[3] = "Line 4"; 

Индекс 1 из исходного массива был удален и индексы 2, 3, и 4 скользил вниз, чтобы заполнить этот пробел.

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

Алгоритм, представленный ниже, будет запускаться каждый раз, когда новая строка будет добавлена ​​в strarray.

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

  1. Поиска всей strarray для матча ул
  2. Если не подходит, ничего не делать
  3. Если матч найден , поставить ул в strarray
  4. Теперь у нас есть strarray с максами входа 1 дубликата
  5. Добавить высокий индекс strarray строки низкого индекса временных массива строк
  6. Продолжить вниз в strarray и проверить каждый элемент
  7. Если дубликат найден, пропустите его
  8. Если нет, добавьте его к следующему самому высокому индексу временного массива строк
  9. Reverse массив строк временного и скопировать strarray

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

Порядок элементов важен, и код должен использовать язык C (а не C++). Самые низкие дубликаты индексов должны быть удалены и сохранен один самый высокий индекс.

Спасибо!

ответ

3

Типичная эффективность уникальная функция заключается в следующем:

  1. Сортировка данного массива.
  2. Убедитесь, что последовательных прогонов одного и того же элемента настроены так, что остается только один.

Я считаю, что вы можете использовать qsort в сочетании с strcmp для выполнения первой части; но написать эффективный remove будет все на вас.

К сожалению, у меня нет конкретных идей; это вид серой области для меня, потому что я обычно с помощью C++, где это было бы просто:

std::vector<std::string> src; 
std::sort(src.begin(), src.end()); 
src.remove(std::unique(src.begin(), src.end()), src.end); 

Я знаю, что вы не можете использовать C++, но реализация должна быть по существу то же самое.

Потому что вам нужно, чтобы сохранить первоначальный заказ, вы можете иметь что-то вроде:

typedef struct 
{ 
    int originalPosition; 
    char * string; 
} tempUniqueEntry; 

Do первого сорта относительно string, удалить уникальные наборы элементов на отсортированном множестве, то прибегают относительно originalPosition. Таким образом, вы все равно получаете O (n lg n) производительность, но вы не теряете первоначальный порядок.

EDIT2: Простой C Пример реализации std::unique:

tempUniqueEntry* unique (tempUniqueEntry * first, tempUniqueEntry * last) 
{ 
    tempUniqueEntry *result=first; 
    while (++first != last) 
    { 
    if (strcmp(result->string,first->string)) 
     *(++result)=*first; 
    } 
    return ++result; 
} 
+1

не сортировал бы порядок элементов? –

+0

@Jerry: Ответ отредактирован. –

+0

Спасибо за ваше редактирование! Я немного ржавый по сортировке, но я могу взять это отсюда. Я собираюсь попробовать вашу идею и посмотреть, как хорошо она работает. Из того, что я понимаю, мне нужно итерировать strarray, создавая временный массив tempUniqueEntry в этом процессе. Сортировка tempArray по строке, удаление дубликатов, сортировка tempArray по позиции, а затем восстановление strarray. Верный? –

0

Вы можете контролировать вход, как это происходит в массив? Если это так, просто сделать что-то вроде этого:

int addToArray(const char * toadd, char * strarray[], int strcount) 
{ 
    const int toaddlen = strlen(toadd); 

    // Add new string to end. 
    // Remember to add one for the \0 terminator. 
    strarray[strcount] = malloc(sizeof(char) * (toaddlen + 1)); 
    strncpy(strarray[strcount], toadd, toaddlen + 1); 

    // Search for a duplicate. 
    // Note that we are cutting the new array short by one. 
    for(int i = 0; i < strcount; ++i) 
    { 
     if (strncmp(strarray[i], toaddlen + 1) == 0) 
     { 
      // Found duplicate. 
      // Remove it and compact. 
      // Note use of new array size here. 
      free(strarray[i]); 
      for(int k = i + 1; k < strcount + 1; ++k) 
       strarray[i] = strarray[k]; 

      strarray[strcount] = null; 
      return strcount; 
     } 
    } 

    // No duplicate found. 
    return (strcount + 1); 
} 

Вы всегда можете использовать вышеуказанную функцию зацикливание по элементам существующего массива, создание нового массива без дубликатов.

PS: Если вы делаете этот тип операции много, вы должны отойти от массива в качестве своей структуры хранения и вместо этого использовать связанный список. Они намного эффективнее для удаления элементов из другого места, кроме конца.

+0

Это хорошо работает; это лучше, чем оригинальное решение OP. +1. Но, к сожалению, производительность по-прежнему остается в порядке n-квадрата :( –

+0

Как я понимаю ваше решение, если оно в strarray уже ничего не делает. Если это не так, оно добавляет его. Если я прав в своем понимании, это не сработает Я могу управлять вводом, когда он вводит массив, но этот метод не дал результат, который я дал в своем сообщении. Мне нужно, чтобы сохранившийся дубликат был в самом высоком, а не самом нижнем индексе. Если toadd уже существует в strarray [1], он не будет добавлен в strarray [N], где N> 1 –

+0

@Jerry Smith. Ваш пример не так тогда. Он должен читать 1, 3, 2, 4. Я скоро исправлю свое решение. Но это намного более дорогая операция, потому что это потребует уплотнения массива каждый раз. – jdmichal

1

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

unsigned int i; 
for (i = n; i > 0; i--) 
{ 
    unsigned int j; 

    if (strarray[i - 1] == NULL) 
    { 
     continue; 
    } 

    for (j = i - 1; j > 0; j--) 
    { 
     if (strcmp(strarray[i - 1], strarray[j - 1]) == 0) 
     { 
      strarray[j - 1] = NULL; 
     } 
    } 
} 

Тогда вы просто нужно отфильтровать нулевые указатели из вашего массива (который я оставлю как упражнение).

Другой подход состоял бы в том, чтобы итератировать назад по массиву и вставить каждый элемент в (сбалансированное) двоичное дерево поиска по мере продвижения. Если элемент уже находится в дереве двоичного поиска, отметьте элемент массива (например, установите элемент массива на NULL) и перейдите к нему. Когда вы обработали весь массив, отфильтруйте отмеченные элементы, как и раньше. Это будет немного больше накладных расходов и будет потреблять больше места, но его время работы будет O (n log n) вместо O (n^2).

+0

То, что я имел в виду на шаге 5, просто: // где 0 - самый низкий индекс, а 9 - самый большой доступный индекс temparray [0] = strarray [9]; –

1

Сортировка массива с помощью алгоритма, как qsort (man 3 qsort в терминале, чтобы увидеть, как она должна быть использована), а затем использовать функцию strcmp для сравнения строк и найти дубликаты

Если вы хотите его поддерживать первоначальный заказ вы можете использовать алгоритм сложности O (N^2), вложенный два for, первый каждый раз выбирает элемент для сравнения с другим, а второй для будет использоваться для сканирования остальной части массива, чтобы найти, является ли выбранный элемент дублировать.

+1

Делать так, чтобы он не выполнял первоначальный заказ –