from functools import reduce

from fractions import gcd

import math

import bisect

import itertools

import sys

sys.setrecursionlimit(10000000)

input = sys.stdin.readline

INF = float("inf")





def dfs(G, visited, v, cnt):

    visited[v] = True



    for nv in G[v]:

        if visited[nv]:

            continue

        cnt = dfs(G, visited, nv, cnt + 1)

    

    return cnt





# 処理内容

def main():

    N, M = list(map(int, input().split()))



    if M == 0:

        print((1))

        return



    P = set()

    for _ in range(M):

        a, b = list(map(int, input().split()))

        a -= 1; b -= 1

        if a > b:

            a, b = b, a

        P.add((a, b))



    G = [[] for _ in range(N)]

    for pair in P:

        a, b = pair

        G[a].append(b)

        G[b].append(a)



    visited = [False] * N

    ans = 1

    for i in range(N):

        if visited[i]:

            continue

        tmp = dfs(G, visited, i, 1)

        ans = max(ans, tmp)

    print(ans)





if __name__ == '__main__':

    main()
from functools import reduce

from fractions import gcd

import math

import bisect

import itertools

import sys

sys.setrecursionlimit(10000000)

input = sys.stdin.readline

INF = float("inf")





# 1-indexed

class UnionFind():

    # 作りたい要素数nで初期化

    # 使用するインスタンス変数の初期化

    def __init__(self, n):

        self.n = n

        # root[x]<0ならそのノードが根かつその値が木の要素数

        # rootノードでその木の要素数を記録する

        self.root = [-1]*(n+1)

        # 木をくっつける時にアンバランスにならないように調整する

        self.rnk = [0]*(n+1)



    # ノードxのrootノードを見つける

    def Find_Root(self, x):

        if(self.root[x] < 0):

            return x

        else:

            # ここで代入しておくことで、後の繰り返しを避ける

            self.root[x] = self.Find_Root(self.root[x])

            return self.root[x]



    # 木の併合、入力は併合したい各ノード

    def Unite(self, x, y):

        # 入力ノードのrootノードを見つける

        x = self.Find_Root(x)

        y = self.Find_Root(y)

        # すでに同じ木に属していた場合

        if(x == y):

            return 

        # 違う木に属していた場合rnkを見てくっつける方を決める

        elif(self.rnk[x] > self.rnk[y]):

            self.root[x] += self.root[y]

            self.root[y] = x



        else:

            self.root[y] += self.root[x]

            self.root[x] = y

            # rnkが同じ（深さに差がない場合）は1増やす

            if(self.rnk[x] == self.rnk[y]):

                self.rnk[y] += 1



    # xとyが同じグループに属するか判断

    def isSameGroup(self, x, y):

        return self.Find_Root(x) == self.Find_Root(y)



    # ノードxが属する木のサイズを返す

    def Count(self, x):

        return -self.root[self.Find_Root(x)]





# 処理内容

def main():

    N, M = list(map(int, input().split()))

    T = UnionFind(N)

    for _ in range(M):

        a, b = list(map(int, input().split()))

        T.Unite(a, b)

    

    ans = 0

    for i in range(1, N+1):

        ans = max(ans, T.Count(i))

    

    print(ans)





if __name__ == '__main__':

    main()
from functools import reduce
from fractions import gcd
import math
import bisect
import itertools
import sys

sys.setrecursionlimit(10000000)
input = sys.stdin.readline
INF = float("inf")


def dfs(G, visited, v, cnt):
    visited[v] = True
    for nv in G[v]:
        if visited[nv]:
            continue
        cnt = dfs(G, visited, nv, cnt + 1)
    return cnt


# 処理内容
def main():
    N, M = list(map(int, input().split()))
    if M == 0:
        print((1))
        return
    P = set()
    for _ in range(M):
        a, b = list(map(int, input().split()))
        a -= 1
        b -= 1
        if a > b:
            a, b = b, a
        P.add((a, b))
    G = [[] for _ in range(N)]
    for pair in P:
        a, b = pair
        G[a].append(b)
        G[b].append(a)
    visited = [False] * N
    ans = 1
    for i in range(N):
        if visited[i]:
            continue
        tmp = dfs(G, visited, i, 1)
        ans = max(ans, tmp)
    print(ans)


