Graph

🍵 그래프 표현

인접 리스트로 그래프 표현

  • 인접 리스트는 그래프를 표현하는 가장 효율적인 방법 중 하나로, 각 노드에 연결된 이웃 노드를 리스트로 저장합니다.

// 무방향 그래프
List<List<Integer>> graph = new ArrayList<>();

// n개의 노드를 가진 그래프 초기화
int n = 5;
for (int i = 0; i < n; i++) {
    graph.add(new ArrayList<>());
}

// 간선 추가 (노드 1과 2가 연결)
graph.get(1).add(2);
graph.get(2).add(1);

인접 행렬로 그래프 표현

  • 인접 행렬은 노드 간의 연결 상태를 2차원 배열로 나타냅니다.

// n개의 노드를 가진 그래프 초기화
int n = 5;
int[][] graph = new int[n][n];

// 간선 추가 (노드 1과 2가 연결)
graph[1][2] = 1;
graph[2][1] = 1;

  • 재귀를 사용한 DFS 구현:

void dfs(int node, boolean[] visited, List<List<Integer>> graph) {
    visited[node] = true;  // 현재 노드 방문 처리
    System.out.println("Visited: " + node);

    // 현재 노드에 연결된 다른 노드들을 재귀적으로 방문
    for (int neighbor : graph.get(node)) {
        if (!visited[neighbor]) {
            dfs(neighbor, visited, graph);
        }
    }
}
  • DFS 호출 예시:

int n = 5;  // 노드 수
boolean[] visited = new boolean[n];
dfs(0, visited, graph);  // 0번 노드부터 탐색 시작

  • 큐를 사용한 BFS 구현:

void bfs(int start, List<List<Integer>> graph) {
    Queue<Integer> queue = new LinkedList<>();
    boolean[] visited = new boolean[graph.size()];

    queue.add(start);
    visited[start] = true;

    while (!queue.isEmpty()) {
        int node = queue.poll();
        System.out.println("Visited: " + node);

        for (int neighbor : graph.get(node)) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                queue.add(neighbor);
            }
        }
    }
}

🍵 사이클 찾기 (Cycle Detection in Graphs)

DFS를 사용한 사이클 찾기 (무방향 그래프)

boolean hasCycle(int node, int parent, boolean[] visited, List<List<Integer>> graph) {
    visited[node] = true;

    for (int neighbor : graph.get(node)) {
        if (!visited[neighbor]) {
            if (hasCycle(neighbor, node, visited, graph)) {
                return true;
            }
        } else if (neighbor != parent) {
            return true;
        }
    }

    return false;
}

🍵 최단 경로 (Shortest Path), Dijkstra

다익스트라 알고리즘 (Dijkstra's Algorithm)

  • 우선순위 큐를 이용한 최단 경로 탐색:

public int[] dijkstra(int start, List<List<int[]>> graph, int n) {
    int[] dist = new int[n];
    Arrays.fill(dist, Integer.MAX_VALUE);
    dist[start] = 0;

    PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
    pq.add(new int[] {start, 0});

    while (!pq.isEmpty()) {
        int[] current = pq.poll();
        int node = current[0];
        int distance = current[1];

        if (distance > dist[node]) continue;

        for (int[] neighbor : graph.get(node)) {
            int nextNode = neighbor[0];
            int weight = neighbor[1];

            if (dist[node] + weight < dist[nextNode]) {
                dist[nextNode] = dist[node] + weight;
                pq.add(new int[] {nextNode, dist[nextNode]});
            }
        }
    }

    return dist;
}

🍵 위상 정렬 (Topological Sorting)

  • BFS를 이용한 위상 정렬:

void topologicalSort(List<List<Integer>> graph, int n) {
    int[] indegree = new int[n];
    for (int i = 0; i < n; i++) {
        for (int neighbor : graph.get(i)) {
            indegree[neighbor]++;
        }
    }

    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        if (indegree[i] == 0) {
            queue.add(i);
        }
    }

    List<Integer> topoOrder = new ArrayList<>();
    while (!queue.isEmpty()) {
        int node = queue.poll();
        topoOrder.add(node);

        for (int neighbor : graph.get(node)) {
            indegree[neighbor]--;
            if (indegree[neighbor] == 0) {
                queue.add(neighbor);
            }
        }
    }

    // 위상 정렬 결과 출력
    for (int node : topoOrder) {
        System.out.print(node + " ");
    }
}

🍵 최소 신장 트리 (Minimum Spanning Tree)

크루스칼 알고리즘 (Kruskal's Algorithm)

  • Union-Find를 이용한 크루스칼 알고리즘 구현:

class Edge {
    int src, dest, weight;
    Edge(int src, int dest, int weight) {
        this.src = src;
        this.dest = dest;
        this.weight = weight;
    }
}

int find(int[] parent, int i) {
    if (parent[i] == i) {
        return i;
    }
    return parent[i] = find(parent, parent[i]);
}

void union(int[] parent, int[] rank, int x, int y) {
    int rootX = find(parent, x);
    int rootY = find(parent, y);
    if (rank[rootX] > rank[rootY]) {
        parent[rootY] = rootX;
    } else if (rank[rootX] < rank[rootY]) {
        parent[rootX] = rootY;
    } else {
        parent[rootY] = rootX;
        rank[rootX]++;
    }
}

void kruskal(List<Edge> edges, int n) {
    Collections.sort(edges, (a, b) -> a.weight - b.weight);
    int[] parent = new int[n];
    int[] rank = new int[n];
    for (int i = 0; i < n; i++) {
        parent[i] = i;
        rank[i] = 0;
    }

    List<Edge> mst = new ArrayList<>();
    for (Edge edge : edges) {
        int root1 = find(parent, edge.src);
        int root2 = find(parent, edge.dest);
        if (root1 != root2) {
            mst.add(edge);
            union(parent, rank, root1, root2);
        }
    }

    // MST 결과 출력
    for (Edge edge : mst) {
        System.out.println(edge.src + " -- " + edge.dest + " == " + edge.weight);
    }
}

🍵 플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm)

  • 모든 쌍 최단 경로 탐색:

void floydWarshall(int[][] graph, int n) {
    int[][] dist = new int[n][n];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            dist[i][j] = graph[i][j];
        }
    }

    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE
                        && dist[i][k] + dist[k][j] < dist[i][j]) {
                    dist[i][j] = dist[i][k] + dist[k][j];
                }
            }
        }
    }

    // 최단 경로 결과 출력
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (dist[i][j] == Integer.MAX_VALUE) {
                System.out.print("INF ");
            } else {
                System.out.print(dist[i][j] + " ");
            }
        }
        System.out.println();
    }
}

Last updated