2017-03-22 118 views
1

我一直在處理這個問題的時間超過了我現在要承認的時間,並且這讓我發瘋了!給定一個簡單的鏈表,比如只存儲一個整數(data),除了指向下一個節點(next)的指針之外,我想要一個算法去除沒有排序或依賴輔助函數的重複項。C:刪除未排序鏈接列表中的重複項

以前的問題已經被問到了Java中未被排序的鏈接列表,它們利用了Java提供的幫助函數。這與C沒有使用輔助函數是嚴格相關的。

我對代碼進行了修改,並且已經將它用於某些情況,但不是全部。下面是一個完整的,可覈查的例子 - 我已經包括push()函數來創建一個鏈表,並與測試用例main(),但我的問題是關於在removeDuplicates()單獨的邏輯:

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

struct node { 
    int data; 
    struct node *next; 
}; 

void push(struct node **headRef, int data) { 
    struct node *newNode = malloc(sizeof(struct node)); 
    newNode->data = data; 
    newNode->next = *headRef; 
    *headRef = newNode; 
} 

void removeDuplicates(struct node **head) { 
     struct node *currentNode = *head; //The node we compare other nodes against 
    struct node *runningNode = (*head)->next; //The node we are comparing to 
    struct node *runningNodePrev = *head; //The node before the node we are comparing to 
    struct node *runningNodeNext = (*head)->next->next; // The node after the node we are comparing to 
    int count = -1; 
    while (currentNode->next != NULL) { //Ensure we are not looking at the last node -- cannot have comparisons past this node 
     count++; 
     if (count) { 
      //'Base Position' 
      currentNode = currentNode->next; 
      runningNodePrev = currentNode; 
      runningNode = currentNode->next; 
      runningNodeNext = runningNode->next; 
     } 
     printf("\nChecking for a match with %d ... \n", currentNode->data); 
     while (runningNode != NULL) { //Compare each node in the list until we reach the end of the list 
      printf("Inspecting next adjacent node ... \n"); 
      if (runningNode->data == currentNode->data) {//If a duplicate is found 
       printf("Found match ... "); 

       //Ensure link is maintained before removing duplicate node 
       if (currentNode == runningNodePrev) 
        currentNode->next = runningNodeNext; 
       runningNodePrev->next = runningNodeNext; 

       free(runningNode);//Delete duplicate node 
       printf("Deleted duplicate.\n"); 
      } 
      runningNode = runningNodeNext;//Continue searching for duplicates at the first unchecked node 
      runningNodeNext = runningNodeNext->next;//Move running node leader up the list.  
     } 
    } 
} 

int main(void) { 
    struct node *t1 = NULL; 
    struct node *t2 = NULL; 
    struct node *t4 = NULL; 
    struct node *t5 = NULL; 
    push(&t1, 1); 
    push(&t1, 1); 
    push(&t1, 1); 
    push(&t1, 1); 
    push(&t2, 6); 
    push(&t2, 1); 
    push(&t2, 2); 
    push(&t2, 3); 
    push(&t2, 4); 
    push(&t2, 5); 
    push(&t2, 6); 
    push(&t4, 4); 
    push(&t4, 2); 
    push(&t4, 3); 
    push(&t4, 2); 
    push(&t4, 1); 
    push(&t5, 0); 
    push(&t5, 0); 
    push(&t5, 1); 
    printf("Testing removeDuplicates()...\n"); 
    printf("Case 1: Calling removeDuplicates({1,0,0}).\n"); 
    printf("Expected result: { 1 0 }\n"); 
    printf("Actual result: { "); 
    removeDuplicates(&t5); 
    ptr = t5; 
    while (ptr != NULL) { 
     printf("%d ", ptr->data); 
     ptr = ptr->next; 
    } 
    printf("}\n"); 
    printf("Case 2: Calling removeDuplicates({1,2,3,2,4}).\n"); 
    printf("Expected result: { 1 2 3 4 }\n"); 
    printf("Actual result: { "); 
    removeDuplicates(&t4); 
    ptr = t4; 
    while (ptr != NULL) { 
     printf("%d ", ptr->data); 
     ptr = ptr->next; 
    } 
    printf("}\n"); 
    printf("Case 3: Calling removeDuplicates({6,5,4,3,2,1,6}).\n"); 
    printf("Expected result: { 6 5 4 3 2 1 }\n"); 
    printf("Actual result: { "); 
    removeDuplicates(&t2); 
    ptr = t2; 
    while (ptr != NULL) { 
     printf("%d ", ptr->data); 
     ptr = ptr->next; 
    } 
    printf("}\n"); 
    printf("Case 4: Calling removeDuplicates({1,1,1,1}).\n"); 
    printf("Expected result: { 1 }\n"); 
    printf("Actual result: { "); 
    removeDuplicates(&t1); 
    ptr = t1; 
    while (ptr != NULL) { 
     printf("%d ", ptr->data); 
     ptr = ptr->next; 
    } 
    printf("}\n"); 
} 

我還包括描述邏輯的圖片,如果它不清楚我在做什麼:http://imgur.com/DbnBOq2

+1

有什麼不對輔助功能? –

+1

爲什麼不修改'push'以*不插入*重複? – StoryTeller

+0

你可以使用散列表檢查重複項......然後你不需要檢查整個列表 – Thomas

回答

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);

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; 
} 

現在(在寫它禁止的錯誤),檢查角落的情況:

  • 如果*頭爲NULL,則永遠不會執行外循環:沒有任何反應
  • 如果(*頭) - >接下來是NULL,內循環永遠不會執行,所以內環路tmp之後仍是空
  • 如果找到一個副本*頭被替換爲它的 - >下一個指針(可能爲NULL)
  • 如果沒有找到重複,* head只是前進到它的 - > next指針(可能是NULL)
-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; 
      } 
     } 
    } 
} 

歡呼聲,並感謝所有誰評論。

+0

我很好奇,並通過您的測試案例運行我的功能。你應該知道在你發佈的代碼中,儘管期望它在那裏,但是不要將1推入't2'。 – StoryTeller

+0

謝謝,@StoryTeller。我注意到這是在測試我的代碼時。我已經用t2修復了push()調用,以準確描述相關的測試用例。 – Matt

+0

'struct node * runningNode =(* head) - > next;':: \ * head在這裏可以是NULL;解引用會引入NULL指針。順便說一句:如果你正確地定義了東西,沒有*特殊情況* – wildplasser

0
/* 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