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、です。
入り口に素敵です。
これは、リングとの出会いの問題を意味しています。
この質問は、したがって、いくつかの問題を解決する必要があります
- あなたはリングのリストを持っていることを確認しています
- リストノードの数を決定します
- 入口ノードの決定
/**
* 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;
}
};
制限時間を超えた-
以下のコードを変更するために、テストに合格しました。
内容を変更します。
- 二重のループを回避しながら、
- ループ終了条件を回避しながらすることは、真の値であります
/**
* 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
プロンプト
それは、配列学んだ内容で、私たちに似ています。しかし、それはより困難と多くのエラーが発生しやすいかもしれません。あなたは、次の点に注意してください。
次のフィールドを呼び出す前に、必ずノードが空であるかどうかを確認してください。
NULLポインタエラーが発生します空のノードの次のノードを取得します。例えば、我々は高速に実行する前に= fast.next.next、必要に高速をチェックしfast.next空でないために。慎重に、サイクルの終わりのための条件を定義します。必ずあなたの状態が無限ループエンドを起こさない作るためにいくつかの例を実行します。あなたが終了条件を定義するときは、私たちの最初のヒントを考慮しなければなりません。
複雑性分析
簡単にスペース複雑な分析。あなただけの任意の余分なスペースを使用せずに、ポインタを使用している場合は、その空間計算量はO(1)です。しかし、分析時間の複雑さはより困難です。答えを得るために、我々は分析の運転サイクル数を必要とします。
前述の例示的なルックアップ・サイクルでは、我々は、より遅いの各ポインタの1ステップを移動、高速2段階、各ポインタが移動を仮定しました。
如果没有循环,快指针需要 N/2 次才能到达链表的末尾,其中 N 是链表的长度。
如果存在循环,则快指针需要 M 次才能赶上慢指针,其中 M 是列表中循环的长度。
明らかに、M <= N. だから我々は、ループのN回実行されます。各サイクルのために、我々は唯一の時間の一定のレベルを必要とします。このように、アルゴリズムの時間複雑度は、O(N)の合計です。
他の問題の独自の分析は、分析スキルを向上させます。アカウント異なる条件に取ることを忘れないでください。それはすべてのケースを分析することが困難である場合は、最悪の場合を考えてみてください。