2016-04-30 101 views
-2

我目前正試圖從雙向鏈表中刪除一個節點,但是當只剩下一個項目時,它會在嘗試在第11行(*sPtr)->prevPtr = NULL;上刪除它時引發訪問衝突異常。這是我目前的刪除功能:如何從雙向鏈表中刪除節點

char del(ListNodePtr *sPtr, char value) 
{ 
    ListNodePtr previousPtr; /* pointer to previous node in list */ 
    ListNodePtr currentPtr; /* pointer to current node in list */ 
    ListNodePtr tempPtr; /* temporary node pointer */ 

    /* delete first node */ 
    if (value == (*sPtr)->data) { 
     tempPtr = *sPtr; /* hold onto node being removed */ 
     *sPtr = (*sPtr)->nextPtr; /* de-thread the node */ 
     (*sPtr)->prevPtr = NULL; 
     if ((*sPtr)->nextPtr != NULL) { 
      free(tempPtr); /* free the de-threaded node */ 
     } 
     return value; 
    } /* end if */ 
    else { 
     previousPtr = *sPtr; 
     currentPtr = (*sPtr)->nextPtr; 

     /* loop to find the correct location in the list */ 
     while (currentPtr != NULL && currentPtr->data != value) { 
      previousPtr = currentPtr; /* walk to ... */ 
      currentPtr = currentPtr->nextPtr; /* ... next node */ 
     } /* end while */ 

      /* delete node at currentPtr */ 
     if (currentPtr != NULL) { 
      tempPtr = currentPtr; 
      previousPtr->nextPtr = currentPtr->nextPtr; 
      free(tempPtr); 
      return value; 
     } /* end if */ 
    } /* end else */ 

    return '\0'; 
} 

編輯:我要補充我的主要功能和我的打印功能在低於爲了什麼,我試圖這樣做,我的問題都可以重新打開更好的背景:

這裏是我與我的listNode結構主要功能:

struct listNode { 
    char data; /* each listNode contains a character */ 
    struct listNode *nextPtr; /* pointer to next node*/ 
    struct listNode *prevPtr; /* pointer to previous node*/ 
}; /* end structure listNode */ 

typedef struct listNode ListNode; /* synonym for struct listNode */ 
typedef ListNode *ListNodePtr; /* synonym for ListNode* */ 

     /* prototypes */ 
void insert(ListNodePtr *sPtr, char value); 
char del(ListNodePtr *sPtr, char value); 
int isEmpty(ListNodePtr sPtr); 
void printList(ListNodePtr currentPtr); 
void printReverse(ListNodePtr currentPtr); 
void instructions(void); 

int main(void) 
{ 
    ListNodePtr startPtr = NULL; /* initially there are no nodes */ 
    int choice; /* user's choice */ 
    char item; /* char entered by user */ 

    instructions(); /* display the menu */ 
    printf("? "); 
    scanf("%d", &choice); 

    /* loop while user does not choose 3 */ 
    while (choice != 3) { 

     switch (choice) { 
     case 1: 
      printf("Enter a character: "); 
      scanf("\n%c", &item); 
      insert(&startPtr, item); /* insert item in list */ 
      printList(startPtr); 
      printReverse(startPtr); 
      break; 
     case 2: 
      /* if list is not empty */ 
      if (!isEmpty(startPtr)) { 
       printf("Enter character to be deleted: "); 
       scanf("\n%c", &item); 

       /* if character is found, remove it */ 
       if (del(&startPtr, item)) { /* remove item */ 
        printf("%c deleted.\n", item); 
        printList(startPtr); 
        printReverse(startPtr); 
       } /* end if */ 
       else { 
        printf("%c not found.\n\n", item); 
       } /* end else */ 
      } /* end if */ 
      else { 
       printf("List is empty.\n\n"); 
      } /* end else */ 

      break; 
     default: 
      printf("Invalid choice.\n\n"); 
      instructions(); 
      break; 
     } /* end switch */ 

     printf("? "); 
     scanf("%d", &choice); 
    } /* end while */ 

    printf("End of run.\n"); 
    return 0; /* indicates successful termination */ 
} /* end main */ 

,這裏是我的printReverse和的printList功能:

