카테고리 없음

[유레카 / 백엔드] TIL - 3(JAVA)

coding-quokka101 2025. 9. 18. 17:29

 

🚀 오늘의 학습 기록: 다익스트라 알고리즘 & 백트래킹

오늘은 알고리즘 공부 중에서도 핵심 탐색 기법인 **다익스트라(Dijkstra)**와 **백트래킹(Backtracking)**을 심도 있게 정리했다.
이 두 개념은 서로 다른 문제를 풀지만, 공통점이 많다.
우선 둘 다 **“탐색 대상의 공간을 효율적으로 살펴보며, 불필요한 경로를 줄인다”**는 원리를 공유한다.


오늘의 성취 

  • 다익스트라 알고리즘을 처음부터 구현하고, 우선순위 큐로 최적화까지 해냈다.
  • 백트래킹 문제(N-Queen, 빵집 문제)를 풀며 재귀 호출과 **가지치기(Pruning)**의 필요성을 체감했다.
  • 단순히 코드를 이해하는 것을 넘어, 흐름도와 호출 스택을 직접 그려보며 논리 구조를 시각화했다.

📌 처음엔 복잡했지만, 단계를 쪼개고 작은 예제부터 연습하니 문제 해결이 훨씬 쉬워졌다.


💡 다익스트라 알고리즘

  • 정의: 하나의 정점에서 출발해 모든 정점까지의 최단 거리를 구하는 알고리즘. 탐욕적(Greedy) 방식으로, **“가장 가까운 정점을 먼저 확정하고, 그 정점으로부터 다른 노드의 거리를 갱신”**한다.
  • 전제 조건: 간선의 가중치가 0 이상이어야 함.
  • 핵심 아이디어: “가장 가까운 정점부터 최단 거리 확정 → 인접 노드 거리 갱신”.
    1. 시작 정점의 거리를 0, 나머지는 무한대로 초기화.
    2. 아직 방문하지 않은 정점 중 최단 거리의 정점을 선택.
    3. 선택한 정점을 기준으로 인접 정점들의 거리를 갱신.
    4. 모든 정점이 확정될 때까지 반복.
  • 시간 복잡도 : 배열: O(V²) / 우선순위 큐: O(E log V)
  • 활용 예시 : 지도 앱 경로 탐색 / 지하철 환승 최적화 / 네트워크 라우팅(OSPF)
  • 메모리 설계 팁
    • 인접 리스트(List<List<Node>>) 구조가 일반적.
    • Integer.MAX_VALUE를 INF로 쓸 때는 덧셈 시 오버플로우를 조심

출처 : [시선]최단 경로 찾기, 다익스트라(Dijkstra) 알고리즘 < 여론칼럼 < 기사본문 - 연세춘추

 

 

다익스트라 동작 원리 

 

 

 

 

  • 정점 (Vertices): A, B, C, D, E, F
  • 간선 (Edges): A-B(4), A-C(1), A-D(2), B-D(5), C-D(2), C-E(3), D-F(2), E-F(3)

 

 

 

 

 

 

 

 

