IT 프로그래밍/알고리즘

[알고리즘] 그래프

기술1 2025. 6. 25. 15:40
// chap06_graph 전체 구현 + 입력 방식 2종류 (직접 addEdge vs 파일 입력)
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <limits>
#include <cstring>
#include <fstream>
using namespace std;

const int INF = 1e9; // 무한대 값 정의

//--------------------------------------
// 1. 인접행렬 표현
class GraphMatrix {
public:
    int n; // 정점 수
    vector<vector<int>> adj;

    GraphMatrix(int size) : n(size), adj(n, vector<int>(n, 0)) {}

    void addEdge(int u, int v) {
        adj[u][v] = 1; // 방향 그래프
        adj[v][u] = 1; // 무방향이면 이 줄 유지
    }
};

//--------------------------------------
// 2. 인접리스트 표현
class GraphList {
public:
    int n;
    vector<vector<int>> adj;

    GraphList(int size) : n(size), adj(n) {}

    void addEdge(int u, int v, bool directed = false) {
        adj[u].push_back(v);
        if (!directed)
            adj[v].push_back(u);
    }

    // 파일로부터 에지 추가: 파일 형식 "정점수 에지수" 후 "u v" 형식의 간선 나열
    void loadFromFile(const string& filename, bool directed = false) {
        ifstream in(filename);
        if (!in) {
            cerr << "파일 열기 실패: " << filename << endl;
            return;
        }
        int u, v, nodeCount, edgeCount;
        in >> nodeCount >> edgeCount;
        n = nodeCount;
        adj.assign(n, vector<int>());
        while (in >> u >> v) {
            addEdge(u, v, directed);
        }
        in.close();
    }
};

//--------------------------------------
// 3. 가중치 그래프 인접행렬 표현
class WeightedGraphMatrix {
public:
    int n;
    vector<vector<int>> weight;

    WeightedGraphMatrix(int size) : n(size), weight(n, vector<int>(n, INF)) {
        for (int i = 0; i < n; ++i)
            weight[i][i] = 0; // 자기 자신으로 가는 비용은 0
    }

    void addEdge(int u, int v, int w) {
        weight[u][v] = w; // 방향 그래프
        // weight[v][u] = w; // 무방향이면 포함
    }
};

//--------------------------------------
// 4. BFS (너비우선탐색)
void BFS(const vector<vector<int>>& adj, int start) {
    int n = adj.size();
    vector<bool> visited(n, false);
    queue<int> q;

    q.push(start);
    visited[start] = true;

    cout << "BFS 순회: ";
    while (!q.empty()) {
        int cur = q.front(); q.pop();
        cout << cur << " ";
        for (int next : adj[cur]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
    cout << "\n";
}

//--------------------------------------
// 5. DFS (재귀)
void DFS_recursive(const vector<vector<int>>& adj, int v, vector<bool>& visited) {
    visited[v] = true;
    cout << v << " ";
    for (int next : adj[v]) {
        if (!visited[next])
            DFS_recursive(adj, next, visited);
    }
}

void DFS(const vector<vector<int>>& adj, int start) {
    int n = adj.size();
    vector<bool> visited(n, false);
    cout << "DFS 순회: ";
    DFS_recursive(adj, start, visited);
    cout << "\n";
}

//--------------------------------------
// 6. 연결 요소 개수 구하기 (DFS)
int countConnectedComponents(const vector<vector<int>>& adj) {
    int n = adj.size(), count = 0;
    vector<bool> visited(n, false);
    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            DFS_recursive(adj, i, visited);
            count++;
        }
    }
    return count;
}

