C 为什么它总是只返回单个链接列表的一个节点?

C 为什么它总是只返回单个链接列表的一个节点?,c,C,我有一个结构 typedef struct song{ string title; string artist; string album; string genre; float rating; struct song *next; }song_t; 和删除功能 song_t *DeleteSong(song_t *head, string key){ song_t *temp, *temp2, *holder;

我有一个结构

    typedef struct song{
    string title;
    string artist;
    string album;
    string genre;
    float rating;
    struct song *next;
    }song_t;
和删除功能

    song_t *DeleteSong(song_t *head, string key){
    song_t *temp, *temp2, *holder;
    holder = (song_t *)malloc(sizeof(song_t));  
    temp = head;


    while(temp != NULL && strcasecmp(temp->title, key) != 0){
        temp = temp->next;
    }
    if(temp == NULL){
        newline;
        printf("Song not found.");
        newline;
        newline;
    }

    else if(temp == head){
        if(temp->next == NULL){
            temp->next = NULL;
            free(temp);
            return NULL;
        }
        else{
        head = temp->next;
        }
    }

    else if(temp->next == NULL){
        temp2 = head;

        while(temp2->next->next != NULL){
            temp2 = temp2->next;
        }
        holder = temp2->next->next;
        temp2->next = NULL;
    }

    else{
        temp2 = head;
        while(temp2->next != temp){
            temp2 = temp2->next;
        }
        holder = temp;
        temp2->next = temp->next;
        free(holder);
    }
    return head;
    }
song_t *RemoveDuplicate(song_t *head){
    song_t *temp;
    song_t *temp2;
    temp = head;
    temp2 = temp->next;

    while(temp != NULL){
        while(temp2 != NULL){
            if(strcasecmp(temp->title,temp2->title) == 0 && strcasecmp(temp->artist,temp2->artist) == 0 && strcasecmp(temp->album,temp2->album) == 0 && strcasecmp(temp->genre,temp2->genre) == 0){
                if(temp2 == temp->next){
                    temp = DeleteSong(temp,temp2->title);
                }
                else{
                    temp2 = DeleteSong(temp2->next,temp2->title);
                }
            }
            temp2 = temp2->next;
        }
        temp = temp->next;
    }

    }
最后是一个删除重复函数

    song_t *DeleteSong(song_t *head, string key){
    song_t *temp, *temp2, *holder;
    holder = (song_t *)malloc(sizeof(song_t));  
    temp = head;


    while(temp != NULL && strcasecmp(temp->title, key) != 0){
        temp = temp->next;
    }
    if(temp == NULL){
        newline;
        printf("Song not found.");
        newline;
        newline;
    }

    else if(temp == head){
        if(temp->next == NULL){
            temp->next = NULL;
            free(temp);
            return NULL;
        }
        else{
        head = temp->next;
        }
    }

    else if(temp->next == NULL){
        temp2 = head;

        while(temp2->next->next != NULL){
            temp2 = temp2->next;
        }
        holder = temp2->next->next;
        temp2->next = NULL;
    }

    else{
        temp2 = head;
        while(temp2->next != temp){
            temp2 = temp2->next;
        }
        holder = temp;
        temp2->next = temp->next;
        free(holder);
    }
    return head;
    }
song_t *RemoveDuplicate(song_t *head){
    song_t *temp;
    song_t *temp2;
    temp = head;
    temp2 = temp->next;

    while(temp != NULL){
        while(temp2 != NULL){
            if(strcasecmp(temp->title,temp2->title) == 0 && strcasecmp(temp->artist,temp2->artist) == 0 && strcasecmp(temp->album,temp2->album) == 0 && strcasecmp(temp->genre,temp2->genre) == 0){
                if(temp2 == temp->next){
                    temp = DeleteSong(temp,temp2->title);
                }
                else{
                    temp2 = DeleteSong(temp2->next,temp2->title);
                }
            }
            temp2 = temp2->next;
        }
        temp = temp->next;
    }

    }
但是,每当我在main中包含remove duplicate函数时, 头部=拆下的副本(头部)


结果总是只返回一个结构并删除整个列表。我认为RemovedUpplicate函数有问题,因为我测试了DeleteSong函数,它工作得很好。

如果列表中的前两个条目是重复的,那么第一次检查时似乎:

if(temp2 == temp->next){
    temp = DeleteSong(temp,temp2->title);
}
它将在列表的最前面评估为true。如果您的列表突然减少到列表的最前面,您可能需要检查接下来会发生什么

我怀疑这个错误实际上可能是在DeleteSong函数中发现的,因为它看起来比我所见过的其他单链接列表要复杂一些

在pseudo-C中,我可能会尝试以下操作:

to_delete = find_node_by_key( key )
return if to_delete == null

current = head
last    = null

if to_delete == current
{
    head = current->next
    to_mem_free = current
}
else do
{
    if to_delete == current
    {
        to_mem_free = current
        if ( last != null ) last->next = current->next
        break
    }

    last = current

} while (current = current->next) != null
可以将对要删除的节点的搜索与实际删除结合起来,这样就不必遍历列表两次。您可能还希望尽量避免使用“temp”作为变量名,除非绝对必要,因为它通常会导致混淆

许多工具链包括一个提供单链接列表的库。通过使用这样的库,您可以节省数小时的编码和调试时间,该库还可能提供排序树或哈希列表,从而显著加快搜索,例如歌曲标题

holder = (song_t *)malloc(sizeof(song_t));
/* snip */
/* else if */
    holder = temp2->next->next;
/* else */
    holder = temp;
    temp2->next = temp->next;
    free(holder);
分配
holder=temp2->next->next时
(顺便说一下,这里总是
NULL
),或者
holder=temp,您正在丢失对
malloc
ed内存的引用。由于您根本没有真正使用
holder
,因此修复方法是将其从函数中删除。在第一种情况下,除了分配一个复杂的
NULL
,您没有以任何方式使用它,在第二种情况下,删除
holder=temp
免费
ing
temp
是正确的方法

还有一些奇怪的事情和错误:

song_t *DeleteSong(song_t *head, string key){
    song_t *temp, *temp2, *holder;
    holder = (song_t *)malloc(sizeof(song_t));  // as said, remove holder completely
    temp = head;

    /* Find song with given title */
    while(temp != NULL && strcasecmp(temp->title, key) != 0){
        temp = temp->next;
    }
    if(temp == NULL){
        newline;
        printf("Song not found.");
        newline;
        newline;
    }
    /* It's the very first in the list */
    else if(temp == head){
        if(temp->next == NULL){
            /* It's even the only one */
            temp->next = NULL;    // This runs only if temp->next is already NULL
            free(temp);           // Also free the members of temp, or you're leaking
            return NULL;
        }
        else{
            head = temp->next;    // You should now free temp and members, or you're leaking memory
        }
    }
    /* It's the last one in the list, but not the first */
    else if(temp->next == NULL){
        temp2 = head;
        /* Find the penultimate song */
        while(temp2->next->next != NULL){
            temp2 = temp2->next;
        }
        holder = temp2->next->next;
        temp2->next = NULL;    // You should now free temp and members, or you're leaking memory
    }
    /* Neither first nor last */
    else{
        temp2 = head;
        /* Find song before */
        while(temp2->next != temp){
            temp2 = temp2->next;
        }
        holder = temp;
        temp2->next = temp->next;
        free(holder);
    }
    return head;
}
但除了泄漏,这是正确的,尽管不必要地复杂

song_t *DeleteSong(song_t *head, string key) {
    song_t *prev = NULL, curr = head;
    /* Find song and node before that */
    while(curr != NULL && strcasecmp(curr->title, key) != 0) {
        prev = curr;
        curr = curr->next;
    }
    if (curr == NULL) {
        /* Not found */
        newline;
        printf("Song not found.");
        newline;
        newline;
    } else if (prev == NULL) {
        /* It's the very first song in the list
         * so let head point to its successor
         * and free the song; it doesn't matter
         * if it's the last in the list
         */
        head = curr->next;
        free(curr->title);   // Probably, but not if title isn't malloced
        free(curr->artist);  // Ditto
        free(curr->album);
        free(curr->genre);
        free(curr);
    } else {
        /* We have a predecessor, let that point
         * to the successor and free the song
         */
        prev->next = curr->next;
        free(curr->title); // See above
        free(curr->artist);
        free(curr->album);
        free(curr->genre);
        free(curr);
    }
    return head;
}
另一方面,您的
RemovedUpplicate
功能并没有达到您的目的。除了副本紧跟原始版本和不紧跟原始版本之间的区别(我看不出有什么原因),赋值
temp=DeleteSong(temp,temp2->title)响应
temp2=DeleteSong(temp2->next,temp2->title)更改什么
temp
resp
temp2
指向,但不指向列表中相应的前置项所指向的位置。让我们用一点ASCII艺术来说明这个问题:

       temp    temp2
         |      |
         v      v
song1->song2->song3->song4->song5->...
其中
song2
song3
是重复的。现在在
temp=DeleteSong(temp,temp2->title)中
,因为这两首歌是重复的,
DeleteSong
head
参数已经匹配,并且在您的
DeleteSong
版本中,
head=temp->next;回流头是所有要做的事情,所以列表根本没有改变,您只需要

            temp temp2
              |   |
              v   v
song1->song2->song3->song4->...
指向同一列表节点的两个指针。在释放节点的
DeleteSong
版本中,
song1。下一个
现在将指向
释放的
d内存,哎哟

如果副本没有立即跟随原始版本,
temp2=DeleteSong(temp2->next,temp2->title)
可能找不到具有匹配标题的歌曲,因为搜索是在已知匹配之后开始的,在这种情况下,列表根本不会修改,
temp2
只是更改为指向后续歌曲。如果之后有一首歌曲具有匹配的标题,找到的副本仍不会从列表中删除,之后的部分可能会更改,可能导致不健全状态。如果
temp2
指向列表中倒数第二个节点,并且最后一个节点具有匹配的标题,则最后一个节点是
free
d,但复制的
next
指针没有更改,因此现在它指向
free
d内存,您有一个悬空指针(这是一个等待发生的segfault)

另一个问题是
DeleteSong
RemoveDuplicate
中的删除标准不同,前者只检查标题,后者还检查艺术家、专辑和流派,因此在后者中使用前一个功能可能会删除不应删除的歌曲(考虑封面版本)

如果要从单链表中删除节点,则需要一个指向前一个节点的指针,以便能够更改该节点指向的内容,否则将创建悬空指针。我在上面给出了一种标准方法,您基本上可以将其复制到内部循环中,但在这里,我们永远不可能删除第一个节点,因此更简单一些:

// void, since head is never changed, only duplicates after the original are removed
void RemoveDuplicates(song_t *head) {
    song_t *orig = head, prev, curr;
    while(orig != NULL && orig->next != NULL) {
        prev = orig;
        curr = orig->next;
        while(curr != NULL) {
            // Find next song to remove
            while(curr != NULL && !meets_deletion_criteria(orig, curr)) {
                prev = curr;
                curr = curr->next;
            }
            // Now either curr is NULL, or it shall be deleted
            if (curr != NULL) {
                // Let the predecessor point to curr's successor
                prev->next = curr->next;
                clean_up(curr); // free all malloced members and the node itself
                curr = prev->next;
            }
        }
        orig = orig->next;
    }
}

在else语句中,我不明白为什么要将temp2->next作为头指针传递。在
temp=temp->next
(在内部while之后)之后,您不应该放置
temp2=temp->next