Zubora Code

NeetCode 150 全問の解き方とポイントまとめ Linked List

Published: 29 August, 2024
Revised: 29 August, 2024

Linked List

本記事は以下記事の一部です。

NeetCode 150 全問の解き方とポイントまとめ & 感想

Linked Listは最初とっつきにくくて考え方が独特に思える。特に競プロとかだとあまり見ないかも?TreeやGraphを理解するための前段階として教育的に知っておくと良いような感じ。実際Linked List自体は例えばJavaでも標準ライブラリに実装されてるし、よく使う。

最初、一旦全部配列に移し替えて、Arraysの問題として取り扱うようにすれば簡単じゃんと思ったし、実際それでSuccessする。が、Linked ListはLinked Listのまま取り扱うように理解することで知識の幅が広がるのと、Space Complexityも抑えることができるので、それで慣れておくと良い。

35. Reverse Linked List

Link

https://neetcode.io/problems/reverse-a-linked-list

Difficulty (Easy, Medium, Hard)

Easy

Time Complexity

O(N + M)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

4

Memo

  • これ最初解答見ても訳わからないかもしれないけど、Linked Listはこうやってひっくり返すもの。このテクニックをLinked Listの別問題でめちゃめちゃ使うので、Binary Searchとかと一緒で流れるように書ける必要がある。
  • currのポインタをnextにずらしつつ、prevの先頭にcurrを設定し続けるイメージ。最終的にprevがheadのケツを先頭にしたLinked Listになる。

Problem

Given the beginning of a singly linked list head, reverse the list, and return the new beginning of the list.

Example 1:

Input: head = [0,1,2,3]

Output: [3,2,1,0]

Copy

Example 2:

Input: head = []

Output: []

Copy

Constraints:

  • 0 <= The length of the list <= 1000.
  • -1000 <= Node.val <= 1000

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        curr = head
        prev = None
        while curr:
            tmp = curr.next
            curr.next = prev
            prev = curr
            curr = tmp
        
        return prev


36. Merge Two Sorted Lists

Link

https://neetcode.io/problems/merge-two-sorted-linked-lists

Difficulty (Easy, Medium, Hard)

Easy

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

4

Memo

  • これも別のHard問題の一部で必要になったりする。流れるように書けると良い。考え方自体はストレートなのでそう難しくないと思う。
  • かの有名なマージソートも同じアルゴリズムでソートされた配列同士をマージするので、そういう意味でも基本的な知識として必要。
  • curr と dummy を用意して、dummyの方でポインターを進めて、最後currの方をreturnするテクは必須の考え方なので重要。

Problem

Given the beginning of a singly linked list head, reverse the list, and return the new beginning of the list.

Example 1:

Input: head = [0,1,2,3]

Output: [3,2,1,0]

Copy

Example 2:

Input: head = []

Output: []

Copy

Constraints:

  • 0 <= The length of the list <= 1000.
  • -1000 <= Node.val <= 1000

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        curr = ListNode()
        dummy = curr

        while list1 and list2:
            if list1.val <= list2.val:
                # ここは dummy.next = ListNode(list1.val) でも良いが、
                # 以下の書き方でも問題なく、空間計算量を減らせる
                dummy.next = list1
                list1 = list1.next
            else:
                dummy.next = list2
                list2 = list2.next
            
            dummy = dummy.next
        
        # ↑まででどちらかは必ずなくなるが、片方余ってる可能性があるので、
        # そのままケツに繋げてあげる
        if list1:
            dummy.next = list1
        
        if list2:
            dummy.next = list2
        
        return curr.next


37. Reorder List

Link

https://neetcode.io/problems/reorder-linked-list

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

6

Memo

  • これは結構LinkedList独特のクセがある感じ。
  • 一旦配列に取り出しちゃえばTwo Pointerの問題として取り扱えるので解きやすくなるが、Linked Listのまま取り扱って無駄な空間計算量を使わないのが大事
  • 最初の半分を示すLinkedNodeと、後ろの半分をひっくり返したLinkedNodeを作って、交互に組み合わせていくイメージ
  • 前半と後半に分けるときに、slowが前半の最後に来るためにはslowとfastをどう進めれば良いかを奇数と偶数でしっかり考えてからコードを書くことが大事。

Problem

You are given the head of a singly linked-list.

The positions of a linked list of length = 7 for example, can intially be represented as:

[0, 1, 2, 3, 4, 5, 6]

Reorder the nodes of the linked list to be in the following order:

[0, 6, 1, 5, 2, 4, 3]

Notice that in the general case for a list of length = n the nodes are reordered to be in the following order:

