0

私には構造があり、

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

    }

ただし、メインに重複削除機能を含めるたびに、 head = RemoveDuplicate(head);

結果は常に 1 つの構造のみを返し、リスト全体を削除します。DeleteSong 機能をテストしたところ、うまく機能したので、RemoveDuplicate 機能に何か問題があると思います。

4

2 に答える 2

0
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存在します)、またはed メモリholder = temp;への参照が失われます。malloc実際にはまったく使用holderしていないため、修正は関数から削除することです。最初のケースでは、複雑な を割り当てる以外の方法では使用していませんNULL。2 番目のケースでは、削除holder = temp;してfreeing する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;
}

一方、あなたのRemoveDuplicate機能はあなたが意図したことをしません。複製が元の直後に続く場合とそうでない場合の違いは別として、理由はわかりませんが、割り当てはそれぞれ異なりtemp = DeleteSong(temp,temp2->title);ます。temp2 = DeleteSong(temp2->next,temp2->title);何を変更しtempます。temp2を指しますが、リスト内のそれぞれの前任者が指している場所ではありません。少し ASCII アートで問題を説明しましょう:

       temp    temp2
         |      |
         v      v
song1->song2->song3->song4->song5->...

とは重複song2しています。では、2 つの曲が重複しているため、 の引数はすでに一致しており、あなたのバージョンのsong3では、すべてが完了しているため、リストはまったく変更されず、取得するだけです。temp = DeleteSong(temp,temp2->title);headDeleteSongDeleteSonghead = temp->next; return head;

            temp temp2
              |   |
              v   v
song1->song2->song3->song4->...

両方のポインタが同じリスト ノードを指しています。私のバージョンでDeleteSongは、ノードを解放し、d メモリをsong1.next指すようになりました。free

複製が元の曲の直後に続いていない場合、検索は既知の一致のtemp2 = DeleteSong(temp2->next,temp2->title);に開始されるため、一致するタイトルの曲が見つからない可能性があります。この場合、リストはまったく変更されず、後続の曲を指すように変更されるだけです。その後にタイトルが一致する曲がある場合、見つかった重複がリストから削除されず、それ以降の部分が変更され、不健全な状態になる可能性があります。がリストの最後から 2 番目のノードを指し、最後のノードに一致するタイトルがある場合、最後のノードはdですが、重複していたもののポインターは変更されていないため、 d メモリを指すようになりました。ポインター (これは発生するのを待っているセグメンテーション違反です)。temp2temp2freenextfree

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;
    }
}
于 2012-10-06T14:04:08.110 に答える
0

リストの最初の 2 つのエントリが重複している場合は、最初に次のことを確認したようです。

if(temp2 == temp->next){
    temp = DeleteSong(temp,temp2->title);
}

リストの先頭で true と評価されます。リストが突然リストの先頭だけに縮小された場合は、そこから何が起こるかを確認することをお勧めします。

エラーは実際には DeleteSong 関数で見つかった可能性があると思われます。これは、私が見た他の単方向リストから思い出すよりも少し複雑に見えるためです。

疑似 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

削除するノードの検索と実際の削除を組み合わせることができるため、リストを 2 回走査する必要はありません。また、どうしても必要な場合を除き、「temp」を変数名として使用しないようにしてください。混乱を招くことが多いためです。

多くのツール チェーンには、単一リンク リストを提供するライブラリが含まれています。このようなライブラリを使用することで、コーディングとデバッグの時間を節約できます。おそらく、ソートされたツリーまたはハッシュ リストも提供され、曲のタイトルなどの検索が大幅に高速化されます。

于 2012-10-06T12:17:11.913 に答える