Buggy python submission (verdict: Runtime Error):
class Vertex(object):
    def __init__(self, id):
        self.id = id
        self.adjacent = {}
        # DFS
        self.visited = False
    def add_neighbor(self, neighbor, weight=0):
        self.adjacent[neighbor] = weight
    
    def get_neighbors(self):
        return self.adjacent
    def get_connections(self):
        return self.adjacent.keys()  
    def get_vertex_id(self):
        return self.id
    def get_weight(self, neighbor):
        return self.adjacent[neighbor]
    # DFS
    def set_visited(self):
        self.visited = True
class Graph(object):
    def __init__(self):
        self.vertex_dict = {}
        self.num_vertex = 0
    
    # DFS ループで扱いやすくする。
    def __iter__(self):
        return iter(self.vertex_dict.values())
    def add_vertex(self, id):
        self.num_vertex = self.num_vertex + 1
        new_vertex = Vertex(id)
        self.vertex_dict[id] = new_vertex
        return new_vertex
    def get_vertex(self, id):
        if id in self.vertex_dict:
            return self.vertex_dict[id]
        else:
            return None
    def add_edge(self, frm, to, weight=0):
        if frm not in self.vertex_dict:
            self.add_vertex(frm)
        if to not in self.vertex_dict:
            self.add_vertex(to)
        self.vertex_dict[frm].add_neighbor(self.vertex_dict[to], weight)
        # 有向グラフの場合は別
        self.vertex_dict[to].add_neighbor(self.vertex_dict[frm], weight)
    def get_vertices(self):
        return self.vertex_dict.keys()
    def get_edges(self):
        edges = []
        for v in self.vertex_dict.values():
            for w in v.get_connections():
                vid = v.get_vertex_id()
                wid = w.get_vertex_id()
                edges.append((vid, wid, v.get_weight(w)))
        return edges
def dfs(graph, current_vertex, traversal_verticies):
    current_vertex.set_visited()
    traversal_verticies.append(current_vertex.get_vertex_id())
    for neighbor in current_vertex.get_connections(): 
        if not(neighbor.visited):  
            dfs(graph, neighbor, traversal_verticies) 
 
def dfs_traversal(graph):
    n = 0
    for current_vertex in graph:
        traversal_verticies = []
        if not(current_vertex.visited):  
            dfs(graph, current_vertex, traversal_verticies) 
        if traversal_verticies:
            n += 1
    
    print(n - 1)
    
if __name__ == '__main__':
    graph = Graph()
    N, M = (int(i) for i in input().split())
    for i in range(N):
        graph.add_vertex(i+1)
    
    for i in range(M):
        a, b = (int(i) for i in input().split())
        graph.add_edge(a, b)
        
    dfs_traversal(graph)


Fixed submission:
# https://qiita.com/takayg1/items/7008e4c9584e42ae13c7

from collections import deque

N, M = (int(i) for i in input().split())

graph = [deque([]) for _ in range(N + 1)]
for _ in range(M):
    a, b = (int(i) for i in input().split())
    graph[a].append(b)
    graph[b].append(a)


seen = [-1] * (N + 1) 

def dfs(v):
    stack = [v]
    while stack:
        v = stack[-1]
        if graph[v]:
            w = graph[v].popleft()
            if seen[w] < 0:
                seen[w] = 0
                stack.append(w) 
        else:
            stack.pop()

ans = 0
for i in range(N):
    if seen[i + 1] < 0:
        dfs(i + 1)
        ans += 1

print(ans - 1)