Python で、連結リストの中心にあるノードを探します。
連結リストは、以下の実装を使います。
単純に考えると、先頭からポインタをたどり最後まで一度移動して連結リストの長さを取得し、その後その半分の位置まで進めば、中心に辿り着きます。
def get_middle_node(self): current_node = self.head size = 0 while current_node is not None: size += 1 current_node = current_node.next_node middle = size // 2 current_node = self.head for _ in range(middle): current_node = current_node.next_node return current_node.data
連結リストに要素数の属性を持たせノードの追加や削除のたびに増減させることで、要素数に\( O(1) \)でアクセスできるようになるので、ループが1回だけになりより速く実行できます。
def get_middle_node_use_length(self): middle = self.length // 2 current_node = self.head for _ in range(middle): current_node = current_node.next_node return current_node.data
ポインタを2つ使い、片方のポインタを倍の速さで進めると、速いポインタが最後に到達した時点で遅いポインタが中心にある、と考えることでも、ループを一回だけにすることができます。
def get_middle_node_use_two_pointers(self): slow_pointer = self.head fast_pointer = self.head while fast_pointer.next_node \ and fast_pointer.next_node.next_node: fast_pointer = fast_pointer.next_node.next_node slow_pointer = slow_pointer.next_node return slow_pointer.data
今回のコードをまとめます。
class Node(object): def __init__(self, data): self.data = data self.next_node = None class LinkedList(object): def __init__(self): self.head = None self.length = 0 # O(1) def insert_first(self, data): new_node = Node(data) self.length += 1 if not self.head: self.head = new_node else: new_node.next_node = self.head self.head = new_node # O(N) def __str__(self): elems = '' current_node = self.head while current_node: if current_node.next_node is None: elems += str(current_node.data) else: elems += str(current_node.data) + ', ' current_node = current_node.next_node return '[' + elems +']' # O(1) def __len__(self): return self.length # O(N) def insert_last(self, data): if self.head is None: self.insert_first(data) else: new_node = Node(data) self.length += 1 current_node = self.head while current_node.next_node is not None: current_node = current_node.next_node current_node.next_node = new_node # 追加 # O(N) def remove(self, data): if self.head is None: print('リストは空です。') return current_node = self.head previous_node = None while current_node.data != data: if current_node.next_node is None: print('削除に該当するデータがありません。') return previous_node = current_node current_node = current_node.next_node # 削除ノードが先頭 if previous_node is None: self.head = current_node.next_node self.length -= 1 else: previous_node.next_node = current_node.next_node self.length -= 1 def get_middle_node(self): current_node = self.head size = 0 while current_node is not None: size += 1 current_node = current_node.next_node middle = size // 2 current_node = self.head for _ in range(middle): current_node = current_node.next_node return current_node.data def get_middle_node_use_length(self): middle = self.length // 2 current_node = self.head for _ in range(middle): current_node = current_node.next_node return current_node.data def get_middle_node_use_two_pointers(self): slow_pointer = self.head fast_pointer = self.head while fast_pointer.next_node \ and fast_pointer.next_node.next_node: fast_pointer = fast_pointer.next_node.next_node slow_pointer = slow_pointer.next_node return slow_pointer.data if __name__ == '__main__': linked_list = LinkedList() linked_list.insert_first(1) linked_list.insert_first(2) linked_list.insert_first(3) linked_list.insert_first(4) linked_list.insert_first(5) linked_list.insert_first(6) linked_list.insert_first(7) # [7, 6, 5, 4, 3, 2, 1] print(linked_list) # 4 print(linked_list.get_middle_node()) # 4 print(linked_list.get_middle_node_use_length()) # 4 print(linked_list.get_middle_node_use_two_pointers())