초기화 단계 

 

 

  • 시작 정점 A의 거리는 0으로 설정하고, 나머지 정점의 거리는 무한대로 설정합니다.
  • 초기 거리 배열: {A:0,B:∞,C:∞,D:∞,E:∞,F:∞}
  • 방문하지 않은 정점 집합: {A,B,C,D,E,F}

 

 

 

 

 

 

 

 

 

 

  • 현재 방문하지 않은 정점 중 가장 거리가 짧은 정점 A를 선택합니다.
  • 정점 A에서 갈 수 있는 모든 인접한 정점의 거리를 업데이트합니다:
    • A-B: 0 + 4 = 4
    • A-C: 0 + 1 = 1
    • A-D: 0 + 2 = 2
  • 업데이트 후 거리 배열: {A:0,B:4,C:1,D:2,E:∞,F:∞}
  • 정점 A를 방문한 것으로 표시하고 방문하지 않은 정점 집합에서 제거: {B,C,D,E,F}
  •  

 

 

 

    • 현재 방문하지 않은 정점 중 가장 거리가 짧은 정점 C를 선택합니다.
    • 정점 C에서 갈 수 있는 모든 인접한 정점의 거리를 업데이트합니다:
      • C-D: 1 + 2 = 3 (기존의 D까지의 거리 2보다 크므로 업데이트하지 않음)
      • C-E: 1 + 3 = 4
    • 업데이트 후 거리 배열: {A:0,B:4,C:1,D:2,E:4,F:∞}
    • 정점 C를 방문한 것으로 표시하고 방문하지 않은 정점 집합에서 제거: {B,D,E,F}

 

 

 

 

 

 

 

 

  • 재 방문하지 않은 정점 중 가장 거리가 짧은 정점 D를 선택합니다.
  • 정점 D에서 갈 수 있는 모든 인접한 정점의 거리를 업데이트합니다:
    • D-F: 2 + 2 = 4
  • 업데이트 후 거리 배열: {A:0,B:4,C:1,D:2,E:4,F:4}
  • 정점 D를 방문한 것으로 표시하고 방문하지 않은 정점 집합에서 제거: {B,E,F}
  •  

 

 

 

 

 

 

 

 

 

 

  • 현재 방문하지 않은 정점 중 가장 거리가 짧은 정점 B를 선택합니다.
  • 정점 B에서 갈 수 있는 모든 인접한 정점의 거리를 업데이트합니다:
    • B-D: 4 + 5 = 9 (기존의 D까지의 거리 2보다 크므로 업데이트하지 않음)
  • 업데이트 후 거리 배열: {A:0,B:4,C:1,D:2,E:4,F:4}
  • 정점 B를 방문한 것으로 표시하고 방문하지 않은 정점 집합에서 제거: {E,F}
  •  

 

 

 

 

 

    • 마지막으로 방문하지 않은 정점 F를 선택합니다.
    • 정점 F에서 갈 수 있는 모든 인접한 정점의 거리를 업데이트합니다 (이미 모든 거리가 최단 거리로 업데이트됨).
    • 정점 F를 방문한 것으로 표시하고 방문하지 않은 정점 집합에서 제거: {}

최종 결과: 정점 A로부터 각 정점까지의 최단 거리는 다음과 같습니다:

    • A: 0 B: 4 C: 1 D: 2 E: 4 F: 4

 


 

💡 백트래킹

  • 정의: 가능한 해의 공간을 깊이 우선 탐색(DFS)하며, 조건을 위배하면 즉시 탐색을 중단하고 되돌아가는 방식.
    • 모든 해를 탐색하지만, 불필요한 경로를 일찍 끊어서 효율을 높이는 알고리즘
  • 특징: “선택 → 조건 확인 → 위배 시 Backtrack → 조건 만족 시 다음 단계”
    • 완전 탐색을 기반으로, “조건을 위배하는 경로는 더 이상 탐색하지 않는다”는 가지치기(pruning)를 적용하는 기법
  • 아이디어
    1. 현재 상태에서 선택 가능한 모든 경우를 시도.
    2. 조건을 만족하지 않으면 더 깊이 탐색하지 않고 되돌아감 (Backtrack).
    3. 조건을 만족하는 해를 찾으면 저장하거나 출력.
  • 예시 : 순열, 조합, N-Queen, 미로 탐색, 부분집합 문제 등에서 쓰임
  • 장점: 완전 탐색보다 훨씬 효율적, 해가 여러 개인 문제를 전부 탐색 가능.
  • 단점: 최악의 경우 여전히 경우의 수가 많아질 수 있음. 

📌 백트래킹과 비슷한 기법과 차이

  • DFS: 모든 경로 탐색 (가지치기 없음)
  • DP: 부분 문제의 결과를 저장해서 효율적으로 해결
  • Greedy: 매 단계 최적 선택
  • Backtracking: 모든 경우의 수 탐색 + 불가능한 경우 빠르게 포기

 

