CS/Algorithm

μœ„μƒ μ •λ ¬(topology sort)

deo2kim 2020. 10. 5. 20:05
λ°˜μ‘ν˜•

πŸ“” μœ„μƒ μ •λ ¬(topology sort) μ΄λž€

  • μˆœμ„œκ°€ μ •ν•΄μ Έ μžˆλŠ” μž‘μ—…μ„ μ°¨λ‘€λ‘œ μˆ˜ν–‰ν•΄μ•Ό ν•  λ•Œ κ·Έ μˆœμ„œλ₯Ό κ²°μ •ν•΄μ£ΌκΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜
  • μ–΄λ–€ 일을 ν•˜λŠ” μˆœμ„œλ₯Ό μ°ΎλŠ” μ•Œκ³ λ¦¬μ¦˜
    • λ°©ν–₯ κ·Έλž˜ν”„μ— μ‘΄μž¬ν•˜λŠ” 각 μ •μ λ“€μ˜ μ„ ν–‰ μˆœμ„œλ₯Ό μœ„λ°°ν•˜μ§€ μ•ŠμœΌλ©΄μ„œ λͺ¨λ“  정점을 λ‚˜μ—΄ν•˜λŠ” 것
  • μ„ ν–‰ 쑰건이 λ°˜λ“œμ‹œ μˆ˜ν–‰λ˜μ–΄μ•Ό λ‹€μŒμ„ 진행할 수 μžˆλ‹€!
  • μœ„μ˜ 그림을 보면 μ•„λΉ„ν„°νŠΈλ¦¬λ·°λ„μ„ 짓기 μœ„ν•΄μ„œλŠ” ν…œν”ŒλŸ¬μ•„μΉ΄μ΄λΈŒμ™€ μŠ€νƒ€κ²Œμ΄νŠΈκ°€ λ‘˜λ‹€ μžˆμ–΄μ•Ό ν•œλ‹€.
  • μ‹œκ°„λ³΅μž‘λ„: O(V+E) | V-λ…Έλ“œμ˜ 수, E-κ°„μ„ μ˜ 수

πŸ“” μœ„μƒ μ •λ ¬(topology sort) κ΅¬ν˜„

  • 큐와 μŠ€νƒμ„ μ‚¬μš©ν•  수 μžˆλ‹€. 일반적으둜 큐λ₯Ό μ‚¬μš©
  • μ§„μž… 차수( ν˜„μž¬ λ…Έλ“œμ— λ“€μ–΄μ˜¬ 수 μžˆλŠ” λ…Έλ“œμ˜ 수 ) κ°€ 0인 정점을 큐에 μ‚½μž… (그림의 μ‚¬μ΄λ²„λ„€ν‹±μŠ€μ½”μ–΄)
  • νμ—μ„œ μ›μ†Œλ₯Ό κΊΌλ‚΄ μ—°κ²°λœ κ°„μ„  제거 ( μ—°κ²°λœ κ°„μ„ μ˜ μ§„μž… 차수λ₯Ό 1μ”© λΉΌμ€€λ‹€ )
  • 이 ν›„ μ§„μž… μ°¨μˆ˜κ°€ 0이 됐닀면 ν•΄λ‹Ή λ…Έλ“œλ₯Ό 큐에 μ‚½μž…
  • μ—¬κΈ°μ„œ λ‘κ°€μ§€λ‘œ λ‚˜λ‰œλ‹€.
    • λͺ¨λ“  정점을 μ„ νƒν•˜κΈ°μ „μ— 큐가 λΉ„μ–΄μžˆλ‹€λ©΄ - 싸이클이 μ‘΄μž¬ν•œλ‹€ -> μœ„μƒ μ •λ ¬ λΆˆκ°€
    • λͺ¨λ“  μ •μ λ§ŒνΌ μˆ˜ν–‰ν–ˆλ‹€λ©΄ -> νμ—μ„œ κΊΌλ‚Έ μˆœμ„œκ°€ μœ„μƒ μ •λ ¬μ˜ κ²°κ³Όκ°€ λœλ‹€.
from collections import defaultdict
from collections import deque

V = 7
edges = [
    [1, 2],
    [1, 5],
    [2, 3],
    [3, 4],
    [5, 6],
    [4, 6],
    [6, 7],
]

# 인접 리슀트
adj = defaultdict(list)
for edge in edges:
    s, e = edge
    adj[s].append(e)

# print(adj)

# μœ„μƒ ( ν˜„μž¬ λ…Έλ“œ λ°”λ‘œ μ „ λ…Έλ“œκ°€ λͺ‡κ°œ 인지 ( λ‚˜λ₯Ό 지λͺ©ν•˜κ³  μžˆλŠ” λ…Έλ“œμ˜ 개수))
topo = [0] * (V + 1)
for key in adj:
    for node in adj[key]:
        topo[node] += 1

print(f'졜초 μ§„μž…μ°¨μˆ˜: {topo}')

# μ§„μž… μ°¨μˆ˜κ°€ 0인 애듀을 q에 λ„£μ–΄ μ€€λ‹€.
q = deque()
for i in range(1, V + 1):
    if topo[i] == 0:
        q.append(i)

result = []
# μœ„μƒ 정렬이 μ™„μ „νžˆ μˆ˜ν–‰λ˜λ €λ©΄ μ •ν™•νžˆ V개의 λ…Έλ“œλ₯Ό λ°©λ¬Έν•΄μ•Ό ν•œλ‹€.
for _ in range(V):
    if not q:
        print('ERROR: 사이클 λ°œμƒ!')
        break

    c = q.popleft()
    result.append(c)
    # c 와 μ—°κ²° λ˜μ–΄μžˆλŠ” 간선을 제거 (= μ§„μž…μ°¨μˆ˜λ₯Ό 1 λΊΈλ‹€)
    for naver in adj[c]:
        topo[naver] -= 1
        if topo[naver] == 0:
            q.append(naver)

print(f'μœ„μƒμ •λ ¬ κ²°κ³Ό: {result}')

πŸ“” μœ„μƒ μ •λ ¬(topology sort) νŠΉμ§•

  • μœ„μƒ μ •λ ¬μ˜ μˆœμ„œ(Topological Order)κ°€ μ—¬λŸ¬κ°€μ§€ λ‚˜μ˜¬ 수 μžˆλ‹€.
  • μœ„μƒ μ •λ ¬ 진행 쀑 남아 μžˆλŠ” 정점 μ€‘μ—μ„œ μ§„μž… μ°¨μˆ˜κ°€ 0인 정점이 μ—†λ‹€λ©΄ 사이클이 μ‘΄μž¬ν•˜λŠ” κ²ƒμœΌλ‘œ μœ„μƒμ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ„ μˆ˜ν–‰ν•  수 μ—†λ‹€.
  • DAG(Directed Acyclic Graph) μ—λ§Œ 적용 κ°€λŠ₯ (즉, λ°©ν–₯이 μžˆλŠ” κ·Έλž˜ν”„μ΄κ³ , 싸이클이 μ‘΄μž¬ν•˜λ©΄ μ•ˆλœλ‹€ )
λ°˜μ‘ν˜•