リンクリスト OJ(2)

目次

1. 2 つのソート済みリンク リストを新しいソート済みリンク リストにマージし、それを返します。新しいリンク リストは、指定された 2 つのリンク リストのすべてのノードを結合することによって形成されます。

2. 指定された値 x に基づいてリンク リストを 2 つの部分に分割し、x 未満のすべてのノードを x 以上のノードの前に配置するコードを作成します。

3. 連結リストの回文構造

4. 2 つのリンク リストを入力し、最初の共通ノードを見つけます。

5. リンクリストが与えられた場合、そのリンクリストにリングがあるかどうかを判断します。

6. リンク リストが与えられた場合、リングへの参加を開始するリンク リストの最初のノードを返します。リンクされたリストが非巡回の場合は NULL を返します

7. リンク リストが与えられると、各ノードには追加のランダム ポインタが含まれ、リンク リスト内の任意のノードまたは空のノードを指すことができます。このリンクされたリストのディープ コピーを返すリクエスト


横たわらずに輝いてください!


1.  2 つのソート済みリンク リストを新しいソート済みリンク リストにマージし、戻ります。新しいリンク リストは、指定された 2 つのリンク リストのすべてのノードを結合することによって形成されます。

https://leetcode.cn/problems/merge-two-sorted-lists/

コード 1 は次を示します: (ヘッダーなし)

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if (list1 == NULL)
    {
        return list2;
    }
    if (list2 == NULL)
    {
        return list1;
    }
    struct ListNode* head = NULL;
    struct ListNode* tail = NULL;
    while (list1 && list2)
    {
        struct ListNode* next1 = list1->next;
        struct ListNode* next2 = list2->next;
        if ((list1->val) < (list2->val))
        {
            if (tail == NULL)
            {
                head = list1;
                tail = list1;
            }
            else
            {
                tail->next = list1;
                tail = list1;
            } 
            list1 = next1;
        }
        else
        {
            if (tail == NULL)
            {
                head = list2;
                tail = list2;
            }
            else
            {
                tail->next = list2;
                tail = list2;
            } 
            list2 = next2;
        }
    }

    if (list1 != NULL)
    {
        tail->next = list1;
       
    }

    if (list2 != NULL)
    {
        tail->next = list2;
    }
    return head;
}

アイデア: 毎回小さなノードを取得し、それを新しいノードに挿入します。

:どちらかが空の場合は注意してください

見出し付き単一リンクリスト: head->next は最初のノードのアドレスを参照します (先頭、先頭の追加ノードに相当しますが、ノード内の val は不確実で、next は次のノードを指します)。 -linked list: head 最初のノードのアドレスを参照します。デフォルトでは、OJ の質問は主導権を持ちません。

コード 2 は次のように示します: (主導権を握る)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));//创建一个头
    struct ListNode* tail = head;
    head->next = NULL;//就是含有值的节点的第一个
    while (list1 && list2)
    {
        struct ListNode* next1 = list1->next;
        struct ListNode* next2 = list2->next;
        if ((list1->val) < (list2->val))
        {
            tail->next = list1;
            tail = list1;
            list1 = next1;
        }
        else
        {
            tail->next = list2;
            tail = list2;
            list2 = next2;
        }
    }

    if (list1 != NULL)
    {
        tail->next = list1;
       
    }
    if (list2 != NULL)
    {
        tail->next = list2;
    }
    struct ListNode* list = head->next;
    free(head);
    return list;
}

アイデア: 毎回小さなノードを取得し、それを新しいノードに挿入します。

: malloc のアドレスは最後に解放する必要があります。

2.指定された値xに基づいてリンク リストを 2 つの部分に分割し、 x未満のすべてのノードをx以上のノードの前に配置するコードを作成します。

https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking

コード表示

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode* LessHead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* LessTail = LessHead;
        struct ListNode* GreaterHead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* GreaterTail = GreaterHead;
        struct ListNode* cur = pHead;
        while (cur != NULL)
        {
            if (cur->val < x)
            {
                LessTail->next = cur;
                LessTail = cur;
            }
            else
            {
                GreaterTail->next = cur;
                GreaterTail = cur;
            }
            cur = cur->next;
        }
        GreaterTail->next = NULL;
        LessTail->next = GreaterHead->next;
        struct ListNode* list = LessHead->next;
        free(LessHead);
        free(GreaterHead);
        return list;


    }
};