void printList(ListNodePtr currentPtr) 
{ 

    /* if list is empty */ 
    if (currentPtr == NULL) { 
     printf("List is empty.\n\n"); 
    } /* end if */ 
    else { 
     printf("The list is:\n"); 

     /* while not the end of the list */ 
     while (currentPtr != NULL) { 
      printf("%c --> ", currentPtr->data); 
      currentPtr = currentPtr->nextPtr; 
     } /* end while */ 

     printf("NULL\n\n"); 
    } /* end else */ 
} /* end function printList */ 

void printReverse(ListNodePtr currentPtr) 
{ 
    /* if list is empty */ 
    if (currentPtr == NULL) { 
     printf("List is empty.\n\n"); 
    } /* end if */ 
    else { 
     printf("The list in reverse is:\n"); 

     while (currentPtr->nextPtr != NULL) 
      currentPtr = currentPtr->nextPtr; 

     /* while not the beginning of the list */ 
     while (currentPtr != NULL) { 
      printf("%c --> ", currentPtr->data); 
      currentPtr = currentPtr->prevPtr; 
     } /* end while */ 

     printf("NULL\n\n"); 
    } /* end else */ 
} /* end function printList */ 

我真的希望這樣可以更清楚地瞭解發生的一切事情,因爲過去3天我一直在這個問題上停滯不前,而且我在網上可以找到很少或沒有的主題,談論如何去做我正在做的事情,因爲該列表在插入和刪除時按字母順序排序。

因此,如果任何人可以嘗試告訴我什麼是錯誤的,以及爲什麼它會在嘗試刪除列表中的最後一項時在第11行拋出訪問衝突異常,我將永遠如此感激。謝謝!

+1

未選中,很可能是因爲'(*特徵碼) - > nextPtr',這是assinged到'* sPtr',是'NULL'因爲只有一個節點離開了。 – MikeCAT

+0

@MikeCAT我該在哪裏解決這個問題? –

+0

如果你沒有頭(單鏈表)和尾(都是雙鏈表)指針,我會強烈推薦它們。我不知道沒有他們的人如何鏈接列表。當列表中有0個或1個元素時,插入和刪除節點是一種特殊情況,當維護頭部和尾部指針時,我認爲這更容易。雖然說過,我確信這裏有人可以看中並在沒有他們的情況下做鏈表。 – yano

回答

0

我認爲你已經使事情變得過於複雜。你不會將「list」類型與「node」類型分開,但是我認爲如果你只是返回一個替換,你可以不傳遞指針指針。

您可能希望編寫一個「find」方法來處理查找字符並返回指向該節點的指針。

/** 
    Delete the node containing value from the list starting with start. 
    If value is not found in list, then the list is unchanged. Returns 
    a replacement value for start, which may be needed if the value is 
    contain in the start node. 
*/ 

ListNodePtr del(ListNodePtr start, char value) 
{ 
    ListNodePtr curr; 

    for (curr = start; curr && curr->data != value; curr = curr->nextPtr) { 
     // skip this node 
    } 

    if (!curr) { 
     // Value not found in list. List is unchanged. 
     return start; 
    } 

    /* Compute return value */ 

    if (curr == start) { 
     start = start->nextPtr; 
    } 

    /* Remove curr node from chain */ 

    if (curr->prevPtr) { 
     curr->prevPtr->nextPtr = curr->nextPtr; 
    } 

    if (curr->nextPtr) { 
     curr->nextPtr->prevPtr = curr->prevPtr; 
    } 

    free(curr); 
    return start; 
} 
0

我最終意識到自己的問題。 Visual Studio不讓我使用斷點,但那是因爲我沒有意識到我已將它設置爲「釋放」而不是「調試」。這樣做,我跟蹤的指針來找出他們成爲關聯或鏈接到那些錯誤的,並用此溶液想出了:

/* Delete a list element */ 
char del(ListNodePtr *sPtr, char value) 
{ 
    ListNodePtr previousPtr; /* pointer to previous node in list */ 
    ListNodePtr currentPtr; /* pointer to current node in list */ 
    ListNodePtr tempPtr; /* temporary node pointer */ 

    /* delete first node */ 
    if (value == (*sPtr)->data) { 
      tempPtr = *sPtr; /* hold onto node being removed */ 
      *sPtr = (*sPtr)->nextPtr; /* de-thread the node */ 
      if(*sPtr != NULL) /* if the list is not empty */ 
       (*sPtr)->prevPtr = NULL; /* the previos pointer is null*/ 
      free(tempPtr); /* free the de-threaded node */ 
      return value; 
    } /* end if */ 
    else { 
     previousPtr = *sPtr; 
     currentPtr = (*sPtr)->nextPtr; 

     /* loop to find the correct location in the list */ 
     while (currentPtr != NULL && currentPtr->data != value) { 
      previousPtr = currentPtr; /* walk to ... */ 
      currentPtr = currentPtr->nextPtr; /* ... next node */ 
     } /* end while */ 

      /* delete node at currentPtr */ 

     if (currentPtr != NULL) { 
      tempPtr = currentPtr; 
      previousPtr->nextPtr = currentPtr->nextPtr; 
      if(previousPtr->nextPtr != NULL) /* if the next pointer isn't null */ 
       previousPtr->nextPtr->prevPtr = currentPtr->prevPtr; /* the previous pointer of the next pointer is the previous pointer of the current pointer */ 
      free(tempPtr); 
      return value; 
     } /* end if */ 

    } /* end else */ 
    return '\0'; 
} /* end function delete */ 
1

您的代碼不檢查刪除最後後的新頭節點是否爲空節點,因此當您嘗試將頭節點的下一個指針設置爲空時,代碼會崩潰。

固定碼(運行無泄漏和無差錯的valgrind下):

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

struct listNode 
{ 
    char data; 
    struct listNode *nextPtr; 
    struct listNode *prevPtr; 
}; 

typedef struct listNode ListNode; 
typedef ListNode *ListNodePtr; 

void insert(ListNodePtr *sPtr, char value); 
char del(ListNodePtr *sPtr, char value); 
int isEmpty(ListNodePtr sPtr); 
void printList(ListNodePtr currentPtr); 
void printReverse(ListNodePtr currentPtr); 

static void ins_check(ListNode **list, char c) 
{ 
    printf("Inserting [%c] (%p)\n", c, (void *)*list); 
    insert(list, c); 
    printList(*list); 
    printReverse(*list); 
} 

static void del_check(ListNode **list, char c) 
{ 
    printf("Deleting [%c] (%p)\n", c, (void *)*list); 
    if (del(list, c) != c) 
     printf("Did not find [%c] in list.\n", c); 
    printList(*list); 
    printReverse(*list); 
} 

int main(void) 
{ 
    ListNodePtr startPtr = NULL; 

    printList(startPtr); 
    printReverse(startPtr); 

    ins_check(&startPtr, 'a'); 
    ins_check(&startPtr, 'b'); 
    ins_check(&startPtr, 'c'); 

    del_check(&startPtr, 'c'); 
    del_check(&startPtr, 'a'); 
    del_check(&startPtr, 'b'); 

    assert(startPtr == 0); 

    printf("End of run.\n"); 
    return 0; 
} 

void printList(ListNodePtr currentPtr) 
{ 
    if (currentPtr == NULL) 
     printf("List is empty.\n"); 
    else 
    { 
     printf("The list is: "); 
     while (currentPtr != NULL) 
     { 
      printf("%c --> ", currentPtr->data); 
      currentPtr = currentPtr->nextPtr; 
     } 
     printf("NULL\n"); 
    } 
} 

void printReverse(ListNodePtr currentPtr) 
{ 
    if (currentPtr == NULL) 
     printf("List is empty (even in reverse).\n"); 
    else 
    { 
     printf("The list in reverse is: "); 
     while (currentPtr->nextPtr != NULL) 
      currentPtr = currentPtr->nextPtr; 
     while (currentPtr != NULL) 
     { 
      printf("%c --> ", currentPtr->data); 
      currentPtr = currentPtr->prevPtr; 
     } 
     printf("NULL\n"); 
    } 
} 

