この記事では、LeetCode問題集の連結リストの問題を紹介します。
リンクされたリストに関するその他の LeetCode の質問:
LeetCode 問題セット: リンクされたリスト (1)
LeetCode 問題セット: リンクされたリスト (2)
19. リストの最後から N 番目のノードを削除 (リンクされたリストの最後の N ノードを削除)
問題の説明
アイデアとコード
この問題には 2 つの解決策があります。
まず、最初にリストをトラバースして長さを決定し、次に別のトラバースを実行して、下から N 番目のノードを削除します。
コードは以下のように表示されます:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dummy = ListNode(val=-1, next=head)
num_node = 0 # number of nodes
while head:
num_node += 1
head = head.next
res = dummy
head = dummy
while head.next:
if num_node == n:
head.next = head.next.next
break
else:
num_node -= 1
head = head.next
res = res.next
return res
実行結果:
2つ目は、高速ポインタと低速ポインタの方法で、最初に高速ポインタをNステップ移動させてから、高速ポインタと低速ポインタを同時に移動させ、高速ポインタが最後まで移動すると、低速ポインタはN+1の一番下に移動します。ノードであり、その次のポイントが一番下の N-1 ノード (次の次のノード) を指していれば十分です。
コードは以下のように表示されます:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
fast, slow = head, head
# move fast n steps in advance
for i in range(n):
fast = fast.next
# special case: remove the 1st node
if not fast:
return head.next
# move fast and slow together
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next # remove the nth node from the end
return head
実行結果:
203. リンクされたリスト要素を削除する
問題の説明
アイデアとコード
この質問の考え方は単純で、ノードを 1 つずつトラバースし、削除する必要があるノードの前のノードの次のパラメーターを変更します。
コードは以下のように表示されます:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
dummy = ListNode(val=-1, next=head)
head = dummy
while head.next:
if head.next.val == val:
head.next = head.next.next
else:
head = head.next
res = dummy.next
return res
実行結果:
61. リストの回転
問題の説明
アイデアとコード
この質問の考え方は、連結リストの回転の結果を観察することです. 回転は、連結リストを 2 つのセグメントに切断してから再度結合することと同等であることがわかります. 切断位置は、回転数 k によって決定でき、対応するノードの次のパラメーターを変更できます。
2 つの特殊なケースに注意してください。
- 連結リストが空の場合、元の空の連結リストに直接戻る
- 連結リストの長さに対する回転数 k の余りを計算し、結果が 0 の場合は元の連結リストに戻る
コードは以下のように表示されます:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head:
return head
dummy = ListNode(val=-1, next=head)
num_node = 0
head = dummy
while head.next:
num_node += 1
head = head.next
# in case: k > num_node
k %= num_node
if not k:
return dummy.next
head_ori = dummy.next # original head
head = dummy.next
for _ in range(num_node - k):
head = head.next
dummy.next = head # new head
for _ in range(k - 1):
head = head.next
head.next = head_ori # next node of tail -> original head
for _ in range(num_node - k):
head = head.next
head.next = None
return dummy.next
実行結果:
公式の問題解は別の考え方で、まず連結リストを環にし、回転数 k に応じて対応する節点の位置を求めて環を解くというものです。
コードは以下のように表示されます:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head or not head.next:
return head
n = 1 # number of nodes
cur = head
while cur.next:
cur = cur.next
n += 1
k %= n # k < n
if not k:
return head
cur.next = head
for _ in range(n - k):
cur = cur.next
res = cur.next
cur.next = None # break the loop
return res
実行結果: