Удаление Дубликатов в массиве в C - PullRequest
1 голос
/ 13 мая 2010

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

Например:

Если ввод введен b a c a d t

Результат должен быть: b a c d t в точном состоянии, в котором введен ввод.

Итак, для сортировки массива проверка не может работать, так как я потерял исходную последовательность. Мне посоветовали использовать массив индексов, но я не знаю, как это сделать. Так что вы посоветуете сделать это?


Для тех, кто готов ответить на вопрос, я хотел бы добавить определенную информацию.

char** finduni(char *words[100],int limit)
{
//
//Methods here
//
}

это моя функция. Массив, дубликаты которого следует удалить и сохранить в другом массиве, представляет собой слова [100]. Итак, процесс будет сделан на этом. Сначала я подумал о том, чтобы поместить все элементы слов в другой массив и отсортировать этот массив, но после некоторых тестов это не сработало. Просто напоминание для решателей:).

Ответы [ 5 ]

3 голосов
/ 13 мая 2010

Ну, вот версия для char типов. Обратите внимание, что это не масштабируется.

#include "stdio.h"
#include "string.h"

void removeDuplicates(unsigned char *string)
{
   unsigned char allCharacters [256] = { 0 };
   int lookAt;
   int writeTo = 0;
   for(lookAt = 0; lookAt < strlen(string); lookAt++)
   {
      if(allCharacters[ string[lookAt] ] == 0)
      {
         allCharacters[ string[lookAt] ] = 1;  // mark it seen
         string[writeTo++] = string[lookAt];     // copy it
      }
   }
   string[writeTo] = '\0';
}

int main()
{
   char word[] = "abbbcdefbbbghasdddaiouasdf";
   removeDuplicates(word);
   printf("Word is now [%s]\n", word);
   return 0;
}

Следующий вывод:

Word is now [abcdefghsiou]

Это что-то вроде того, что вы хотите? Вы можете изменить метод, если между буквами есть пробелы, но если вы используете int, float, double или char * в качестве типов, этот метод не будет масштабироваться вообще.

EDIT

Я написал, а затем увидел ваше разъяснение, где это массив char *. Я обновлю метод.


Надеюсь, это не слишком много кода. Я адаптировал этот алгоритм QuickSort и добавил к нему индексную память. Алгоритм O (n log n), так как 3 шага ниже являются аддитивными, и это сложность наихудшего случая для 2 из них.

  1. Сортировка массива строк, но каждый своп также должен быть отражен в массиве индекса. После этого этапа i-й элемент originalIndices содержит исходный индекс i-го элемента отсортированного массива.
  2. Удалите дублирующиеся элементы в отсортированном массиве, установив для них значение NULL и установив значение индекса на elements, что является максимальным значением, которое может быть.
  3. Сортируйте массив исходных индексов и убедитесь, что каждый своп отражен в массиве строк. Это возвращает нам исходный массив строк, за исключением того, что дубликаты находятся в конце, и все они NULL.
  4. Для удобства я возвращаю новое количество элементов.

Код:

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

void sortArrayAndSetCriteria(char **arr, int elements, int *originalIndices)
{
   #define  MAX_LEVELS  1000
   char *piv;
   int  beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R;
   int idx, cidx;
   for(idx = 0; idx < elements; idx++)
      originalIndices[idx] = idx;
   beg[0] = 0;
   end[0] = elements;
   while (i>=0)
   {
      L = beg[i];
      R = end[i] - 1;
      if (L<R)
      {
         piv = arr[L];
         cidx = originalIndices[L];
         if (i==MAX_LEVELS-1)
            return;
         while (L < R)
         {
            while (strcmp(arr[R], piv) >= 0 && L < R) R--;
            if (L < R)
            {
               arr[L] = arr[R];
               originalIndices[L++] = originalIndices[R];
            }
            while (strcmp(arr[L], piv) <= 0 && L < R) L++;
            if (L < R)
            {
               arr[R] = arr[L];
               originalIndices[R--] = originalIndices[L];
            }
         }
         arr[L] = piv;
         originalIndices[L] = cidx;
         beg[i + 1] = L + 1;
         end[i + 1] = end[i];
         end[i++] = L;
      }
      else
      {
         i--;
      }
   }
}

