単一リンクリストのブラッシング質問 (1-3)

目次

逆リンクリスト 

要素を削除する

ソートされたリンクリストを結合する


逆リンクリスト 

リーキ

この問題は、ヘッド ノードを取得し、そのヘッドを順番に挿入することで解決し ます。次のノードは接続する前に保存する必要があることに注意してください

struct ListNode* reverseList(struct ListNode* head){
    typedef struct ListNode SL;
    SL* cur = head;
    SL* rhead = NULL;//初始指向空
    while(cur)
    {
        SL* next = cur->next;
        cur->next = rhead;
        rhead = cur;
        cur = next;  
    }
    return rhead;
}

要素を削除する

リーキ

アイデア 1:位置 pos の要素、つまり以前に実装したインターフェイスを削除するには、ヘッダーの削除と、セカンダリ ポインターを渡すかどうかを考慮する必要があります。

アイデア 2: 上のデモ画像を見て、配列表の最後にデータを追加するイメージはありますか? 末尾挿入の条件を満たすデータを取得し、指定された要素を無視して保持することもできます。次の要素です。

 1. ポインタを初期化する

struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode SLT;
SLT* newhead = NULL,*tail = NULL;//新链表头和尾
SLT* cur = head;//原链表遍历指针

2.テールプラグ  

while(cur)
{
    if(cur->val!=val)//不等
    {
    if(tail == NULL)//第一次
    {
        newhead = tail = cur;        
    }
    else
    {
        tail->next = cur;
        tail = tail->next;
    }
    cur =cur->next;
    else //相等
    {
        SLT* Next = cur->next;
        free(cur);
        cur = Next;
    }
}

全体的なロジックは問題ありませんが、テスト ケースを通過できません

特定の問題を詳細に分析しましょう。何が問題なのかわからないので、もっと熱心に、デバッグ用の単純な単一リンク リストを作成する必要があります。この方法により、問題解決能力が向上し、コード レベルが向上します。

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

typedef struct ListNode
{
    int val;
    struct ListNode* next;
}SLT;
SLT* CreateSList(int* a, int n)//用数组构建链表
{
    SLT* phead = nullptr, * ptail = nullptr;
    int x = 0;
    for (int i = 0; i < n; i++)
    {
        //sacnf("%d",&x);
        SLT* newnode = (SLT*)malloc(sizeof(SLT));//初始化(BuyList())
        if (newnode == nullptr)
        {
            perror("malloc fail");
            exit(-1);
        }
        else
        {
            newnode->val = a[i];
            newnode->next = nullptr;
        }
        if (phead == nullptr)//连结
        {
            phead = ptail = newnode;
        }
        else
        {
            ptail->next = newnode;
            ptail = newnode;
        }
    }
    return phead;
}

struct ListNode* removeElements(SLT* head, int val) {
    SLT* newhead = nullptr, * tail = nullptr;//新链表头和尾
    SLT* cur = head;//原链表遍历指针
    while (cur)
    {
        if (cur->val != val)//不等
        {
            if (tail == NULL)//第一次
            {
                newhead = tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else //相等
        {
            SLT* Next = cur->next;
            free(cur);
            cur = Next;
        }
    }
    return newhead;
}
        int main()
        {
            int a[] = { 1,2,6,3,4,5,6};
            SLT* plist = CreateSList(a, sizeof(a) / sizeof(int));
            removeElements(plist, 6);
            return 0;
        }

何が間違っているのかわかりますか? はい、プログラムは通常、必要なデータを削除しますが、最後のデータは空を指しておらず、削除される 6 のアドレスを指していることがわかります

この問題を解決した後、Likou 上のテスト ケースを置き換えて再度デバッグすると、まだエラーがあることがわかります。

今回は、配列がすべて同じで、すべて削除されたオブジェクトであるため、末尾が空を指し、逆参照の問題が発生するため、判断を追加します。これは完璧です。

struct ListNode* removeElements(SLT* head, int val) {
    typedef struct ListNode SLT;//最好不要这样定义
    SLT* newhead = nullptr, * tail = nullptr;//新链表头和尾
    SLT* cur = head;//原链表遍历指针
    while (cur)
    {
        if (cur->val != val)//不等
        {
            if (tail == NULL)//第一次
            {
                newhead = tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else //相等
        {
            SLT* Next = cur->next;
            free(cur);
            cur = Next;
        }
    }
    if(tail)
    tail->next = nullptr;
    return newhead;
}

先ほど作った単連結リストのデバッグ機能を自分で実装することもできますので、今後出題する際にこのような注意喚起に遭遇した際には、テンプレートデバッグを利用すると効率が上がりますよ~。

この質問は、センチネル ノードを追加することによっても行うことができます。これには利点があります。新しいリンク リストに末尾が挿入されるとき、リンク リストが空であることを心配する必要はなく、最初の末尾の挿入は空であると判断されます。 。


struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode SLT;
SLT* guard,*tail;
 guard = tail = (SLT*)malloc(sizeof(SLT));
SLT* cur = head;
while(cur)
{
    if(cur->val!=val)
    {
        tail->next = cur;
        tail = tail->next;
    cur =cur->next;
    }
    else 
    {
        SLT* Next = cur->next;
        free(cur);
        cur = Next;
    }
}
     tail->next = NULL;//避免删除最后一个数据前一个数据指向野指针
     SLT* newhead = guard->next;
     free(guard);
    return newhead;
}

返すとき、ガードを返すことができないとは明確に述べられていませんが、その次のノード、つまり有効なデータを格納しているノードが返されることに注意してください。null ポインタが渡された場合でも返すことは有効です。誰もが自分自身でそれを感じることができます。

ソートされたリンクリストを結合する

リーキ

アイデア:小さな末尾挿入を行い、そのうちの 1 つが終了したら、残りのデータを新しい配列に (順序よく) リンクします。

ここでは簡単にするためにセンチネル ノードを使用します。 

上記のテスト ケースは、一方が空であるか両方が空の場合、比較せずにアドレスを直接返すことができるため、完全に合格できることを示しています。

 

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    /*if(list1 == NULL)//非哨兵位
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }*/
    struct ListNode* guard,*tail;
    guard = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
                tail->next = list1;
                tail = tail->next;
            list1 = list1->next;
        }
        else
        {
                tail->next = list2;
                tail = tail->next;
            list2 = list2->next;
        }
    }
if(list1)//剩余链接
    tail->next = list1;
if(list2)
    tail->next = list2;
    struct ListNode* newhead = guard->next;
    free(guard);
return newhead;
}

前の質問とは異なり、この質問ではエンド ノードのワイルド ポインタを気にする必要はありません。見張りの位置が非常に便利であることがわかります。

結論は:

  • センチネル ビットを使用すると、セカンダリ ポインタを渡す必要はありません (構造を変更するだけです)。
  • テールプラグはとても便利です
  • 単一リンクリストは一般的には使用されません

おすすめ

転載: blog.csdn.net/dwededewde/article/details/131229601
1-3