if __name__ == "__main__":
    main()

from functools import reduce
from fractions import gcd
import math
import bisect
import itertools
import sys

sys.setrecursionlimit(10000000)
input = sys.stdin.readline
INF = float("inf")
# 1-indexed
class UnionFind:
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        # root[x]<0ならそのノードが根かつその値が木の要素数
        # rootノードでその木の要素数を記録する
        self.root = [-1] * (n + 1)
        # 木をくっつける時にアンバランスにならないように調整する
        self.rnk = [0] * (n + 1)

    # ノードxのrootノードを見つける
    def Find_Root(self, x):
        if self.root[x] < 0:
            return x
        else:
            # ここで代入しておくことで、後の繰り返しを避ける
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]

    # 木の併合、入力は併合したい各ノード
    def Unite(self, x, y):
        # 入力ノードのrootノードを見つける
        x = self.Find_Root(x)
        y = self.Find_Root(y)
        # すでに同じ木に属していた場合
        if x == y:
            return
        # 違う木に属していた場合rnkを見てくっつける方を決める
        elif self.rnk[x] > self.rnk[y]:
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            # rnkが同じ（深さに差がない場合）は1増やす
            if self.rnk[x] == self.rnk[y]:
                self.rnk[y] += 1

    # xとyが同じグループに属するか判断
    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)

    # ノードxが属する木のサイズを返す
    def Count(self, x):
        return -self.root[self.Find_Root(x)]


# 処理内容
def main():
    N, M = list(map(int, input().split()))
    T = UnionFind(N)
    for _ in range(M):
        a, b = list(map(int, input().split()))
        T.Unite(a, b)
    ans = 0
    for i in range(1, N + 1):
        ans = max(ans, T.Count(i))
    print(ans)


if __name__ == "__main__":
    main()

+# 1-indexed +class UnionFind: +    # 作りたい要素数nで初期化 +    # 使用するインスタンス変数の初期化 +    def __init__(self, n): +        self.n = n +        # root[x]<0ならそのノードが根かつその値が木の要素数 +        # rootノードでその木の要素数を記録する +        self.root = [-1] * (n + 1) +        # 木をくっつける時にアンバランスにならないように調整する +        self.rnk = [0] * (n + 1) +    # ノードxのrootノードを見つける +    def Find_Root(self, x): +        if self.root[x] < 0: +            return x +        else: +            # ここで代入しておくことで、後の繰り返しを避ける +            self.root[x] = self.Find_Root(self.root[x]) +            return self.root[x] -def dfs(G, visited, v, cnt): -    visited[v] = True -    for nv in G[v]: -        if visited[nv]: -            continue -        cnt = dfs(G, visited, nv, cnt + 1) -    return cnt +    # 木の併合、入力は併合したい各ノード +    def Unite(self, x, y): +        # 入力ノードのrootノードを見つける +        x = self.Find_Root(x) +        y = self.Find_Root(y) +        # すでに同じ木に属していた場合 +        if x == y: +            return +        # 違う木に属していた場合rnkを見てくっつける方を決める +        elif self.rnk[x] > self.rnk[y]: +            self.root[x] += self.root[y] +            self.root[y] = x +        else: +            self.root[y] += self.root[x] +            self.root[x] = y +            # rnkが同じ（深さに差がない場合）は1増やす +            if self.rnk[x] == self.rnk[y]: +                self.rnk[y] += 1 + +    # xとyが同じグループに属するか判断 +    def isSameGroup(self, x, y): +        return self.Find_Root(x) == self.Find_Root(y) + +    # ノードxが属する木のサイズを返す +    def Count(self, x): +        return -self.root[self.Find_Root(x)] -    if M == 0: -        print((1)) -        return -    P = set() +    T = UnionFind(N) -        a -= 1 -        b -= 1 -        if a > b: -            a, b = b, a -        P.add((a, b)) -    G = [[] for _ in range(N)] -    for pair in P: -        a, b = pair -        G[a].append(b) -        G[b].append(a) -    visited = [False] * N -    ans = 1 -    for i in range(N): -        if visited[i]: -            continue -        tmp = dfs(G, visited, i, 1) -        ans = max(ans, tmp) +        T.Unite(a, b) +    ans = 0 +    for i in range(1, N + 1): +        ans = max(ans, T.Count(i))