[毎日の簡単な質問] データ構造リンク リスト - 単一リンク リスト oj 質問 (1)、いくつかの典型的な例により、単一リンク リストをすぐにマスターできます。

ここに画像の説明を挿入

Junxi_ の個人ホームページ

勤勉であり、誰も待たないように何年も費やすことを奨励してください

C/C++ ゲーム開発

こんにちは、ミナさん、Junxi_ です。今日は、長い間埋もれていた穴を埋めるためにここに来ました。夏休み前にこのシリーズをプレビューしましたが、さまざまな理由により (主に少し時間が経ってしまったため)怠け者)、今日は穴を開けただけです。私たちのシリーズは主にブロガー自身のプログラミング学習経験に基づいており、いくつかの古典的な質問タイプをブラッシュアップするためにモジュールを分割し、質問を行うことでコンテンツのこの部分の習熟度を全員が深めることができます。
それでは早速、今日の勉強を始めましょう!

  • 歯磨きの質問の順番は「はじめ方」から「土に入る」までとなっており、前の「始め方」の内容の一部は後の「土に入る」内容と関連しており、そのまま土に入るのに役立ちます!(冗談ではありません。これらの質問について注意深く考えていれば、独立して回答することができます)

1. リンクされたリスト要素を削除する

  • この質問の oj リンクは次のとおりです:リンクされたリスト要素を削除します
    ここに画像の説明を挿入
  • トピックのテキスト説明は理解するのが難しくありませんが、与えられた例を組み合わせると、このトピックがリンク リスト内の val に等しいすべての値を削除し、残りのノードを接続するヘッド ノードを返したいことがわかります。
  • 以下に、問題の解決策とコードの実装を示します。
  • 1. この質問は実際には、前に説明した単一リンク リストの中間削除と非常によく似ていますが、中間の削除はアドレスを通じて削除する場所を見つけることであり、この質問は val に等しい値を見つける必要がある点が異なります。削除するノードでは、val に等しいノードの前のノードを、値が val に等しいノードをスキップして、val の次のノードを指すようにし、最後に val に等しいノードを解放するだけです。
struct ListNode* removeElements(struct ListNode* head, int val) {
    
    
    if(head == NULL)//链表中什么都没有
        return NULL;
    
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    
    while(cur)
    {
    
    
        //如果当前节点是需要删除的节点
        if(cur->val == val)
        {
    
    
            //首先保存下一个节点
            struct ListNode* next = cur->next;
            //如果删除的为头节点,更新头节点
            //否则让当前节点的前趋节点链接next节点
            if(prev == NULL)
            {
    
    
                head = cur->next;
            }
            else
            {
    
    
                prev->next = cur->next;  
            }
            //释放当前节点,让cur指向next
            free(cur);
            cur = next;
        }
        else
        {
    
    
            //如果cur不是需要删除的节点,则更新prev,cur
            prev = cur;
            cur = cur->next;
        }
    }
    
    return head;//返回新头
}

ここに画像の説明を挿入


2. 逆リンクリスト

  • この質問 oj へのリンクは次のとおりです:リンクされたリストの反転
    ここに画像の説明を挿入

  • この質問には 2 つの考え方があります。それぞれを個別に紹介し、コードで実装してみましょう。

  • 1. リンクリストには先頭挿入という挿入方法がありますが、その名の通り先頭にノードを挿入する方法です 例えば、リンクリストの先頭に 1 2 3 4 5 を挿入すると、実際、リンク リストは実際には先頭から順番に挿入されます。つまり、5 4 3 2 1 です。これは、この質問で達成したい逆リンク リストと一致しませんか?

struct ListNode* reverseList(struct ListNode* head){
    
    
    //把原链表中的节点依次头插入新链表中
    struct ListNode*newnode=NULL;
    struct ListNode*cur1=head;
    while(cur1)
    {
    
    
        //头插 
      struct ListNode*next=cur1->next;
           
            cur1->next=newnode;
            newnode=cur1;
            cur1=next;
        
    }
    return newnode;
   
}