[0, n-1, 1, n-2, 2, n-3, ...]

You may not modify the values in the list's nodes, but instead you must reorder the nodes themselves.

Example 1:

Input: head = [2,4,6,8]

Output: [2,8,4,6]

Copy

Example 2:

Input: head = [2,4,6,8,10]

Output: [2,10,4,8,6]

Copy

Constraints:

  • 1 <= Length of the list <= 1000.
  • 1 <= Node.val <= 1000

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def reorderList(self, head: Optional[ListNode]) -> None:
        slow, fast = head, head
        while fast.next and fast.next.next:
            slow = slow.next
            fast = fast.next.next
        
        # 後半半分の先頭がslow.nextになる
        second = slow.next
        # 前半と後半を切り離す
        slow.next = None
        # 後半をひっくり返す
        prev = None
        while second:
            tmp = second.next
            second.next = prev
            prev = second
            second = tmp
        
        # prevに後半半分がひっくり返った先頭のポインタがあるので、
        # 再度secondに設定し直す
        second = prev

        # 前半半分は改めてheadを設定する
        first = head

        # secondがなくなるまでループを続ける
        # first -> second -> という感じで組み直す
        while second:
            tmp1 = first.next
            tmp2 = second.next
            first.next = second
            second.next = tmp1
            first = tmp1
            second = tmp2


38. Remove Nth Node From End of List

Link

https://neetcode.io/problems/remove-node-from-end-of-linked-list

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

5

Memo

  • くどいようだけど、これも配列に取り出せばめちゃ簡単だけど、LinkedListのままだと後ろからn番目をどうやって特定するかが肝。
  • これもslowとfastを使って、fastの方はn個だけ先に進めておいて、あとは同じスピードでfastがNoneになるまで移動すれば、slowがn番目にくる。
  • ただ、slowがn-1番目に来るようにしてあげるとslow.next = slow.next.next で飛ばして終わりなので、headの前に一つNodeを追加してそこから始める。

Problem

You are given the beginning of a linked list head, and an integer n.

Remove the nth node from the end of the list and return the beginning of the list.

Example 1:

Input: head = [1,2,3,4], n = 2

Output: [1,2,4]

Copy

Example 2:

Input: head = [5], n = 1

Output: []

Copy

Example 3:

Input: head = [1,2], n = 2

Output: [2]

Copy

Constraints:

  • The number of nodes in the list is sz.
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

Solution

# 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: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy = ListNode()
        dummy.next = head
        slow = dummy
        fast = head

        # fastをn個だけ先に進めておく
        while n > 0:
            fast = fast.next
            n -= 1
        
        # fastが最後に辿り着くまでslowも一緒に進める
        while fast:
            slow = slow.next
            fast = fast.next
        
        # slowが後ろからn番目の値の一つ前に来ているので、
        # n番目を飛ばす
        slow.next = slow.next.next

        # 改めてheadを返す
        return dummy.next


39. Copy List With Random Pointer

Link

https://neetcode.io/problems/copy-linked-list-with-random-pointer

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(N)

The personal need for review (1 ~ 10)

5

Memo

  • old -> copy のhashmapをまず作って、copy側のnextとrandomはこのhashmapを元に作成していくとシンプルに解ける
  • None -> None を最初に持っておくと処理がよりシンプルになる
  • 最初よく分からなかったけど一旦理解しちゃえば全体的にシンプル

Problem

You are given the head of a linked list of length n. Unlike a singly linked list, each node contains an additional pointer random, which may point to any node in the list, or null.

Create a deep copy of the list.

The deep copy should consist of exactly n new nodes, each including:

  • The original value val of the copied node
  • A next pointer to the new node corresponding to the next pointer of the original node
  • A random pointer to the new node corresponding to the random pointer of the original node

Note: None of the pointers in the new list should point to nodes in the original list.

Return the head of the copied linked list.

In the examples, the linked list is represented as a list of n nodes. Each node is represented as a pair of [val, random_index] where random_index is the index of the node (0-indexed) that the random pointer points to, or null if it does not point to any node.

Example 1:

Input: head = [[3,null],[7,3],[4,0],[5,1]]

Output: [[3,null],[7,3],[4,0],[5,1]]

Copy

Example 2:

Input: head = [[1,null],[2,2],[3,2]]

Output: [[1,null],[2,2],[3,2]]

Copy

Constraints:

  • 0 <= n <= 100
  • -100 <= Node.val <= 100
  • random is null or is pointing to some node in the linked list.

