2 - ビス問題リストポインタLeetCode--

2 - ビス問題リストポインタLeetCode--

考える:
チェーンリングがあるかどうかを判断するために
、いくつかの状況を引用:

graph LR
A-->B
B-->C
C-->D
D-->E
E-->C
graph LR
A-->B
B-->A

あなたは、ハッシュテーブルを使用してソリューションを提案している可能性があります。しかし、両手法を使用して、より効率的な解決策があります。あなたは以下のものを読む前に、自分自身について考えてみます。

2人の異なる速度ランナーがある想像してみてください。彼らは直線道路を走行している場合は、最初の目的地で実行されます。彼らは、円形の軌道上で実行されている場合は、それはジョガーを実行し続けた場合になります人々に追いつくためにスプリント。

これは、我々はリストに使用するものである2つの異なる速度ポインタの状況が発生しました:

如果没有环,快指针将停在链表的末尾。
如果有环,快指针最终将与慢指针相遇。

だから、残りの質問は次のとおりです。

这两个指针的适当速度应该是多少?

安全なオプションは、ゆっくりとポインタ一歩たび、ポインタが移動、高速の2つのステップを移動することです。各反復は、ポインタはすぐに追加のステップを移動します。ループの長さがMである場合には、M回の反復の後、高速のポインタは確かに一度ループ、およびポインタに追いつくために遅くすること。

那其他选择呢?它们有用吗?它们会更高效吗?

1つの1タイトル循環リンクリスト(シンプル)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 当链表中没有节点或只有一个节点时 false
        if(!head || !head->next)
            return false;
        // 两个节点的初始值都是head
        ListNode* p1 = head;
        ListNode* p2 = head;
        // 
        while( p1->next && p2->next)
        {
            if(p1->next == p2->next->next)
                return true;
            p1 = p1->next;
            p2 = p2->next->next;
            if( !p1 || !p2 )
                return false;
        }
        return false;
    }
};

質問2循環リンクリスト2(中タイトル)

自身のアイデア:
barabara
が、
自分の考え...しないように
他の人の良いアイデアを見て

graph LR
0-->1
1-->2
2-->3
3-->4
4-->5
5-->2

この質問は、安全性、ダブルポインタを主に使用し、異なる出発点を証明するために提供するアイデアにあります。
リング内に配置されたノードの数が、N N + 1つのノードから他のノードから0、です。
入り口に素敵です。
これは、リングとの出会いの問題を意味しています。

この質問は、したがって、いくつかの問題を解決する必要があります

  1. あなたはリングのリストを持っていることを確認しています
  2. リストノードの数を決定します
  3. 入口ノードの決定
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        int num = counter(head);
        if ( num == 0)
            return NULL;
        ListNode* pa = head;
        ListNode* pb = head;
        for (int i = 0 ; i < num; i++)
        {
            pb = pb->next;
        }
        while (pa->next && pa->next)
        {
            if(pa == pb)
                return pb;
            pa = pa->next;
            pb = pb->next;
        }
        return NULL;
    }
    
    int counter( ListNode* head )
    {
        // 链表为空或链表中只有一个节点-->不存在环-返回0
        if( !head || !head->next )
            return 0;
        // 设置双指针
        ListNode* p1 = head;
        ListNode* p2 = head;
        // 
        int count = 0;
        while( p1->next && p2->next )
        {
            // 若p1和P2即将相遇,重新赋值,并开始计数
            if( p1->next == p2->next->next)
            {
                p1 = p1->next;
                p2 = p1->next;
                count = 2;
                while(p2->next)
                {
                    if( p1 == p2 )
                    {
                        return count;
                    }
                    p2 = p2->next;
                    count ++;
                }
            }
            p1 = p1->next ;
            p2 = p2->next->next;
            if(!p1||!p2)
                return 0;
        }
        return 0;
    }
};

制限時間を超えた-
以下のコードを変更するために、テストに合格しました。