ここに画像の説明を挿入

  • 2. 3 つのポインタを介して連結リストを反転します このとき、ポインタが 3 つあり、n1 が先頭を指し、n2 が n1 の次を指し、n3 が n2 の次を指すとします。ロジック図を図に示します。
    ここに画像の説明を挿入
  • リンクリストを反転させたいのですが、2つのノードを結ぶ矢印を回転させます。C言語で言えば、ノードのポインタを次のノードを指す状態から前のノードを指すように変えることです。この時間は変更されます。図に示すように、新しい尾の場合は 1 の次を空にします`
    ここに画像の説明を挿入
  • このステップを完了したら、リンクされたリスト内の反転されていないノードも反転されるように、3 つのポインタを 1 ステップ前に移動する必要があります。

ここに画像の説明を挿入

  • 私たちの最終条件は何でしょうか?n1 が最後のノードである場合、リンク リスト内のすべてのノードが反転されたことを意味します。この時点で停止できますか? このとき、n2 が前の桁、つまり n2 が空の場合、n1 が最後の桁に来ているので、n2 が空であることをループ停止の条件として使用できます。

ここに画像の説明を挿入

struct ListNode* reverseList(struct ListNode* head) {
    
    
    if(head == NULL || head->next == NULL)//说明此时只有一个节点或者没有节点,直接返回head即可
        return head;
    
    struct ListNode* n1, *n2, *n3;
    n1 = head;
    n2 = n1->next;
    n3 = n2->next;
    n1->next = NULL;
    //中间节点不为空,继续修改指向
    while(n2)
    {
    
    
        //中间节点指向反转
        n2->next = n1;
        //更新三个连续的节点
        n1 = n2;
        n2 = n3;
        if(n3)//防止n3越界,判断一下n3是否为空
            n3 = n3->next;
    }
    //返回新的头
    return n1;
}

ここに画像の説明を挿入

3. 連結リストの中間ノード

  • oj リンクは次のとおりです:リンクされたリストの中間ノード
    ここに画像の説明を挿入
  • この oj の質問には 2 つの解決策もあります。1 つずつ分析してみましょう
  • 1.暴力的な解決方法
  • タイトルで必要なのは、リンク リストの中間ノードを見つけることです。次に、リンク リストにノードがいくつあるかを記録するという目標を達成するために、まずリンク リストを走査し、同時にカウントします。カウントの半分を取得し、リンクされたリストの半分をトラバースします。この時点では、 のノードが中間ノードです。
struct ListNode* middleNode(struct ListNode* head){
    
    
     
        struct ListNode*cur=head;
        struct ListNode*midcur=head;
        int size=0;//标记计数
        while(cur)
        {
    
    
            size++;
            cur=cur->next;
        }
        int len=size/2;//取链表节点数的一半找到中间结点
        while(len)
        {
    
    
            midcur=midcur->next;
            len--;
        }
        return midcur;
}

ここに画像の説明を挿入

  • 2 番目の方法はより独創的なもので、理解を助けるために実際の例を使用してみましょう。
  • ある日、老僧が若い僧侶にいくつかの質問をし、線香の半分以内で答えるように頼みました。このとき、老僧はあなたにタイミングの責任を任せましたが、線香が足りないため、寺院では、時間指定できるお線香が 1 本しかありません。では、この時点で何をすべきでしょうか?
  • 答えは「両端から同時に焼き始める」です。
  • この実践的な例にインスピレーションを受けましたか? また、リンク リストの中間ノード (半分) も要求する必要があります。
  • 答えを明らかにします。高速ポインタと低速ポインタを使用します。高速ポインタは一度に 2 ステップを実行し、低速ポインタは一度に 1 ステップを実行します。高速ポインタが最後に到達すると、低速ポインタはこの時点で中間ノードにあります。 。
  • 类比一下,此时快指针就是从两头烧,慢指针是从一头烧,当两头烧完时,就相当于一头烧烧完了半炷香!!
struct ListNode* middleNode(struct ListNode* head){
    
    
     
       struct ListNode*fast,*slow;
        fast=slow=head;
        while(fast&&fast->next)
        {
    
    
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
}

ここに画像の説明を挿入


要約する

  • 本日の内容はこれで終わりですが、一度に多くのことを話しすぎると欲張りになってしまう可能性があるので、とりあえずはこの3つの典型的な質問を紹介し、その他の関連トピックについては後ほど更新していきます。本当によく学びたいのであれば、自分で試してみなければなりません。自分で書くことができなければ決して学ぶことはできないということを覚えておいてください。

  • 質問がございましたら、コメント欄またはプライベート メッセージでお問い合わせください。また次回お会いしましょう。

新しいブロガーを作成するのは簡単ではありません。記事の内容が役立つと思われる場合は、離れる前にこの新しいブロガーをクリックするとよいでしょう。皆様の応援が更新の励みになります!

**(Ke Li はブロガーを 3 回連続でサポートするようお願いしています!!! 以下のコメントをクリックして「いいね」を押し、Ke Li を支援するために集めてください)**

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/syf666250/article/details/132098918