n,m = list(map(int,input().split()))

import sys

sys.setrecursionlimit(10**9)

rks = [None] *n

p = [set([]) for i in range(n)]

flist = [[] for i in range(n)]

for i in range(m):

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

    flist[a-1].append(b-1)

    flist[b-1].append(a-1)

def dfs(graph,w,c):

    if rks[w] is None:

        rks[w] = 0

    for i in range(len(graph[w])):

        if rks[graph[w][i]] is not None:

            continue

        p[c].add(flist[w][i])

        dfs(graph, graph[w][i],c)

c = -1

for w in range(n):

    c += 1

    if rks[w] is None:

        dfs(flist,w,c)

ans = 0

for i in range(n):

    ans = max(ans,len(p[i])+1)

print(ans)
n,m = list(map(int,input().split()))

par = []

for i in range(n):

    par.append(i)  #初期親

rank = [1 for i in range(n)] #初期ランク

cut = []

def find(n): #親検索andランク短縮

    global cut

    if par[n] == n:

        for i in range(len(cut)):

            par[cut[i]] = n

        cut = []

        return n

    else:

        cut.append(n)

        find(par[n])

        return find(par[n])

def shorten(n): # 根に直接接続する

    global cut

    if par[n] == n:

        for i in range(len(cut)):

            par[cut[i]] = n

        cut = []

    else:

        cut.append(n)

        shorten(par[n])

def unite(a,b): #グループ併合

    x = find(a)

    y = find(b) #根っこ同士をくっつける

    if x == y: 

        return    #既に同一ユニオンなら何もしない

    if rank[x] < rank[y]:

        par[x] = y 

    elif rank[x] == rank[y]:

        par[y] = x

        rank[x] += 1

    else:

        par[y] = x

def judge(a,b):

    return par[a] == par[b]

for i in range(m):

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

    a -= 1

    b -= 1

    unite(a,b)

for i in range(n):

    shorten(i)

import collections

c = collections.Counter(par)

ans = 0

for i in range(n):

    ans = max(ans,c[i])

print(ans)
n, m = list(map(int, input().split()))
import sys

sys.setrecursionlimit(10**9)
rks = [None] * n
p = [set([]) for i in range(n)]
flist = [[] for i in range(n)]
for i in range(m):
    a, b = list(map(int, input().split()))
    flist[a - 1].append(b - 1)
    flist[b - 1].append(a - 1)


def dfs(graph, w, c):
    if rks[w] is None:
        rks[w] = 0
    for i in range(len(graph[w])):
        if rks[graph[w][i]] is not None:
            continue
        p[c].add(flist[w][i])
        dfs(graph, graph[w][i], c)


c = -1
for w in range(n):
    c += 1
    if rks[w] is None:
        dfs(flist, w, c)
ans = 0
for i in range(n):
    ans = max(ans, len(p[i]) + 1)
print(ans)

n, m = list(map(int, input().split()))
par = []
for i in range(n):
    par.append(i)  # 初期親
rank = [1 for i in range(n)]  # 初期ランク
cut = []


def find(n):  # 親検索andランク短縮
    global cut
    if par[n] == n:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
        return n
    else:
        cut.append(n)
        find(par[n])
        return find(par[n])


def shorten(n):  # 根に直接接続する
    global cut
    if par[n] == n:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
    else:
        cut.append(n)
        shorten(par[n])


def unite(a, b):  # グループ併合
    x = find(a)
    y = find(b)  # 根っこ同士をくっつける
    if x == y:
        return  # 既に同一ユニオンなら何もしない
    if rank[x] < rank[y]:
        par[x] = y
    elif rank[x] == rank[y]:
        par[y] = x
        rank[x] += 1
    else:
        par[y] = x


def judge(a, b):
    return par[a] == par[b]


for i in range(m):
    a, b = list(map(int, input().split()))
    a -= 1
    b -= 1
    unite(a, b)
for i in range(n):
    shorten(i)
import collections

c = collections.Counter(par)
ans = 0
for i in range(n):
    ans = max(ans, c[i])
print(ans)

-import sys +par = [] +for i in range(n): +    par.append(i)  # 初期親 +rank = [1 for i in range(n)]  # 初期ランク +cut = [] -sys.setrecursionlimit(10**9) -rks = [None] * n -p = [set([]) for i in range(n)] -flist = [[] for i in range(n)] + +def find(n):  # 親検索andランク短縮 +    global cut +    if par[n] == n: +        for i in range(len(cut)): +            par[cut[i]] = n +        cut = [] +        return n +    else: +        cut.append(n) +        find(par[n]) +        return find(par[n]) + + +def shorten(n):  # 根に直接接続する +    global cut +    if par[n] == n: +        for i in range(len(cut)): +            par[cut[i]] = n +        cut = [] +    else: +        cut.append(n) +        shorten(par[n]) + + +def unite(a, b):  # グループ併合 +    x = find(a) +    y = find(b)  # 根っこ同士をくっつける +    if x == y: +        return  # 既に同一ユニオンなら何もしない +    if rank[x] < rank[y]: +        par[x] = y +    elif rank[x] == rank[y]: +        par[y] = x +        rank[x] += 1 +    else: +        par[y] = x + + +def judge(a, b): +    return par[a] == par[b] + + -    flist[a - 1].append(b - 1) -    flist[b - 1].append(a - 1) +    a -= 1 +    b -= 1 +    unite(a, b) +for i in range(n): +    shorten(i) +import collections - -def dfs(graph, w, c): -    if rks[w] is None: -        rks[w] = 0 -    for i in range(len(graph[w])): -        if rks[graph[w][i]] is not None: -            continue -        p[c].add(flist[w][i]) -        dfs(graph, graph[w][i], c) - - -c = -1 -for w in range(n): -    c += 1 -    if rks[w] is None: -        dfs(flist, w, c) +c = collections.Counter(par) -    ans = max(ans, len(p[i]) + 1) +    ans = max(ans, c[i])