🌳 백트래킹의 기본 흐름  

  1. 문제 정의 & 상태 표현
    • 탐색해야 하는 문제를 “트리(혹은 그래프)의 상태 공간”으로 표현
    • 각 노드는 현재까지의 선택(부분 해)을 의미
  2. 종료 조건(Base Case) 확인
    • 현재 상태가 문제의 해(목표) 조건을 만족하면 → 결과를 기록
    • 더 이상 진행할 필요가 없으면(범위 벗어남, 조건 위배) → 탐색 중단
  3. 후보군 탐색 (DFS)
    • 현재 상태에서 선택할 수 있는 모든 “후보(move)”를 순회
    • 각 후보를 선택 → 상태를 갱신 → 재귀 호출(깊이 탐색)
  4. Pruning (가지치기)
    • 중간에 “이 선택은 해답이 될 수 없음”이 확실하면, 더 깊이 탐색하지 않고 돌아감
    • 예: N-Queen에서 이미 같은 열/대각선에 퀸이 있으면 바로 종료
  5. 복원 (Backtrack)
    • 재귀가 끝난 후, 이전 상태로 돌아가 다음 후보를 탐색
    • 이렇게 해서 모든 가능성을 확인

🧩 간단 예시 (부분 순열)

static int N = 3, M = 2;
static int[] result = new int[M];
static boolean[] used = new boolean[N + 1];

static void backtrack(int depth) {
    if (depth == M) {
        System.out.println(Arrays.toString(result));
        return;
    }
    for (int i = 1; i <= N; i++) {
        if (!used[i]) {
            used[i] = true;
            result[depth] = i;
            backtrack(depth + 1);
            used[i] = false; // Backtrack
        }
    }
}

 

 


⚡ 알고리즘 비교

알고리즘 탐색 방식 장점 단점
다익스트라 탐욕적(Greedy) + 우선순위 큐 가중치 있는 그래프의 최단 경로 음수 가중치 불가
BFS 레벨 탐색 단순 구현, 무가중치 최단 거리 가중치가 있으면 부적합
백트래킹 DFS 기반 불필요한 탐색을 줄임 최악의 경우 여전히 지수적 탐색

 


📈 시간 복잡도 분석

  • 다익스트라: O(E log V) (우선순위 큐 사용)
  • 백트래킹(N-Queen): 최악은 O(N!), 하지만 Pruning으로 평균 탐색 시간은 크게 줄어든다.
출처 : https://velog.velcdn.com/images/jieun_han/post/3e42f241-434e-4870-af70-f4b926de7c53/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202022-02-28%20%EC%98%A4%EC%A0%84%2010.01.02.png

💡 실생활 응용

  • 다익스트라
    • 내비게이션 경로 탐색
    • 물류 배송 최적화
    • 인터넷 라우팅
  • 백트래킹
    • 스도쿠 퍼즐
    • 미로 문제 해결
    • AI 게임 봇의 상태 탐색
출처 : https://img.freepik.com/premium-vector/sudoku-kids-adult-mathematical-mosaic-magic-square-logic-puzzle-game-digital-rebus-vector-illustration_355904-1393.jpg

🔍 디버깅 팁

  • 다익스트라
    • INF + 거리에서 오버플로우 방지 → long 타입 고려
    • 이미 방문한 노드의 거리보다 크면 무시.
  • 백트래킹
    • 방문 여부 배열을 정확히 관리해야 한다.
    • 재귀 종료 후 상태를 원상복구(Backtrack)하지 않으면 경로가 꼬인다.

 


 📝 연습 문제 추천

  • 다익스트라
    • BOJ 1753(최단 경로)
    • BOJ 1916(최소 비용 구하기)
  • 백트래킹
    • BOJ 9663(N-Queen)
    • BOJ 2580(스도쿠)
    • BOJ 15649(순열)

📌 간단한 용어 정리

  • 그래프(Graph): 정점(Vertex)과 간선(Edge)으로 이루어진 자료구조
  • Pruning(가지치기): 탐색 중 불필요한 경로를 미리 제거
  • 우선순위 큐(Priority Queue): 항상 가장 작은(또는 큰) 값을 효율적으로 꺼낼 수 있는 자료구조
  • 재귀 호출(Recursion): 함수가 자기 자신을 호출해 문제를 단계적으로 해결

🌟 복습 팁

“알고리즘은 단순한 코드가 아니라 사고방식을 훈련하는 도구다.
작은 예제에서 출발해 흐름도를 그려보고, 문제의 본질을 이해하려는 습관이 중요하다.”