char del(ListNodePtr *sPtr, char value) 
{ 
    ListNodePtr previousPtr; 
    ListNodePtr currentPtr; 
    ListNodePtr tempPtr; 

    assert(*sPtr != 0); 
    if (value == (*sPtr)->data) 
    { 
     tempPtr = *sPtr; 
     printf("Deleting 1: [%c] (node = %p, next = %p, prev = %p\n", tempPtr->data, 
       (void *)tempPtr, (void *)tempPtr->nextPtr, (void *)tempPtr->prevPtr); 
     *sPtr = (*sPtr)->nextPtr; 
     if (*sPtr != 0) // Crucial change! 
      (*sPtr)->prevPtr = NULL; 
     free(tempPtr); 
     return value; 
    } 
    else 
    { 
     previousPtr = *sPtr; 
     currentPtr = (*sPtr)->nextPtr; 

     while (currentPtr != NULL && currentPtr->data != value) 
     { 
      previousPtr = currentPtr; 
      currentPtr = currentPtr->nextPtr; 
     } 

     if (currentPtr != NULL) 
     { 
      assert(previousPtr != 0); 
      tempPtr = currentPtr; 
      printf("Deleting 2: [%c] (node = %p, next = %p, prev = %p\n", tempPtr->data, 
        (void *)tempPtr, (void *)tempPtr->nextPtr, (void *)tempPtr->prevPtr); 
      previousPtr->nextPtr = currentPtr->nextPtr; 
      free(tempPtr); 
      return value; 
     } 
     else 
      printf("Did not find [%c]\n", value); 
    } 

    return '\0'; 
} 

void insert(ListNode **list, char value) 
{ 
    ListNode *node = malloc(sizeof(*node)); 
    if (node == 0) 
    { 
     fprintf(stderr, "Out of memory\n"); 
     exit(EXIT_FAILURE); 
    } 
    node->data = value; 
    node->nextPtr = 0; 
    node->prevPtr = 0; 
    if (*list != 0) 
    { 
     node->nextPtr = *list; 
     assert((*list)->prevPtr == 0); 
     (*list)->prevPtr = node; 
    } 
    *list = node; 
} 

實施例運行:

List is empty. 
List is empty (even in reverse). 
Inserting [a] (0x0) 
The list is: a --> NULL 
The list in reverse is: a --> NULL 
Inserting [b] (0x7fccc3503070) 
The list is: b --> a --> NULL 
The list in reverse is: a --> b --> NULL 
Inserting [c] (0x7fccc3503090) 
The list is: c --> b --> a --> NULL 
The list in reverse is: a --> b --> c --> NULL 
Deleting [c] (0x7fccc35030b0) 
Deleting 1: [c] (node = 0x7fccc35030b0, next = 0x7fccc3503090, prev = 0x0 
The list is: b --> a --> NULL 
The list in reverse is: a --> b --> NULL 
Deleting [a] (0x7fccc3503090) 
Deleting 2: [a] (node = 0x7fccc3503070, next = 0x0, prev = 0x7fccc3503090 
The list is: b --> NULL 
The list in reverse is: b --> NULL 
Deleting [b] (0x7fccc3503090) 
Deleting 1: [b] (node = 0x7fccc3503090, next = 0x0, prev = 0x0 
List is empty. 
List is empty (even in reverse). 
End of run. 

使用注意事項的包裝函數(ins_check()del_check())和使用的固定數據,以便於測試(無需輸入)。還要注意打印正在發生的事情。

我希望你insert()是有點類似一個我設計 - 一個真正的MCVE(How to create a Minimal, Complete, and Verifiable Example?)或SSCCE(Short, Self-Contained, Correct Example)會提供該功能。

請注意,'新'代碼服從Is it a good idea to typedef pointers建議的限制 - 簡潔的答案是「否」(對於非不透明的數據指針)。

請注意,您的刪除函數與單鏈表一樣需要複雜,但可以更簡單一些,因爲雙鏈表中的每個節點都知道它自己的前任。這個版本也適用乾淨:

char del(ListNodePtr *sPtr, char value) 
{ 
    assert(*sPtr != 0); 
    ListNode *curr = *sPtr; 
    while (curr != NULL) 
    { 
     if (curr->data == value) 
     { 
      if (curr->prevPtr != NULL) 
       curr->prevPtr->nextPtr = curr->nextPtr; 
      if (curr->nextPtr != NULL) 
       curr->nextPtr->prevPtr = curr->prevPtr; 
      if (*sPtr == curr) 
       *sPtr = curr->nextPtr; 
      free(curr); 
      return value; 
     } 
     curr = curr->nextPtr; 
    } 

    printf("Did not find [%c]\n", value); 
    return '\0'; 
}