//--------------------------------------
// 7. 두 노드 간 경로 존재 여부 (BFS)
bool pathExists(const vector<vector<int>>& adj, int u, int v) {
    int n = adj.size();
    vector<bool> visited(n, false);
    queue<int> q;
    q.push(u);
    visited[u] = true;
    while (!q.empty()) {
        int cur = q.front(); q.pop();
        if (cur == v) return true;
        for (int next : adj[cur]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
    return false;
}

//--------------------------------------
// main 함수 예시 실행
int main() {
    // 방법 1: 수동 addEdge로 그래프 생성
    GraphList g(8);
    g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 3);
    g.addEdge(2, 4); g.addEdge(3, 5); g.addEdge(4, 5);

    BFS(g.adj, 0);
    DFS(g.adj, 0);

    cout << "연결 요소 수: " << countConnectedComponents(g.adj) << "\n";
    cout << "0과 5 사이 경로 존재? " << (pathExists(g.adj, 0, 5) ? "Yes" : "No") << "\n";

    // 방법 2: 파일로부터 그래프 입력 (예: input.txt)
    GraphList g2(0);
    g2.loadFromFile("graph_input.txt");
    BFS(g2.adj, 0);
    DFS(g2.adj, 0);

    return 0;
}

 

이제 각각의 코드를 구현하면

 

// chap06_graph 전체 구현 + 입력 방식 2종류 (직접 addEdge vs 파일 입력)
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <limits>
#include <cstring>
#include <fstream>
using namespace std;

const int INF = 1e9; // 무한대 값 정의

//--------------------------------------
// 1. 인접행렬 표현
// 사용 예: GraphMatrix mat(6); mat.addEdge(0,1); mat.printMatrix();
class GraphMatrix {
public:
    int n; // 정점 수
    vector<vector<int>> adj;

    GraphMatrix(int size) : n(size), adj(n, vector<int>(n, 0)) {}

    void addEdge(int u, int v) {
        adj[u][v] = 1; // 방향 그래프
        adj[v][u] = 1; // 무방향이면 이 줄 유지
    }

    void printMatrix() {
        cout << "\n[인접행렬] 무방향 그래프 출력:\n";
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                cout << adj[i][j] << " ";
            }
            cout << "\n";
        }
    }
};

//--------------------------------------
// 2. 인접리스트 표현 + 파일 입력 기능
// 사용 예:
//  - 수동: g.addEdge(0,1);
//  - 파일: g.loadFromFile("graph_input.txt");
class GraphList {
public:
    int n;
    vector<vector<int>> adj;

    GraphList(int size) : n(size), adj(n) {}

    void addEdge(int u, int v, bool directed = false) {
        adj[u].push_back(v);
        if (!directed)
            adj[v].push_back(u);
    }

    // 파일 형식: 첫 줄 "정점수 간선수", 이후 각 줄 "u v"
    void loadFromFile(const string& filename, bool directed = false) {
        ifstream in(filename);
        if (!in) {
            cerr << "파일 열기 실패: " << filename << endl;
            return;
        }
        int u, v, nodeCount, edgeCount;
        in >> nodeCount >> edgeCount;
        n = nodeCount;
        adj.assign(n, vector<int>());
        while (in >> u >> v) {
            addEdge(u, v, directed);
        }
        in.close();
    }
};

//--------------------------------------
// 3. 가중치 그래프 인접행렬 표현
// 사용 예: WeightedGraphMatrix wg(4); wg.addEdge(0,1,5);
class WeightedGraphMatrix {
public:
    int n;
    vector<vector<int>> weight;

    WeightedGraphMatrix(int size) : n(size), weight(n, vector<int>(n, INF)) {
        for (int i = 0; i < n; ++i)
            weight[i][i] = 0; // 자기 자신으로 가는 비용은 0
    }

    void addEdge(int u, int v, int w) {
        weight[u][v] = w;
        // 무방향 그래프의 경우 다음 줄도 포함:
        // weight[v][u] = w;
    }
};

//--------------------------------------
// 4. BFS: 너비우선탐색
// 실행 예: BFS(g.adj, 0);
void BFS(const vector<vector<int>>& adj, int start) {
    int n = adj.size();
    vector<bool> visited(n, false);
    queue<int> q;

    q.push(start);
    visited[start] = true;

    cout << "BFS 순회 (시작 노드: " << start << "): ";
    while (!q.empty()) {
        int cur = q.front(); q.pop();
        cout << cur << " ";
        for (int next : adj[cur]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
    cout << "\n";
}

//--------------------------------------
// 5. DFS: 깊이우선탐색
// 실행 예: DFS(g.adj, 0);
void DFS_recursive(const vector<vector<int>>& adj, int v, vector<bool>& visited) {
    visited[v] = true;
    cout << v << " ";
    for (int next : adj[v]) {
        if (!visited[next])
            DFS_recursive(adj, next, visited);
    }
}

void DFS(const vector<vector<int>>& adj, int start) {
    int n = adj.size();
    vector<bool> visited(n, false);
    cout << "DFS 순회 (시작 노드: " << start << "): ";
    DFS_recursive(adj, start, visited);
    cout << "\n";
}

//--------------------------------------
// 6. 연결 요소 개수 구하기
// 실행 예: int components = countConnectedComponents(g.adj);
int countConnectedComponents(const vector<vector<int>>& adj) {
    int n = adj.size(), count = 0;
    vector<bool> visited(n, false);
    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            DFS_recursive(adj, i, visited);
            count++;
        }
    }
    return count;
}