Solution

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        old_copy_map = { None: None }

        curr = head
        while curr:
            copy = Node(curr.val)
            old_copy_map[curr] = copy
            curr = curr.next
        
        curr = head
        while curr:
            copy = old_copy_map[curr]
            copy.next = old_copy_map[curr.next]
            copy.random = old_copy_map[curr.random]
            curr = curr.next
        
        return old_copy_map[head]


40. Add Two Numbers

Link

https://neetcode.io/problems/add-two-numbers

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(N)

The personal need for review (1 ~ 10)

5

Memo

  • 配列みたいにサイズがかっちり決まってない分LinkedListの方が扱いやすいと思える良問だと思う
  • l1 or l2 or carry でwhileを回すことでコードがかなりシンプルになるので大事

Problem

You are given the head of a linked list of length n. Unlike a singly linked list, each node contains an additional pointer random, which may point to any node in the list, or null.

Create a deep copy of the list.

The deep copy should consist of exactly n new nodes, each including:

  • The original value val of the copied node
  • A next pointer to the new node corresponding to the next pointer of the original node
  • A random pointer to the new node corresponding to the random pointer of the original node

Note: None of the pointers in the new list should point to nodes in the original list.

Return the head of the copied linked list.

In the examples, the linked list is represented as a list of n nodes. Each node is represented as a pair of [val, random_index] where random_index is the index of the node (0-indexed) that the random pointer points to, or null if it does not point to any node.

Example 1:

Input: head = [[3,null],[7,3],[4,0],[5,1]]

Output: [[3,null],[7,3],[4,0],[5,1]]

Copy

Example 2:

Input: head = [[1,null],[2,2],[3,2]]

Output: [[1,null],[2,2],[3,2]]

Copy

Constraints:

  • 0 <= n <= 100
  • -100 <= Node.val <= 100
  • random is null or is pointing to some node in the linked list.

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        ans = ListNode()
        dummy = ans
        carry = 0

        while l1 or l2 or carry:
            total = carry
            if l1:
                total += l1.val
                l1 = l1.next
            
            if l2:
                total += l2.val
                l2 = l2.next
            
            dummy.next = ListNode(total % 10)
            # totalが10を超えてたら1を次に引き継ぐ
            carry = total // 10
            
            dummy = dummy.next
        
        return ans.next


41. Linked List Cycle

Link

https://neetcode.io/problems/linked-list-cycle-detection

Difficulty (Easy, Medium, Hard)

Easy

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

3

Memo

  • hash_set 使えば一瞬で解ける。すでに追加したnodeがまた現れたらサイクルがあるのでtrueを返せばよく、Noneにそのままたどり着いたらfalseを返せば良い
  • ただ、slowポインターと1つだけ速く進むfastポインターを両方進めて、同じnodeに辿り着くことがあればサイクルがあると判定する、というロジックの方がよりLinkedListっぽいし、Space ComplexityもO(1)で済む。slow, fastを使う典型問題として良い。
  • もしサイクルがあった場合、slowとfastの差は1ずつ広がっていくので、いずれサイクルの中で必ず出会うことになる。図を書いてみるとよりわかりやすくなる。

Problem

Given the beginning of a linked list head, return true if there is a cycle in the linked list. Otherwise, return false.

There is a cycle in a linked list if at least one node in the list that can be visited again by following the next pointer.

Internally, index determines the index of the beginning of the cycle, if it exists. The tail node of the list will set it's next pointer to the index-th node. If index = -1, then the tail node points to null and no cycle exists.

Note: index is not given to you as a parameter.

Example 1:

Input: head = [1,2,3,4], index = 1

Output: true

Copy

Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).

Example 2:

Input: head = [1,2], index = -1

Output: false

Copy

Constraints:

  • 1 <= Length of the list <= 1000.
  • -1000 <= Node.val <= 1000
  • index is -1 or a valid index in the linked list.

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

            if slow == fast:
                return True
        
        return False


42. Find The Duplicate Number

Link

https://neetcode.io/problems/find-duplicate-integer

Related Links

https://neetcode.io/problems/linked-list-cycle-detection

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

4

Memo

  • これもhash_set 使えば一瞬で解ける。すでに追加した値が現れたらそれを返せば良い。
  • ただこれも、配列の中の数字がindexを指すと考えると、cycle detection の問題として考えることができる。
  • 基本的には前問と同じで Floyd’s Tortoise and Hare (Cycle Detection) アルゴリズムを使う。slowとfastがいずれ出会うのはわかりやすいけど、slow2ポインタとslowが重複する数字で出会う、というのが直感的にわかりづらい。数学的に証明できる。俺はもうこういうもんだと覚えてしまった。

