【问题标题】:Removing Duplicates in an array in C在 C 中删除数组中的重复项
【发布时间】:2011-02-19 01:16:36
【问题描述】:

这个问题有点复杂。这里的问题是去除重复元素并将数组的唯一元素保存到具有原始序列的另一个数组中。

例如:

如果输入是 b a c a d t

结果应该是:b a c d t 处于输入输入的确切状态。

所以,为了对数组进行排序,然后检查无法工作,因为我丢失了原始序列。有人建议我使用索引数组,但我不知道该怎么做。那么您对此有何建议?


对于那些愿意回答问题的人,我想补充一些具体信息。

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

是我的功能。应删除其重复项并将其存储在不同数组中的数组是 words[100]。因此,该过程将在此完成。我首先考虑将单词的所有元素放入另一个数组并对该数组进行排序,但经过一些测试后这不起作用。只是提醒解决者:)。

【问题讨论】:

  • 这是chars 的数组,就像您的示例所暗示的那样?在这种情况下,只需保留一个包含 256 个布尔值的数组,这些值指示您以前见过哪些字符。
  • 它必须是有序的......
  • 我有一些问题 - 输入是一次输入 1,还是一次输入?这是char 的数组,还是具有更高界限的其他类型?
  • @thomas 它是一个字符串数组。我只是保持这个例子简短。 char *words[100] -> 这种数组。 @phil 输入一次全部输入。
  • @Trevor Tippins:不。对客户来说是必要的。其实我不是 C 方面的专家,但客户总是对的,所以我也不得不问这些基本的事情。

标签: c arrays duplicates duplicate-removal


【解决方案1】:

嗯,这是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]

这和你想要的一样吗?如果字母之间有空格,则可以修改该方法,但如果您使用intfloatdoublechar *作为类型,则此方法根本无法缩放。

编辑

我发布然后看到你的澄清,它是char * 的数组。我会更新方法。


我希望这不是太多的代码。我改编了this QuickSort algorithm 并基本上为它添加了索引内存。该算法是 O(n log n),因为以下 3 个步骤是相加的,这是其中 2 个步骤的最坏情况复杂度。

  1. 对字符串数组进行排序,但每次交换也应反映在索引数组中。在此阶段之后,originalIndices 的第 i 个元素保存已排序数组的第 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;
}

【讨论】:

  • 非常感谢。实际上,你给我这个想法就足够了,我可以编写这个想法。感谢您的努力。我很快就会试试这个。
  • 没问题。自从我使用 C 以来已经有一段时间了,所以那里可能存在一些极端情况,而且它不是最干燥的代码。希望对您有所帮助!
【解决方案2】:
  1. 遍历数组中的项——O(n)操作
  2. 对于每个项目,将其添加到另一个排序数组中
  3. 在将其添加到排序数组之前,请检查该条目是否已存在 - O(log n) 操作

最后,O(n log n) 操作

【讨论】:

  • 按照OP的说法,新的数组需要保持原来的排序。
  • 您可以将步骤 2. 和 3. 的排序数组替换为一个哈希集,并且您将获得整个操作的摊销 O(n)。这假设您对要删除重复的元素有一个哈希函数,但我们已经假设我们有一个总订单,所以......
  • @Nick 也许 MasterGaurav 解释得不够好,但他们显然正在考虑一种保留原始数组顺序的算法(重复的元素在结果数组中的位置表示第一次出现在原始数组中)
  • @Pascal 从目前的措辞中似乎不太清楚。我对如何在 O(log n) 时间检查现有元素而不对数组进行排序有点模糊。除非我们在谈论第三个临时数组?
  • @Nick 我很确定这是第三种结构(可能根本不是数组)。将其视为一个哈希集,那么它就不会与源数组或结果数组混淆:)
【解决方案3】:

我认为在 C 中您可以创建第二个数组。然后,仅当该元素尚未在发送数组中时,才从原始数组中复制该元素。 这也保留了元素的顺序。

如果您一个一个地读取元素,您可以在插入原始数组之前丢弃该元素,这样可以加快处理速度。

【讨论】:

  • 你能解释一下你是如何在这里加速的吗?搜索成本仍然是名称.. 排序的最佳 O(log n)
  • 伙计们 :)。这里的问题是完成工作实际上不是复杂性。所以请尽量专注于此。
【解决方案4】:

正如 Thomas 在评论中所建议的,如果保证数组的每个元素都来自一组有限的值(例如 char),那么您可以在 O(n) 时间内实现这一点。

  1. 保留一个包含 256 个 bool 的数组(或 int,如果您的编译器不支持 bool),或者数组中可能包含许多不同的离散值。将所有值初始化为false
  2. 逐一扫描输入数组。
  3. 对于每个元素,如果bool数组中对应的值为false,则将其添加到输出数组中,并将bool数组值设置为true。否则,什么也不做。

【讨论】:

  • 问题是数组不是字符数组而是字符串数组。
  • 是的,我看到你现在已经添加了。
【解决方案5】:

你知道如何处理 char 类型,对吧? 您可以对字符串做同样的事情,但不是使用布尔数组(技术上是“set”对象的实现),您必须使用线性字符串数组来模拟“set”(或布尔数组)你已经遇到了。 IE。你有一个你已经看到的字符串数组,对于每个新字符串,你检查它是否在“seen”字符串数组中,如果是,那么你忽略它(不是唯一的),如果它不在数组中,你添加它到看到的字符串数组和输出。如果您有少量不同的字符串(低于 1000 个),您可以忽略性能优化,只需将每个新字符串与您之前看到的所有字符串进行比较。

但是,对于大量字符串(几千个),您需要稍微优化一下:

1) 每次向已经看到的字符串数组添加新字符串时,使用插入排序算法对数组进行排序。不要使用 quickSort,因为插入排序在数据几乎排序时往往会更快。

2) 检查字符串是否在数组中时,使用二分查找。

如果不同字符串的数量是合理的(即您没有数十亿个唯一字符串),那么这种方法应该足够快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-15
    • 2021-02-20
    • 1970-01-01
    • 2012-03-25
    • 2019-07-11
    相关资源
    最近更新 更多