アイデア: x 未満をリンク リストに挿入、x 以上をリンク リストに挿入、リンク リスト 1 とリンク リスト 2 をマージ (先頭のものを使用)

: リンク リスト 2 の最後の項目は NULL を指している必要があります。NULL を指していない場合、無限ループが発生する可能性があります [リンク リストの質問では、先頭と末尾に注意してください]

3. 連結リストの回文構造

https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

コード表示
 

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    while(cur)
    {
        struct ListNode* next = cur->next;
        cur ->next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while (fast && (fast ->next))
    {
        slow = slow ->next;
        fast = fast->next->next;
    }
    return slow;
}

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code here
        struct ListNode* mid = middleNode(A);
        struct ListNode* rHead = reverseList(mid);
        while (A && rHead)
        {
            if (A->val == rHead->val)
            {
                A = A->next;
                rHead = rHead->next;
            }
            else 
            {
                return false;
            }
        }
        return true;
    }
};

アイデア: まず、リンク リストの中間アドレス (奇数) または 2 番目のリンク リストの先頭として 2 番目の中間アドレス (偶数) を見つけます。次に、2 番目のリンク リストを反転し、次に元のリンク リストと 2 番目のリンク リストを反転します。リストを比較します。(元のリンク リストと 2 番目のリンク リストのいずれかが空の場合、ループから抜け出すことができ、それが回文リンク リストであることを示します)

4.  2 つのリンク リストを入力し、最初の共通ノードを見つけます。

https://leetcode.cn/problems/intersection-of-two-linked-lists/description/

コード表示:(案2)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //判断是否相交
    struct ListNode* tailA = headA;
    struct ListNode* tailB = headB;
    int lenA = 1;
    int lenB = 1;
    while (tailA->next != NULL)
    {
        tailA = tailA->next;
        lenA++;
    }
    while (tailB->next != NULL)
    {
        tailB = tailB->next;
        lenB++;
    }
    if (tailA != tailB)
    {
        return NULL;
    }
    //找到节点
    int gap = abs(lenA - lenB);
    //想法值得学习,大小
    struct ListNode* shortList = headA;
    struct ListNode* longList = headB;
    if (lenA > lenB)
    {
        shortList = headB;
        longList = headA;
    }
    while(gap--)
    {
        longList = longList->next;
    }
    while (longList && shortList)
    {
        if (longList == shortList)
        {
            return longList;
        }
        longList = longList->next;
        shortList = shortList->next;
    }
    return NULL;
}

アイデア 1 : A リンク リストと B リンク リストの各ノードを順番に比較し、等しい場合はそれが交点であり、最初の等しいものが交点になります。

(交差点かどうか、交差点の数を判定) 時間計算量:O(M*N)

アイデア 2 : 末尾ノードのアドレスが同じ場合、交差です。(交差点かどうかの判断)

2 つのリンク リストの長さを調べて、リンク リストの方が長い場合は最初に 2 つのリンク リスト間の差をたどり、次に 2 つのリンク リストを一緒にたどります。アドレスが同じ場合、その位置はノードです (何は交点) 時間計算量: O(M+ N)

: (1) アドレスは同じですが、値が等しいわけではありません(値はノードと必ずしも等しいとは限りません)

(2) コンパイラが実行する内容は文法エラーであり、コンパイラは実行ロジックをチェックアウトできないため、最後に return NULL を追加する必要があります (上に if があり値を返すため、コンパイラはif 条件が満たされない場合、戻り値はありません)。

5. リンクリストが与えられた場合、そのリンクリストにリングがあるかどうかを判断します。

https://leetcode.cn/problems/linked-list-cycle/submissions/

コード表示

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            return true;
        }
    }
    return false;
}