Problem

Given the beginning of a linked list head, return true if there is a cycle in the linked list. Otherwise, return false.

There is a cycle in a linked list if at least one node in the list that can be visited again by following the next pointer.

Internally, index determines the index of the beginning of the cycle, if it exists. The tail node of the list will set it's next pointer to the index-th node. If index = -1, then the tail node points to null and no cycle exists.

Note: index is not given to you as a parameter.

Example 1:

Input: head = [1,2,3,4], index = 1

Output: true

Copy

Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).

Example 2:

Input: head = [1,2], index = -1

Output: false

Copy

Constraints:

  • 1 <= Length of the list <= 1000.
  • -1000 <= Node.val <= 1000
  • index is -1 or a valid index in the linked list.

Solution

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        slow, fast = 0, 0
        while True:
            slow = nums[slow]
            fast = nums[nums[fast]]

            if slow == fast:
                break
        
        slow2 = 0
        while True:
            slow = nums[slow]
            slow2 = nums[slow2]

            if slow == slow2:
                return slow2


43. LRU Cache

Link

https://neetcode.io/problems/lru-cache

Related Links

Difficulty (Easy, Medium, Hard)

Medium

Time Complexity

O(N)

Space Complexity

O(N)

The personal need for review (1 ~ 10)

8

Memo

  • 実装量が多いしMediumの中では難しめだと思う。バグりがち。よく出るっぽいので何度か書いておくと良さそう。
  • putの時にkeyに紐づいてるnodeがすでにある場合は置き換えなきゃいけないのを忘れがち(putは冪等性のある処理)
  • Doubly LinkedList を使って、左側にLRU(Least Recently Used: 最も使われてない値)を、右側に新しい値を格納するイメージで実装する。

Problem

Implement the Least Recently Used (LRU) cache class LRUCache. The class should support the following operations

  • LRUCache(int capacity) Initialize the LRU cache of size capacity.
  • int get(int key) Return the value cooresponding to the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the introduction of the new pair causes the cache to exceed its capacity, remove the least recently used key.

A key is considered used if a get or a put operation is called on it.

Ensure that get and put each run in O(1)O(1) average time complexity.

Example 1:

Input:
["LRUCache", [2], "put", [1, 10],  "get", [1], "put", [2, 20], "put", [3, 30], "get", [2], "get", [1]]

Output:
[null, null, 10, null, null, 20, -1]

Explanation:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 10);  // cache: {1=10}
lRUCache.get(1);      // return 10
lRUCache.put(2, 20);  // cache: {1=10, 2=20}
lRUCache.put(3, 30);  // cache: {2=20, 3=30}, key=1 was evicted
lRUCache.get(2);      // returns 20 
lRUCache.get(1);      // return -1 (not found)

Copy

Constraints:

  • 1 <= capacity <= 100
  • 0 <= key <= 1000
  • 0 <= value <= 1000

Solution

class ListNode:
    def __init__(self, key: int, val: int):
        self.key = key
        self.val = val
        self.prev = None
        self.nxt = None

class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {} # key -> node
        self.left = ListNode(0, 0)
        self.right = ListNode(0, 0)
        self.left.nxt = self.right
        self.right.prev = self.left

    def add(self, node: ListNode) -> None:
        prev, nxt = self.right.prev, self.right
        prev.nxt = node
        nxt.prev = node
        node.nxt = self.right
        node.prev = prev
    
    def remove(self, node: ListNode) -> None:
        prev, nxt = node.prev, node.nxt
        prev.nxt = nxt
        nxt.prev = prev

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        
        # remove→addすることで、cacheが使われた状態を更新する
        node = self.cache[key]
        self.remove(node)
        self.add(node)
        return node.val

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            # keyが存在する場合は元のkeyに紐づいたnodeをまず削除
            target_node = self.cache[key]
            self.remove(target_node)
        
        # nodeを新しく作成して追加
        new_node = ListNode(key, value)
        self.cache[key] = new_node
        self.add(new_node)
        
        if len(self.cache) > self.capacity:
            lru = self.left.nxt
            self.remove(lru)
            del self.cache[lru.key]


44. Merge K Sorted Linked Lists

Link

https://neetcode.io/problems/merge-k-sorted-linked-lists

Related Links

https://neetcode.io/problems/merge-two-sorted-linked-lists

Difficulty (Easy, Medium, Hard)

Hard

Time Complexity

O(N Log K)

Nはマージする対象のListNode二つの合計の長さ

Kは配列中のListNodeの数

Space Complexity

O(1)