内容を変更します。
  1. 二重のループを回避しながら、
  2. ループ終了条件を回避しながらすることは、真の値であります
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 确定快慢指针相遇的节点
        ListNode* pmeet = meeting(head);
        if ( pmeet == NULL)
            return NULL;
        // 确定环内节点的个数
        int count = 1 ;
        ListNode* p1 = pmeet;
        while( p1->next != pmeet )
        {
            p1 = p1->next;
            count ++;
        }
        // 确定环的入口节点
        ListNode* pa = head;
        ListNode* pb = head;
        for (int i = 0 ; i < count; i++)
        {
            pb = pb->next;
        }
        while ( pa != pb )
        {
            pa = pa->next;
            pb = pb->next;
        }
        return pa;
    }
    
    // 确定快慢指针相遇的节点
    ListNode* meeting (ListNode* head )
    {
        // 链表为空或链表中只有一个节点-->不存在环-返回0
        if( !head || !head->next )
            return NULL;
        // 设置双指针
        ListNode* p1 = head;
        ListNode* p2 = head;
        // 
        ListNode* meet = head;
        while( p1->next && p2->next )
        {
            // 若p1和P2即将相遇,重新赋值,并开始计数
            if( p1->next == p2->next->next)
            {
                meet = p1->next;
                return meet;
            }
            p1 = p1->next ;
            p2 = p2->next->next;
            if(!p1||!p2)
                return NULL;
        }
        return NULL;
    }
};

問題3交差リスト

graph LR
A-->B
B-->C
C-->F
E-->F
D-->E
F-->G
G-->H
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 如果两个链表其中任意一个为空就不会有相交节点
        if( !headA || !headB )
            return NULL;
        // 两个链表从头节点就相交了
        if( headA == headB )
            return headA;
        ListNode* pa = headA;
        ListNode* pb = headB;
        // 求两个链表的长度
        int numa = counter(headA);
        int numb = counter(headB);
        // 哪一个链表长,其指针就往前步进长度差的步长
        int step = 0;
        if ( numa >= numb )
        {
            step = numa - numb;
            for(int i = 0; i < step ; ++i)
            {
                pa = pa->next;        
            }
        }
        else
        {
            step = numb - numa;
            for(int j = 0 ; j < step; ++j)
            {
                pb = pb->next;
            }
        }
        
        // 定位第一个相同的节点
        while ( pa && pb &&  (pa != pb) )
        {
            pa = pa->next;
            pb = pb->next;
        }
        return pb;
        
        // 第二种循环的写法
        /*
        while ( pa && pb )
        {
            if ( pa == pb )
                return pa;
            pa = pa->next;
            pb = pb->next;
        }
        return NULL;
        */
    }
    // 返回单链表中的节点数
    int counter(ListNode* head)
    {
        ListNode* p = head;
        int count = 1;
        if( !p )
            return 0;
        if( !p->next )
            return 1;
        while( p->next )
        {
            p = p->next;
            ++count;
        }
        return count;
    }
};

4つの質問は最後から二番目のノードnのリストを削除します

最初のアイデアは、補助スタックによって解決されます。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 
        if( !head || n <= 0 )
            return NULL;
        // 建立一个辅助栈
        stack<ListNode*> nodes;
        // 遍历链表,依次放入栈中
        ListNode* p = head;
        while(p)
        {
            nodes.push(p);
            p = p->next;
        }
        
        if(n == 1)
        {
            nodes.pop();
            ListNode* pend = nodes.top();
            pend->next = nullptr;
            return head;
        }
        // 遍历栈中的节点到第n-1个节点
        int i = 1;
        while ( i != n-1 && n > 1 )
        {
            if(nodes.empty())
                return NULL;
            nodes.pop();
            ++i;
        }
        ListNode* pe = nodes.top();
        nodes.pop();
        nodes.pop();
        ListNode* ps = nodes.top();
        ps->next = pe;
        return head;
    }
};

