1.交差するリンクリスト(160)
タイトル説明:
[簡単な質問]
2つの単一リンクリストが交差する開始ノードを見つけるプログラムを作成します。
次の2つのリンクリストなど。
交差点はノードc1から始まります。
例1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
トピックリンクの
アイデアの分析:
解決策1:暴力法
- リンクリストAのノードごとに、リンクリストBに移動してトラバースし、同じノードがあるかどうかを確認します。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
pA=headA
while pA:
pB=headB
while pB:
if pA==pB:
return pA
pB=pB.next
pA=pA.next
- 制限時間を超えました
- 時間計算量:O(M ∗ N)O(M * N)O (M∗N )、M、Nは、それぞれ2つのリンクリストの長さです。
- スペースの複雑さ:O(1)O(1)O (1 )。
問題解決策2:ハッシュテーブル
- 最初にリンクリストAをトラバースし、ハッシュテーブルを使用して各ノードを記録します(ノード値ではなくノード参照を保存する必要があることに注意してください)。
- 次に、リンクリストBをトラバースし、ハッシュテーブルに表示されているノードが2つのリンクリストの共通部分であることを確認します。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
hashmap=set()
pA=headA
while pA:
hashmap.add(pA)
pA=pA.next
pB=headB
while pB:
if pB in hashmap:
return pB
pB=pB.next
return None
- 時間計算量:O(m + n)O(m + n)O (m+n )
- スペースの複雑さ:O(m)O(m)O (m )
問題解決策3:ダブルポインター
1.リンクリストAの長さがa + cであり、リンクリストBの長さがb + cであると仮定します。ここで、cはテールの共通部分の長さであり、a + c + b =であることがわかります。 b + c + a。
2. 2つのポインタpAとpBを作成します。これらは、それぞれリンクリストAとBのヘッドノードとして初期化されます。次に、ノードごとに後方にトラバースさせます。
3.リンクリストAにアクセスするポインタpAがリンクリストの最後に到達したら、リンクリストBの先頭から開始してリンクリストBにアクセスします。同様に、リンクリストBにアクセスするポインタpBがリンクリストBにアクセスする場合も同様です。リンクリストの最後に到達したら、リンクリストAの最後から開始します。ヘッドはリンクリストAへのアクセスを開始します。pAとpBが特定の瞬間に出会う場合、pA / pBは交差ノードです。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
pA, pB = headA, headB
while pA != pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
return pA
- 時間計算量:O(m + n)O(m + n)O (m+n )
- スペースの複雑さ:O(1)O(1)O (1 )
2.リンクリストの分割(725)
タイトル説明:
【中】
ヘッドノードがルートであるリンクリストが与えられた場合、リンクリストをk個の連続する部分に分割する関数を記述します。
各パーツの長さは可能な限り等しくする必要があります。2つのパーツ間の長さの差は1を超えることはできません。つまり、一部のパーツがnullになる可能性があります。
k個のパーツは、リンクリストに表示される順序で出力され、前面のパーツの長さは背面の長さ以上である必要があります。
上記のルールを満たすリンクリストのリストを返します。
例:1-> 2-> 3-> 4、k = 5 // 5結果[[1]、[2]、[3]、[4]、null]
例1:
输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
解释:
输入输出各部分都应该是链表,而不是数组。
例如, 输入的结点 root 的 val= 1, root.next.val = 2, \root.next.next.val = 3, 且 root.next.next.next = null。
第一个输出 output[0] 是 output[0].val = 1, output[0].next = null。
最后一个元素 output[4] 为 null, 它代表了最后一个部分为空链表。
思考分析:
1.最初にリンクリストを走査して、リンクリストの長さを計算しますtotal_len
2、kは、割り当てられる各セグメントの全長を除算することによって得られ基础长度
ますbasic_len= total_len // k
。
図3に示すように、上面長さの要求部分の対象は長さ以上でなければならず、後部長さの差は1を超えることはできないので、kを割ることによって得られる全長余数 m
は前 m 段每段的长度为:basic_len + 1
;である。
4.次に、2層のループが必要です。外側のループはk個のセグメントをトラバースし、毎回現在のノードを結果リストに追加します。
5.内側のループは各セグメントのサイズをトラバースし、ポインターを後方に移動します:cur = cur.next、割り当てられるべきセグメントの長さに達するまで、このセグメントは切り捨てられ、次のリンクリストは引き続き後続のループで割り当てられます。現在のノードcurが存在する必要があるという内部ループの前提条件に特に注意してください。curがNullの場合、cur.nextが存在しないとエラーが報告されます。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
# 链表总长度
total_len=0
cur=root
while cur:
total_len+=1
cur=cur.next
# 每段的基础长度
basic_len=total_len // k
# 余数,前m段需要在基础长度加1
m=total_len % k
res=[]
cur=root
for i in range(k):
res.append(cur)
# 算出每段的长度
size=basic_len + (1 if m>0 else 0)
if cur:
for j in range(size-1):
cur=cur.next
m-=1
tmp=cur.next # 把后面一段截掉,后面一段需要在后面继续划分
cur.next=None
cur=tmp
return res
- 時間計算量:O(n + k)O(n + k)O (n+k )
- スペースの複雑さ:O(k)O(k)O (k )