再帰呼び出しのスタック領域のメモリは O(Log K)

The personal need for review (1 ~ 10)

8

Memo

  • 配列のマージソート知ってると馴染みのある処理だと思う
  • Easyでもやった二つのリストをマージする、という処理をK個やる感じ
  • listsの中のListNodeを二つずつ処理していく。バグらないように書くのがちょっと難しい

Problem

Implement the Least Recently Used (LRU) cache class LRUCache. The class should support the following operations

  • LRUCache(int capacity) Initialize the LRU cache of size capacity.
  • int get(int key) Return the value cooresponding to the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the introduction of the new pair causes the cache to exceed its capacity, remove the least recently used key.

A key is considered used if a get or a put operation is called on it.

Ensure that get and put each run in O(1)O(1) average time complexity.

Example 1:

Input:
["LRUCache", [2], "put", [1, 10],  "get", [1], "put", [2, 20], "put", [3, 30], "get", [2], "get", [1]]

Output:
[null, null, 10, null, null, 20, -1]

Explanation:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 10);  // cache: {1=10}
lRUCache.get(1);      // return 10
lRUCache.put(2, 20);  // cache: {1=10, 2=20}
lRUCache.put(3, 30);  // cache: {2=20, 3=30}, key=1 was evicted
lRUCache.get(2);      // returns 20 
lRUCache.get(1);      // return -1 (not found)

Copy

Constraints:

  • 1 <= capacity <= 100
  • 0 <= key <= 1000
  • 0 <= value <= 1000

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:    
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        if not lists:
            return None

        while len(lists) > 1:
            merged_list = []
            for i in range(0, len(lists), 2):
                l1 = lists[i]
                l2 = lists[i+1] if (i+1) < len(lists) else None
                merged = self.merge(l1, l2)
                merged_list.append(merged)
            lists = merged_list
        
        return lists[0]

    def merge(self, l1: ListNode, l2: ListNode) -> ListNode:
        ans = ListNode()
        dummy = ans
        while l1 and l2:
            if l1.val <= l2.val:
                dummy.next = l1
                l1 = l1.next
            else:
                dummy.next = l2
                l2 = l2.next
            dummy = dummy.next
        
        if l1:
            dummy.next = l1
        
        if l2:
            dummy.next = l2
        
        return ans.next


45. Reverse Nodes In K Group

Link

https://neetcode.io/problems/reverse-nodes-in-k-group

Related Links

https://neetcode.io/problems/reverse-a-linked-list

Difficulty (Easy, Medium, Hard)

Hard

Time Complexity

O(N)

Space Complexity

O(1)

The personal need for review (1 ~ 10)

9

Memo

  • これはまじで難しい。ここまでだと一番難しい。Linked List独特の処理が中々しっくりこない。

Problem

You are given the head of a singly linked list head and a positive integer k.

You must reverse the first k nodes in the linked list, and then reverse the next k nodes, and so on. If there are fewer than k nodes left, leave the nodes as they are.

Return the modified list after reversing the nodes in each group of k.

You are only allowed to modify the nodes' next pointers, not the values of the nodes.

Example 1:

Input: head = [1,2,3,4,5,6], k = 3

Output: [3,2,1,6,5,4]

Copy

Example 2:

Input: head = [1,2,3,4,5], k = 3

Output: [3,2,1,4,5]

Copy

Constraints:

  • The length of the linked list is n.
  • 1 <= k <= n <= 100
  • 0 <= Node.val <= 100

Solution

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        dummy = ListNode()
        dummy.next = head
        groupPrev = dummy

        while True:
            kthNode = self.findKthNode(groupPrev, k)
            if not kthNode:
                break

            nextGroupFirst = kthNode.next

            # 反転した後の先頭は次のグループの先頭
            # (単に反転する際はprev=Noneだった)
            prev = kthNode.next
            curr = groupPrev.next
            while curr != nextGroupFirst:
                tmp = curr.next
                curr.next = prev
                prev = curr
                curr = tmp
            
            tmp = groupPrev.next # 1をtmpに
            groupPrev.next = kthNode # 0→3に繋げる
            groupPrev = tmp # 1をgroupPrevに

        return dummy.next
    

    def findKthNode(self, node: ListNode, k: int) -> ListNode:
        while node and k > 0:
            k -= 1
            node = node.next
        
        return node

Toshimitsu Kugimoto

Software Engineer

仕事では決済やメディアのWebやスマホアプリのBE開発、WebのFE開発をやっています。 JavaとTypeScriptをよく使います。プライベートではFlutterでのアプリ開発にも挑戦中です。