int removeDuplicatesFromBoth(char **arr, int elements, int *originalIndices)
{
   // now remove duplicates
   int i = 1, newLimit = 1;
   char *curr = arr[0];
   while (i < elements)
   {
      if(strcmp(curr, arr[i]) == 0)
      {
         arr[i] = NULL;   // free this if it was malloc'd
         originalIndices[i] = elements;  // place it at the end
      }
      else
      {
         curr = arr[i];
         newLimit++;
      }
      i++;
   }
   return newLimit;
}

void sortArrayBasedOnCriteria(char **arr, int elements, int *originalIndices)
{
   #define  MAX_LEVELS  1000
   int piv;
   int beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R;
   int idx;
   char *cidx;
   beg[0] = 0;
   end[0] = elements;
   while (i>=0)
   {
      L = beg[i];
      R = end[i] - 1;
      if (L<R)
      {
         piv = originalIndices[L];
         cidx = arr[L];
         if (i==MAX_LEVELS-1)
            return;
         while (L < R)
         {
            while (originalIndices[R] >= piv && L < R) R--;
            if (L < R)
            {
               arr[L] = arr[R];
               originalIndices[L++] = originalIndices[R];
            }
            while (originalIndices[L] <= piv && L < R) L++;
            if (L < R)
            {
               arr[R] = arr[L];
               originalIndices[R--] = originalIndices[L];
            }
         }
         arr[L] = cidx;
         originalIndices[L] = piv;
         beg[i + 1] = L + 1;
         end[i + 1] = end[i];
         end[i++] = L;
      }
      else
      {
         i--;
      }
   }
}

int removeDuplicateStrings(char *words[], int limit)
{
   int *indices = (int *)malloc(limit * sizeof(int));
   int newLimit;
   sortArrayAndSetCriteria(words, limit, indices);
   newLimit = removeDuplicatesFromBoth(words, limit, indices);
   sortArrayBasedOnCriteria(words, limit, indices);
   free(indices);
   return newLimit;
}

int main()
{
   char *words[] = { "abc", "def", "bad", "hello", "captain", "def", "abc", "goodbye" };
   int newLimit = removeDuplicateStrings(words, 8);
   int i = 0;
   for(i = 0; i < newLimit; i++) printf(" Word @ %d = %s\n", i, words[i]);
   return 0;
}
0 голосов
/ 13 мая 2010

Вы знаете, как сделать это для типа char, верно? Вы можете сделать то же самое со строками, но вместо использования массива bools (который технически является реализацией объекта "set"), вам придется симулировать "set" (или массив bools) с линейным массивом строк ты уже сталкивался. То есть у вас есть массив строк, которые вы уже видели, для каждой новой строки вы проверяете, находится ли она в массиве «видимых» строк, если она есть, то вы игнорируете ее (не уникальную), если ее нет в массиве, вы добавляете для обоих массивов видимых строк и вывода. Если у вас есть небольшое количество различных строк (ниже 1000), вы можете игнорировать оптимизацию производительности и просто сравнивать каждую новую строку со всем, что вы уже видели ранее.

Однако при большом количестве строк (несколько тысяч) вам нужно будет немного оптимизировать:

1) Каждый раз, когда вы добавляете новую строку в массив строк, которые вы уже видели, сортируйте массив с помощью алгоритма сортировки вставками. Не используйте быструю сортировку, потому что сортировка вставками имеет тенденцию выполняться быстрее, когда данные почти сортируются.

2) При проверке, находится ли строка в массиве, используйте бинарный поиск.

Если число различных строк разумно (т. Е. У вас нет миллиардов уникальных строк), такой подход должен быть достаточно быстрым.

0 голосов
/ 13 мая 2010

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

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

0 голосов
/ 13 мая 2010

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

  1. Сохраняйте массив 256 bool (или int, если ваш компилятор не поддерживает bool), или, тем не менее, в массиве может быть много разных дискретных значений. Инициализируйте все значения в false.
  2. Сканирование входного массива по одному.
  3. Для каждого элемента, если соответствующее значение в массиве bool равно false, добавьте его в выходной массив и установите значение массива bool на true. В противном случае ничего не делать.
0 голосов
/ 13 мая 2010
  1. Просматривать элементы в массиве - O(n) операция
  2. Для каждого элемента добавьте его в другой отсортированный массив
  3. Перед добавлением его в отсортированный массив проверьте, существует ли уже запись - O(log n) операция

Наконец, O(n log n) операция

...