アイデア: [ファストとスロー ポインタ] スローは一度に 1 歩ずつ進み、ファストは一度に 2 歩ずつ進み、スローがリングに入るとキャッチアップ モードがオンになり、最後にファストがスローに追いつきます。リングはありません。 fast->next または fast は NULL になります。リングがある場合は NULL になりません。

: 最初は、高速と低速は等しいため、ループ内では、最初に割り当ててから比較します。

 (1)遅い場合は1歩ずつ、速い場合は2歩ずつ進めば追いつきます。

スローが半分まで進むと、ファストがリングに入り始めます。この時の二人の距離はNで、1回ごとに距離が1ずつ減っていくので必ず追いつくことができます。【距離は0、追いつく】

(2) 遅い場合は 1 歩ずつ、速い場合は 3 歩ずつ進み、追いつけない場合があります。

スローが1/3になると、ファストがリングに入り始めます。このときの二人の距離はNです。1回ごとに距離は2ずつ減っていきます。距離が奇数の場合は見逃します。【偶数なら追いつきます】。このとき、距離はリング番号-1【偶数は追いつく、奇数は追いつく】決して追いつけない】ので、追いつけない可能性があります。

6. リンク リストが与えられた場合、リングへの参加を開始するリンク リストの最初のノードを返します。リンクされたリストが非巡回の場合は NULLを返します

https://leetcode.cn/problems/linked-list-cycle-ii/description/

コード表示:(案1)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            struct ListNode* meet = slow;
            while (head != meet)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

アイデア1:リングに入るまでの距離をL、リングに入ってから二人が出会うまでの遅い移動距離×距離、リングに入ってからの二人の距離をN、リングの長さをCとする(速いと遅い)ポインター] ゆっくりは 1 歩歩きます、速いは一度に 2 歩かかります、速いは遅いの 2 倍の速さです [遅い場合は円を一周歩くことはなく、速い人に追いつかれますが、円を歩かずに速い人に追いつきます。スローがリングに入り、二人の距離は N で、N はリングの数より小さくなければなりません。したがって、二人が出会ったとき、スローは一周してはいけません。したがって、スローが移動した距離はリングに入るまでの距離です。 + 二人が出会う前とリングに入った後の距離 x は L+x; 高速で移動した距離は nC+L+x; n*C+L+x=2(L+x),----n *C=L+x; n は未知数]【遅い リングに入ったら速い人は一周するのは不可能】 n*C=L+x: エントリーリストから歩いていることが証明できる、もう1人は合流点から歩き、リングの入り口でゆっくりと速く合流します。

アイデア 2 : リンク リストの交点を見つけるように変換します。[合流点からの切断] 尾部として合流、合流→次を先頭として、先頭が与えられ、交点を見つけます。

7. リンク リストが与えられると、各ノードには追加のランダム ポインタが含まれ、リンク リスト内の任意のノードまたは空のノードを指すことができます。このリンクされたリストのディープ コピーを返すリクエスト

https://leetcode.cn/problems/copy-list-with-random-pointer/description/

コード表示

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head)
{
	struct Node* cur = head;
    //拷贝节点链接
    while (cur!= NULL)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = cur->next->next;
    }
    //random
    cur = head;
    while (cur != NULL)
    {
        if (cur->random == NULL)
        {
            cur->next->random = NULL;
        }
        else
        {
            cur->next->random = cur->random->next;
        }
        cur = cur->next->next;
    }
    //摘下来、链接
    struct Node* copyHead = NULL;
    struct Node* copyTail = NULL;
    cur = head;
    while (cur != NULL)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        if (copyHead == NULL)
        {
            copyHead  = copyTail = copy;
        }
        else
        {
            copyTail->next = copy;
            copyTail = copy;
        }
        cur = next;
    }
    return copyHead;

}

アイデア: 各ノードをコピーして元のノードに接続し、コピーしたノードのランダムをリンクします。新しいランダムは前のランダムの隣にあります。最後に、コピーしたノードをほどいてリンクします。[インデックスは0から始まります]


その他の質問のリンクリスト

リートコード: https://leetcode.cn/tag/linked-list/problemset/

Niuke.com: https://www.nowcoder.com/exam/oj

おすすめ

転載: blog.csdn.net/m0_57388581/article/details/131501329