如何从C中删除数组中的重复字符串?

时间:2010-08-01 05:54:21

标签: c algorithm arrays duplicates duplicate-removal

我在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以匹配str
  2. 如果不匹配,则不执行任何操作
  3. 如果找到匹配,请将str放入strarray
  4. 现在我们有一个最多1个重复条目的strarray
  5. 将最高索引的strarray字符串添加到临时字符串数组的最低索引
  6. 继续向下进入strarray并检查每个元素
  7. 如果发现重复,请跳过
  8. 如果没有,请将其添加到临时字符串数组的下一个最高索引
  9. 反向临时字符串数组并复制到strarray
  10. 再一次,这是未经测试的(我现在正在实施它)。我希望那里的人能有更好的解决方案。

    项目的顺序很重要,代码必须使用C语言(而不是C ++)。应删除最低索引重复项并保留单个最高索引。

    谢谢!

4 个答案:

答案 0 :(得分:3)

典型的高效独特功能是:

  1. 对给定数组进行排序。
  2. 确认已设置同一项目的连续运行,以便只剩下一个。
  3. 我相信您可以将qsortstrcmp结合使用来完成第一部分;写一个有效的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;
    

    string进行第一次排序,删除排序集上的唯一元素集,然后依靠originalPosition求助。这样你仍然可以获得O(n lg n)性能,但你不会丢失原始订单。

    EDIT2: std::unique的简单C实现示例:

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

答案 1 :(得分:1)

你可以控制进入数组的输入吗?如果是这样,请执行以下操作:

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:如果你经常进行这种类型的操作,你应该远离数组作为你的存储结构,而是使用链表。它们可以更有效地从末端以外的位置删除元素。

答案 2 :(得分: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)。

答案 3 :(得分:0)

使用终端中的qsortman 3 qsort)算法对数组进行排序以查看应如何使用该算法,然后使用函数strcmp比较字符串并查找重复项< / p>

如果你想保留原始顺序,可以使用嵌套两个for的O(N ^ 2)复杂度算法,第一次选择一个元素与另一个进行比较,第二个将用于扫描数组的其余部分以查找所选元素是否重复。