C:从未排序的链表中删除重复项

时间:2017-03-22 13:27:06

标签: c linked-list duplicates

我一直在研究这个问题的时间超过了我现在想承认的时间,这让我疯了!给定一个简单的链表,比如说除了指向下一个节点(data)的指针之外还存储一个整数(next),我想要一个算法去除重复而不排序或依赖辅助功能。

以前的问题已经被问及Java中未分类的链表,它利用了Java提供的辅助功能。这与C完全相关,不使用辅助函数。

我已经修改了代码,并且已经将其用于某些情况,但不是全部。这是一个完整的,可验证的示例 - 我已经包含push()函数来创建链接列表和main()包含测试用例,但我的问题涉及的逻辑是{{{ 1}}单独:

removeDuplicates()

我还提供了一张描述逻辑的图片,如果不清楚我在做什么:http://imgur.com/DbnBOq2

5 个答案:

答案 0 :(得分:2)

/* Program to remove duplicates in an unsorted linked list */

#include <bits/stdc++.h>
using namespace std;

/* A linked list node */
struct Node
{
    int data;
    struct Node *next;
};

// Utility function to create a new Node
struct Node *newNode(int data)
{
   Node *temp = new Node;
   temp->data = data;
   temp->next = NULL;
   return temp;
}

/* Function to remove duplicates from a
   unsorted linked list */
void removeDuplicates(struct Node *start)
{
    struct Node *ptr1, *ptr2, *dup;
    ptr1 = start;

    /* Pick elements one by one */
    while (ptr1 != NULL && ptr1->next != NULL)
    {
        ptr2 = ptr1;

        /* Compare the picked element with rest
           of the elements */
        while (ptr2->next != NULL)
        {
            /* If duplicate then delete it */
            if (ptr1->data == ptr2->next->data)
            {
                /* sequence of steps is important here */
                dup = ptr2->next;
                ptr2->next = ptr2->next->next;
                delete(dup);
            }
            else /* This is tricky */
                ptr2 = ptr2->next;
        }
        ptr1 = ptr1->next;
    }
}

/* Function to print nodes in a given linked list */
void printList(struct Node *node)
{
    while (node != NULL)
    {
        printf("%d ", node->data);
        node = node->next;
    }
}

/* Druver program to test above function */
int main()
{
    /* The constructed linked list is:
     10->12->11->11->12->11->10*/
    struct Node *start = newNode(10);
    start->next = newNode(12);
    start->next->next = newNode(11);
    start->next->next->next = newNode(11);
    start->next->next->next->next = newNode(12);
    start->next->next->next->next->next =
                                    newNode(11);
    start->next->next->next->next->next->next =
                                    newNode(10);

    printf("Linked list before removing duplicates ");
    printList(start);

    removeDuplicates(start);

    printf("\nLinked list after removing duplicates ");
    printList(start);

    return 0;
}

参考:geeksforgeeks

答案 1 :(得分:0)

这种问题在各种变体中被提出很多。通常当一个人实现一个链表时,最终会出现一个需要保持方式指向各种元素的指针的点。从表面上看,似乎单个重定向更容易使用,而实际上它并没有传达足够的关于列表的信息来在本地执行操作。

这是重写(但未经过全面测试)的功能,以利用“链接”抽象(基本上是struct node**):

void removeDuplicates(struct node** head) {
  if(!head)
    return;

  struct node **link = head;

  // We will iterate over the links. I.e. the `next` pointers in the list.
  while(*link) {
    struct node **rest = &((*link)->next);
    while(*rest) {
      if ((*link)->data != (*rest)->data) {
        rest = &((*rest)->next); // move to the next link
      } else {
        // modify the current link of rest to look one past the next
        struct node *to_remove = *rest;
        *rest = to_remove->next;
        free(to_remove);
      }
    }
    link = &((*link)->next); // again, move the the next link
  }
}

通过使用另一个间接层,可以保证我们用于遍历列表的迭代器在任何时候都不会失效。没有办法(除非写错误)上面的循环可以使*link无效,因此无需在作业link = &((*link)->next);之前检查

答案 2 :(得分:0)

void removeDuplicates(struct node **head){
struct node *tmp;

while (*head) {
        /* Look below *head, to see if it has any duplicates */
        for (tmp= (*head)->next; tmp; tmp = tmp->next) {
                if (tmp->data == (*head)->data) break;
                }
                /* Found no duplicate: advance head */
        if(!tmp) { head = &(*head)->next; continue;}
                /* Duplicate found :: delete *head */
        tmp = (*head)->next;
        free(*head);
        *head = tmp;
        }
return;
}

现在,查看角落案例:

  • 如果* head为NULL,则永远不会执行外部循环:没有任何反应
  • 如果(* head) - &gt; next为NULL,则内部循环永远不会执行,因此内循环后tmp仍为NULL
  • 如果找到重复项*将head替换为 - > gt;下一个指针(可能为NULL)
  • 如果没有找到重复,* head只是前进到它的 - &gt;下一个指针(可能是NULL)

答案 3 :(得分:0)

#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *createNode(int data)
 {
struct node *n;
n = (struct node *)malloc(sizeof(struct node));
n->data = data;
n->next = NULL;
return n;
}
void traverseList(struct node *head)
{
while (head != NULL)
{
    printf("%d ", head->data);
    head = head->next;
}
}
 void removeDuplicates(struct node *head)
 {
struct node *ptr = head;
while (ptr != NULL)
{
    struct node *runner = ptr;
    while (runner->next != NULL)
    {
        if (ptr->data == runner->next->data)
        {
            runner->next = runner->next->next;
        }
        else
        {
            runner = runner->next;
        }
    }
    free(runner->next);
     ptr = ptr->next;
 }
 }
 int main()
 {
struct node *node1, *node2, *node3, *node4;
node1 = createNode(2);
node2 = createNode(5);
node3 = createNode(5);
node4 = createNode(1);
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
traverseList(node1);
removeDuplicates(node1);
printf("\n");
traverseList(node1);

return 0;
}

答案 4 :(得分:-3)

特别感谢@StoryTeller - 我还没有验证你的解决方案,但你对有太多指针的评论绝对是关键。我已经重新编写了我的函数,它现在适用于4种不同的特殊情况(列表末尾的复制品,列表的开头,整个列表中随机的列表,纯粹包含重复的列表)。这是正确的代码:

void removeDuplicates(struct node** head){
    struct node* currentNode = *head;
    struct node* runningNode = (*head)->next;
    struct node* runningNodePrev = *head;
    int count = -1;
    while(currentNode->next != NULL){
        count++;
        if(count){
            currentNode = currentNode->next;
            runningNodePrev = currentNode;
            runningNode = currentNode->next;
        }
        while(runningNode != NULL){
            if(runningNode->data == currentNode->data){
                runningNodePrev->next = runningNode->next;
                free(runningNode);
                runningNode = runningNodePrev->next;
            }else{
                runningNodePrev = runningNodePrev->next;
                runningNode = runningNode->next;
            }
        }
    }
}

干杯谢谢所有评论的人。