テストケースを通じて、エラーへの回答を提出

Line 152: Char 17: runtime error: reference binding to misaligned address 0xbebebebebebec0b6 for type 'struct ListNode *', which requires 8 byte alignment (stl_deque.h)

次のようにコードが変更されます。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 
        if( !head || n <= 0 )
            return NULL;
        // 建立一个辅助栈
        stack<ListNode*> nodes;
        // 遍历链表,依次放入栈中
        ListNode* p = head;
        while(p)
        {
            nodes.push(p);
            p = p->next;
        }
        // 倒数第1个节点
        if(n == 1)
        {
            nodes.pop();
            if(nodes.empty())
                return NULL;
            ListNode* pend = nodes.top();
            pend->next = NULL;
            return head;
        }
        // 倒数n-1个节点之前的节点出栈
        int i = 1;
        while ( i != n-1 && n >= 2 )
        {
            if(nodes.empty())
                return NULL;
            nodes.pop();
            ++i;
        }
        // 得到第n-1个节点,使其出栈
        ListNode* pe = nodes.top();
        nodes.pop();
        // 第n个节点出栈
        nodes.pop();
        // 如果倒数第n个节点之前再无节点,head = pe
        if(nodes.empty())
        {
            head = pe;
            return head;
        }
        ListNode* ps = nodes.top();
        ps->next = pe;
        return head;
    }
};

補助スタックを使用する場合は、コードの堅牢性がに大きな注意を払う必要があり
、我々は注意を払う必要がある場合、スタック、スタックが空であります!

概要 - 問題のリストへのダブルポインタ

コードテンプレート:

// Initialize slow & fast pointers
ListNode* slow = head;
ListNode* fast = head;
/**
 * Change this condition to fit specific problem.
 * Attention: remember to avoid null-pointer error
 **/
while (slow && fast && fast->next) {
    slow = slow->next;          // move slow pointer one step each time
    fast = fast->next->next;    // move fast pointer two steps each time
    if (slow == fast) {         // change this condition to fit specific problem
        return true;
    }
}
return false;   // change return value to fit specific problem

プロンプト

それは、配列学んだ内容で、私たちに似ています。しかし、それはより困難と多くのエラーが発生しやすいかもしれません。あなたは、次の点に注意してください。

  1. 次のフィールドを呼び出す前に、必ずノードが空であるかどうかを確認してください。
    NULLポインタエラーが発生します空のノードの次のノードを取得します。例えば、我々は高速に実行する前に= fast.next.next、必要に高速をチェックしfast.next空でないために。

  2. 慎重に、サイクルの終わりのための条件を定義します必ずあなたの状態が無限ループエンドを起こさない作るためにいくつかの例を実行します。あなたが終了条件を定義するときは、私たちの最初のヒントを考慮しなければなりません。

複雑性分析

簡単にスペース複雑な分析。あなただけの任意の余分なスペースを使用せずに、ポインタを使用している場合は、その空間計算量はO(1)です。しかし、分析時間の複雑さはより困難です。答えを得るために、我々は分析の運転サイクル数を必要とします。

前述の例示的なルックアップ・サイクルでは、我々は、より遅いの各ポインタの1ステップを移動、高速2段階、各ポインタが移動を仮定しました。

如果没有循环,快指针需要 N/2 次才能到达链表的末尾,其中 N 是链表的长度。
如果存在循环,则快指针需要 M 次才能赶上慢指针,其中 M 是列表中循环的长度。

明らかに、M <= N. だから我々は、ループのN回実行されます。各サイクルのために、我々は唯一の時間の一定のレベルを必要とします。このように、アルゴリズムの時間複雑度は、O(N)の合計です。

他の問題の独自の分析は、分析スキルを向上させます。アカウント異なる条件に取ることを忘れないでください。それはすべてのケースを分析することが困難である場合は、最悪の場合を考えてみてください。

おすすめ

転載: www.cnblogs.com/jiangxinyu1/p/12285012.html