//--------------------------------------
// 7. 두 노드 간 경로 존재 여부
// 실행 예: bool found = pathExists(g.adj, 0, 5);
bool pathExists(const vector<vector<int>>& adj, int u, int v) {
    int n = adj.size();
    vector<bool> visited(n, false);
    queue<int> q;
    q.push(u);
    visited[u] = true;
    while (!q.empty()) {
        int cur = q.front(); q.pop();
        if (cur == v) return true;
        for (int next : adj[cur]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
    return false;
}

//--------------------------------------
// main 함수 예시 실행
int main() {
    // [1] 인접행렬 예제 실행
    GraphMatrix mat(6);
    mat.addEdge(0, 1);
    mat.addEdge(0, 2);
    mat.addEdge(1, 2);
    mat.addEdge(1, 3);
    mat.addEdge(2, 4);
    mat.addEdge(4, 5);
    mat.printMatrix();

    // [2~5] 수동 방식으로 그래프 구성 후 BFS, DFS
    GraphList g(8);
    g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 3);
    g.addEdge(2, 4); g.addEdge(3, 5); g.addEdge(4, 5);

    BFS(g.adj, 0);   // [4] BFS 실행
    DFS(g.adj, 0);   // [5] DFS 실행

    // [6] 연결 요소 개수 출력
    int comp1 = countConnectedComponents(g.adj);
    cout << "[6] 연결 요소 수 (수동 그래프): " << comp1 << "\n";

    // [7] 경로 존재 여부 확인
    bool path1 = pathExists(g.adj, 0, 5);
    cout << "[7] 0과 5 사이 경로 존재? (수동 그래프): " << (path1 ? "Yes" : "No") << "\n";

    // [2~7] 파일 입력 방식 예제
    GraphList g2(0);
    g2.loadFromFile("graph_input.txt");

    BFS(g2.adj, 0);   // [4] BFS 실행 (파일 그래프)
    DFS(g2.adj, 0);   // [5] DFS 실행 (파일 그래프)

    int comp2 = countConnectedComponents(g2.adj);  // [6]
    cout << "[6] 연결 요소 수 (파일 그래프): " << comp2 << "\n";

    bool path2 = pathExists(g2.adj, 0, 5);          // [7]
    cout << "[7] 0과 5 사이 경로 존재? (파일 그래프): " << (path2 ? "Yes" : "No") << "\n";

    return 0;
}

 

// chap06_graph 전체 구현 + 입력 방식 2종류 (직접 addEdge vs 파일 입력)
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <limits>
#include <cstring>
#include <fstream>
#include <algorithm>
using namespace std;

const int INF = 1e9; // 무한대 값 정의

//--------------------------------------
// (생략된 부분 동일) 기존 코드 유지...

//--------------------------------------
// 8. 두 노드 간 경로 출력 (BFS 기반 경로 역추적)
// 실행 예: vector<int> path; if (findPath(g.adj, 0, 5, path)) ...
bool findPath(const vector<vector<int>>& adj, int u, int v, vector<int>& path) {
    int n = adj.size();
    vector<int> parent(n, -1);
    vector<bool> visited(n, false);
    queue<int> q;
    q.push(u);
    visited[u] = true;

    while (!q.empty()) {
        int cur = q.front(); q.pop();
        if (cur == v) break;
        for (int next : adj[cur]) {
            if (!visited[next]) {
                visited[next] = true;
                parent[next] = cur;
                q.push(next);
            }
        }
    }

    if (!visited[v]) return false; // 경로 없음

    for (int cur = v; cur != -1; cur = parent[cur])
        path.push_back(cur);
    reverse(path.begin(), path.end());
    return true;
}

//--------------------------------------
// 9. 싸이클 존재 여부 판별 (DFS 기반)
// 실행 예: bool has = hasCycle(g.adj);
bool dfsCycle(const vector<vector<int>>& adj, int v, int parent, vector<bool>& visited) {
    visited[v] = true;
    for (int next : adj[v]) {
        if (!visited[next]) {
            if (dfsCycle(adj, next, v, visited)) return true;
        } else if (next != parent) {
            return true; // back edge
        }
    }
    return false;
}

bool hasCycle(const vector<vector<int>>& adj) {
    int n = adj.size();
    vector<bool> visited(n, false);
    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            if (dfsCycle(adj, i, -1, visited)) return true;
        }
    }
    return false;
}

//--------------------------------------
// main 함수 예시 실행
int main() {
    // [1] 인접행렬 예제 실행
    GraphMatrix mat(6);
    mat.addEdge(0, 1);
    mat.addEdge(0, 2);
    mat.addEdge(1, 2);
    mat.addEdge(1, 3);
    mat.addEdge(2, 4);
    mat.addEdge(4, 5);
    mat.printMatrix();

    // [2~5] 수동 방식으로 그래프 구성 후 BFS, DFS
    GraphList g(8);
    g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 3);
    g.addEdge(2, 4); g.addEdge(3, 5); g.addEdge(4, 5);

    BFS(g.adj, 0);   // [4] BFS 실행
    DFS(g.adj, 0);   // [5] DFS 실행

    int comp1 = countConnectedComponents(g.adj);  // [6]
    cout << "[6] 연결 요소 수 (수동 그래프): " << comp1 << "\n";

    bool path1 = pathExists(g.adj, 0, 5);          // [7]
    cout << "[7] 0과 5 사이 경로 존재? (수동 그래프): " << (path1 ? "Yes" : "No") << "\n";

    // [8] 경로 출력
    vector<int> path;
    if (findPath(g.adj, 0, 5, path)) {
        cout << "[8] 경로 출력 (0 -> 5): ";
        for (int node : path) cout << node << " ";
        cout << "\n";
    } else {
        cout << "[8] 경로 없음\n";
    }

    // [9] 싸이클 유무 검사
    bool cycle1 = hasCycle(g.adj);
    cout << "[9] 싸이클 존재? (수동 그래프): " << (cycle1 ? "Yes" : "No") << "\n";

    // 파일 입력 그래프
    GraphList g2(0);
    g2.loadFromFile("graph_input.txt");

    BFS(g2.adj, 0);
    DFS(g2.adj, 0);

    int comp2 = countConnectedComponents(g2.adj);
    cout << "[6] 연결 요소 수 (파일 그래프): " << comp2 << "\n";

    bool path2 = pathExists(g2.adj, 0, 5);
    cout << "[7] 0과 5 사이 경로 존재? (파일 그래프): " << (path2 ? "Yes" : "No") << "\n";

    vector<int> path2vec;
    if (findPath(g2.adj, 0, 5, path2vec)) {
        cout << "[8] 경로 출력 (파일 그래프 0 -> 5): ";
        for (int node : path2vec) cout << node << " ";
        cout << "\n";
    }

    bool cycle2 = hasCycle(g2.adj);
    cout << "[9] 싸이클 존재? (파일 그래프): " << (cycle2 ? "Yes" : "No") << "\n";

    return 0;
}

 

전체코드

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <fstream>
using namespace std;

////////////////////////////////////////////////////////
// [1] 위상 정렬 함수 (BFS 방식) - 사이클 없을 때만 성공
////////////////////////////////////////////////////////
bool topologicalSort(const vector<vector<int>>& adjMatrix, vector<int>& result) {
    int N = adjMatrix.size();
    vector<int> indegree(N, 0);

    // 진입 차수 계산
    for (int j = 0; j < N; ++j)
        for (int i = 0; i < N; ++i)
            if (adjMatrix[i][j]) indegree[j]++;

    queue<int> q;
    // 진입 차수가 0인 정점을 큐에 삽입
    for (int i = 0; i < N; ++i)
        if (indegree[i] == 0) q.push(i);

    while (!q.empty()) {
        int u = q.front(); q.pop();
        result.push_back(u);

        // u와 연결된 모든 v의 진입 차수를 줄임
        for (int v = 0; v < N; ++v) {
            if (adjMatrix[u][v]) {
                if (--indegree[v] == 0)
                    q.push(v);
            }
        }
    }

    return result.size() == N;  // 모든 정점을 방문했으면 성공
}

////////////////////////////////////////////////////////
// [2] 사이클 존재 여부 판단
////////////////////////////////////////////////////////
bool hasCycle(const vector<vector<int>>& adjMatrix) {
    vector<int> dummy;
    return !topologicalSort(adjMatrix, dummy); // 위상 정렬 실패 = 사이클 있음
}

////////////////////////////////////////////////////////
// [3] 가능한 모든 위상 정렬 출력 (DFS + 백트래킹)
////////////////////////////////////////////////////////
void allTopologicalSorts(vector<vector<int>>& adjMatrix, vector<int>& indegree,
                         vector<int>& path, vector<bool>& visited) {
    int N = adjMatrix.size();
    bool flag = false;

    for (int i = 0; i < N; ++i) {
        if (indegree[i] == 0 && !visited[i]) {
            // 정점 i를 선택하여 경로에 추가
            for (int j = 0; j < N; ++j)
                if (adjMatrix[i][j]) indegree[j]--;

            path.push_back(i);
            visited[i] = true;

            allTopologicalSorts(adjMatrix, indegree, path, visited);

            // 백트래킹
            visited[i] = false;
            path.pop_back();
            for (int j = 0; j < N; ++j)
                if (adjMatrix[i][j]) indegree[j]++;

            flag = true;
        }
    }

    // 모든 정점 사용한 경우 출력
    if (!flag && path.size() == N) {
        for (int x : path) cout << x << " ";
        cout << "\n";
    }
}

////////////////////////////////////////////////////////
// [4] 각 작업의 최소 완료 시간 계산
////////////////////////////////////////////////////////
vector<int> computeEarliestCompletion(const vector<vector<int>>& adjMatrix, const vector<int>& duration) {
    int N = adjMatrix.size();
    vector<int> indegree(N, 0);
    vector<int> completion(N, 0);

    // 진입 차수 계산
    for (int j = 0; j < N; ++j)
        for (int i = 0; i < N; ++i)
            if (adjMatrix[i][j]) indegree[j]++;

    queue<int> q;
    // 진입 차수가 0인 작업부터 시작 (초기 수행 가능 작업들)
    for (int i = 0; i < N; ++i) {
        if (indegree[i] == 0) {
            q.push(i);
            completion[i] = duration[i];
        }
    }

    while (!q.empty()) {
        int u = q.front(); q.pop();

        for (int v = 0; v < N; ++v) {
            if (adjMatrix[u][v]) {
                // 이전 작업 완료시간 + 현재 작업 수행시간 중 최대값
                completion[v] = max(completion[v], completion[u] + duration[v]);
                if (--indegree[v] == 0)
                    q.push(v);
            }
        }
    }

    return completion;
}

////////////////////////////////////////////////////////
// [5] 파일로부터 인접행렬 및 duration 벡터 읽기
////////////////////////////////////////////////////////
bool loadFromFile(const string& filename, vector<vector<int>>& adjMatrix, vector<int>& duration) {
    ifstream fin(filename);
    if (!fin) {
        cerr << "파일 열기 실패: " << filename << "\n";
        return false;
    }

    int N, M;
    fin >> N >> M;
    adjMatrix.assign(N, vector<int>(N, 0));

    // 간선 입력 받기
    for (int i = 0; i < M; ++i) {
        int u, v;
        fin >> u >> v;
        adjMatrix[u][v] = 1;
    }

    duration.resize(N);
    for (int i = 0; i < N; ++i)
        fin >> duration[i];

    return true;
}

////////////////////////////////////////////////////////
// [6] 메인 함수 - 실행
////////////////////////////////////////////////////////
int main() {
    vector<vector<int>> adjMatrix;
    vector<int> duration;

    // 🔷 파일로부터 그래프 및 duration 불러오기
    if (!loadFromFile("input.txt", adjMatrix, duration)) return 1;

    int N = adjMatrix.size();

    cout << "[1] 기본 위상 정렬 결과:\n";
    vector<int> result;
    if (topologicalSort(adjMatrix, result)) {
        for (int x : result) cout << x << " ";
        cout << "\n";
    } else {
        cout << "사이클이 존재하여 위상 정렬 불가\n";
    }

    cout << "\n[2] 사이클 존재 여부:\n";
    cout << (hasCycle(adjMatrix) ? "사이클 존재\n" : "사이클 없음\n");

    cout << "\n[3] 가능한 모든 위상 정렬:\n";
    vector<int> indegree(N, 0);
    for (int j = 0; j < N; ++j)
        for (int i = 0; i < N; ++i)
            if (adjMatrix[i][j]) indegree[j]++;
    vector<bool> visited(N, false);
    vector<int> path;
    allTopologicalSorts(adjMatrix, indegree, path, visited);

    cout << "\n[4] 각 작업의 최소 완료 시간:\n";
    vector<int> complete = computeEarliestCompletion(adjMatrix, duration);
    for (int i = 0; i < N; ++i)
        cout << "작업 " << i << ": 완료 시간 = " << complete[i] << "\n";

    return 0;
}

 

'IT 프로그래밍 > 알고리즘' 카테고리의 다른 글

[알고리즘] 그래프 참고 슬라이드  (0) 2025.06.25
[알고리즘] 다익스트라, 프림, 크루스칼  (1) 2025.06.25
[알고리즘] 해슁  (0) 2025.06.25
알고리즘 그룹액티비티6  (0) 2025.06.02
[알고리즘]  (0) 2025.05.21