6

I am learning C and came over the topic of sorting. I wrote a comp() function in and used qsort to sort an array of int. Now for the next task I need to remove the duplicates from the array.
Is it possible to sort and remove duplicates at the same time?

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>    
int indexes[10] = { 0, 98, 45, 65, 45, 98, 78, 56, 65, 45 };

int comp(const void * elem1, const void * elem2) {

    int f = *((int*) elem1);
    int s = *((int*) elem2);

    if (f > s) {    
        return 1;
    }    
    if (f < s) {    
        return -1;
    }    
    return 0;
}

void printIndexArray() {    
    int i = 0;    
    for (i = 0; i < 10; i++) {    
        printf("i is %d\n", indexes[i]);    
    }
}

int main() {    
    qsort(indexes, sizeof(indexes) / sizeof(int), sizeof(int), comp);    
    printIndexArray();    
    return 0;
}
4

5 回答 5

2

由于您的号码已经排序,因此删除欺骗很容易。在 C++ 中,它甚至内置为std::unique

http://en.cppreference.com/w/cpp/algorithm/unique

假设你想自己做,你可以这样做unique

int* unique (int* first, int* last)
{
  if (first==last) return last;

  int* result = first;
  while (++first != last)
  {
    if (!(*result == *first)) 
      *(++result)=*first;
  }
  return ++result;
}
于 2013-09-20T20:01:00.103 回答
1

那是使用合并排序删除重复项的代码。这段代码完成了删除工作:

else if(a[p1] == a[p2])
{
    merged[p] = a[p1];
    p1++;
    p2++;
}

那是迭代合并排序,而递归版本会更容易。

#include <stdio.h>
#include <stdlib.h>

#define min(a,b) (((a) < (b)) ? (a) : (b))

int indexes[10] = { 0, 98, 45, 65, 45, 98, 78, 56, 65, 45 };

void merge(int *a, int s, int m, int e)
{
    int p1 = s;
    int p2 = m + 1;
    int * merged = (int*)malloc(sizeof(int) * (e - s + 1));
    int p = 0;
    while(p1 < m + 1 && p2 < e + 1)
    {
        if(a[p1] > a[p2])
        {
            merged[p] = a[p2];
            p2++;
        }
        else if(a[p1] == a[p2])
        {
            merged[p] = a[p1];
            p1++;
            p2++;
        }
        else
        {
            merged[p] = a[p1];
            p1++;
        }
        p++;
    }

    while(p1 < m + 1)
    {
        merged[p++] = a[p1++];
    }

    while(p2 < e + 1)
        merged[p++] = a[p2++];

    int i;
    for(i = 0;i < (e -s+1); i++)
    {
        a[s + i] = merged[i];
    }

    free(merged);
}

void merge_sort(int *a, int n)
{
    int width;
    for(width = 1; width < n; width = 2 * width)
    {
        int i;
        for(i = 0; i < n; i = i + 2 * width)
        {
            merge(a, i, min(i + width - 1, n - 1), min(i + 2 * width - 1, n - 1) );
        }
    }
}

void printIndexArray()
{    
    int i = 0;    
    for(i = 0; i < 10; i++)
    {    
        printf("i is %d\n", indexes[i]);    
    }
}

int main()
{
    merge_sort(indexes, sizeof(indexes) / sizeof(int) );
    printIndexArray();
    return 0;
}
于 2013-09-21T00:06:23.700 回答
1
#include <stdio.h>
#include <stdlib.h>

int indexes[10] = { 0, 98, 45, 65, 45, 98, 78, 56, 65, 45 };

size_t undup(int array[], size_t len)
{
size_t src,dst;

if (!len) return 0;
for (src=dst=1; src < len; src++) {
        if (array[dst-1] == array[src]) continue;
        array[dst++] = array[src];
        }
return dst;
}

int comp(const void * elem1, const void * elem2) {

    int f = *((int*) elem1);
    int s = *((int*) elem2);

    if (f > s)     return 1;
    if (f < s)     return -1;

    return 0;
}

void printIndexArray(size_t len) {
    size_t i = 0;
    for (i = 0; i < len; i++) {
        printf("array[%zu] is %d\n", i, indexes[i]);
    }
}

int main() {
    size_t len = 10;
    printf("Before sort\n" );
    printIndexArray(len);

    qsort(indexes, sizeof indexes / sizeof indexes[0], sizeof indexes[0], comp);
    printf("After sort\n" );
    printIndexArray(len);

    len = undup(indexes,10);
    printf("After undup\n" );
    printIndexArray(len);

    return 0;
}
于 2013-09-22T14:07:48.377 回答
1

是的

这可以通过mergesort来实现。如果左右都相同,只需合并一个值

于 2013-09-20T19:59:28.347 回答
0

简短的回答是:是的。

长答案是:总是有可能的,但是这样做的复杂性在很大程度上取决于您使用的算法。

更复杂的算法,如快速排序、慢速排序、桶排序和直基数排序不适合这种增强,因为它们依赖于连续数组中的数据,可以隐式地拆分为子数组。因此,当您检测到重复项时,您无法轻易将其取出。同样,这是可能的,但对于初学者来说肯定不是问题。

气泡排序、插入排序和壳排序等不太复杂的就地算法使其相对容易:您只需将检测到的重复项之一替换为排序大于所有合法值的哨兵值,然后让它上升到顶部。在那之后,你只需要舀出哨兵值的精华,你就完成了。

真正有助于删除重复项的算法是使用在此过程中增长/缩小的中间数组的算法;在这些情况下,当您检测到重复时,您可以缩小或跳过增长这些中间数组之一。候选者是合并排序和堆排序。

但是请注意,仅对数组进行排序并在第二个单独的步骤中消除重复项更为谨慎。为什么?因为消除重复增加了排序算法的内部循环的复杂性,在大多数相关情况下为 O(n*log(n))。但是从排序数组中消除重复是一个 O(n) 操作,这使得拆分操作比融合操作更快。

于 2013-09-20T23:39:32.530 回答