Skip to content

Conversation

@oh-chaeyeon
Copy link
Collaborator

[그래프]

그래프(Graph)란?

  • 정점(Vertex, Node)간선(Edge) 으로 이루어진 자료구조 == 연결된 점들의 집합

그래프의 종류

  1. 방향 그래프 : 간선에 방향이 있는 그래프 (A → B)
  2. 무방향 그래프 : 간선에 방향이 없어서 양방향 이동이 가능한 그래프 (A → B, B → A)

그래프 탐색 방법

  1. BFS (Breadth-First Search, 너비 우선 탐색)
  • 가까운 노드부터 탐색하는 방식
  • Queue(큐, FIFO) 자료구조를 사용하면서, 최단 거리를 찾는 문제에서 유용함.
  1. DFS (Depth-First Search, 깊이 우선 탐색)
  • 한쪽 길을 끝까지 탐색한 후 다른 길을 탐색하는 방식
  • Stack(스택, LIFO) 또는 재귀(recursion) 를 사용하면서, 경로를 찾거나, 모든 노드를 방문하는 문제에서 유용함.

📌 푼 문제


📝 간단한 풀이 과정

Find the Town Judge

  • 문제 : 마을에 1부터 n까지 번호가 매겨진 사람이 있다. 이중에 판사를 찾아라
  • 판사는 다른사람을 믿지 않지만, 모든 사람은 판사를 신뢰한다. 앞에 조건 2개를 만족하는 사람은 단 한명이다.
  • 접근방식 : 각 사람이 신뢰하는 횟수를 감소시키고, 신뢰받는 횟수를 증가시켜 n-1만큼 신뢰받은 사람을 찾는 방식
/**
 * @param {number} n
 * @param {number[][]} trust
 * @return {number}
 */
var findJudge = function(n, trust) {
    // 1. 신뢰 관계를 저장할 배열 생성
    const trustCount = new Array(n + 1).fill(0);
    
    // 2. trust 배열을 순회하며 신뢰 관계 반영
    for (const [a, b] of trust) {
        trustCount[a]--;  // a는 다른 사람을 신뢰했으므로 감소
        trustCount[b]++;   // b는 신뢰를 받았으므로 증가
    }
    
   // 3. 판사 후보 찾기 : n-1명에게 신뢰받은 사람 찾기
    for (let i = 1; i <= n; i++) {
        if (trustCount[i] === n - 1) { 
            return i; 
        }
    }
    
   // 4. 판사가 존재하지 않는 경우에는 -1를 반환
    return -1; 
};

Find if Path Exists in Graph

  • 문제 : 주어진 무방향 그래프에서, source 에서 destination까지 경로가 존재하는지 확인하는 문제
  • 접근방식 : 무방향 그래프를 인접 리스트로 변환한 뒤, DFS 탐색을 수행하여 source에서 destination까지 도달할 수 있는지 확인하여 도달하면 true 반환, 안되면 false 반환.
/**
 * @param {number} n
 * @param {number[][]} edges
 * @param {number} source
 * @param {number} destination
 * @return {boolean}
 */
var validPath = function(n, edges, source, destination) {
    // 1. 그래프를 인접 리스트로 변환
    const graph = Array.from({ length: n }, () => []);
    
    edges.forEach(([u, v]) => {
        graph[u].push(v);
        graph[v].push(u);
    });
    
    // 2. 방문한 노드를 추적할 Set
    const visited = new Set();
    
   // 3. 깊이 우선 탐색 함수
    const dfs = (node) => {
        if (node === destination) return true;  // 목적지 도착 시 true 반환
        visited.add(node);
        
        for (const neighbor of graph[node]) {
            if (!visited.has(neighbor) && dfs(neighbor)) {
                return true;
            }
        }
        
        // 모든 경로를 탐색했지만 도달할 수 없는 경우 false 반환
        return false;
    };
    
   // 4. DFS 탐색 시작
    return dfs(source);
};

Find Center of Star Graph

  • 문제 : 무방향 스타 그래프에서 중앙 노드를 찾아 반환하는 문제
  • 스타 그래프란?
  1. 하나의 중심 노드가 있고, 나머지 모든 노드가 이 중심 노드와 직접 연결되어 있는 그래프
  2. n개의 노드가 있을 때, 항상 n - 1개의 간선(Edge) 이 존재
  • 접근방식 : 스타 그래프에서 중앙 노드는 항상 두 개 이상의 간선에서 공통으로 등장한다는 특성으로 서로서로 비교하면서 반환하기
/**
 * @param {number[][]} edges
 * @return {number}
 */
var findCenter = function(edges) {
    return edges[0][0] === edges[1][0] || edges[0][0] === edges[1][1] 
         ? edges[0][0]
         : edges[0][1];
};

게임 맵 최단거리

  • 문제 : 게임 맵이 주어지며, (0,0)에서 (n-1,m-1)까지 최단 거리를 찾는 문제(벽(0)과 길(1)이 존재하고 벽을 통과할 수 없음)
  • 접근방식 : 너비우선탐색(BFS)를 사용하여 시작점에서 도착점까지의 최단 경로를 찾고, 갈 수 없는 경우 -1을 반환하도록 함.
function solution(maps) {
    const n = maps.length; //행 개수
    const m = maps[0].length;  //열 개수
    const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]];
    const queue = [[0, 0]];   // BFS를 위한 큐, 시작점 (0,0) 추가
    const visited = Array.from({ length: n }, () => Array(m).fill(false));
    visited[0][0] = true;
    let count = 1;

    while (queue.length > 0) {
        const size = queue.length;  // 현재 단계에서 탐색할 노드 개수
        for (let i = 0; i < size; i++) {
            const [x, y] = queue.shift();

            // 목표 지점 (n-1, m-1) 도착 시 종료
            if (x === n - 1 && y === m - 1) {
                return count; 
            }

            for (const [dx, dy] of directions) {
                const nx = x + dx;
                const ny = y + dy;

                 // 범위 내에 있고, 벽이 아니며, 방문하지 않은 곳이라면 이동
                if (nx >= 0 && ny >= 0 && nx < n && ny < m && maps[nx][ny] === 1 && !visited[nx][ny]) {
                    visited[nx][ny] = true;
                    queue.push([nx, ny]);
                }
            }
        }
        count++;
    }

    // 도착 불가능할 경우 -1 반환
    return -1; 
}

타겟 넘버

  • 문제 : 주어진 숫자 배열 numbers에서 숫자의 순서를 바꾸지 않고, 각각의 숫자 앞에 + 또는 -를 붙여 연산한 결과가 target이 되는 경우의 수를 구하는 문제
  • 숫자 앞에 + 또는 -를 붙이는 모든 경우의 수를 탐색해야 하는 문제로, 각 숫자에 + 또는 -를 붙여 가능한 모든 합계를 계산하며, 중간 결과를 Map을 이용해 저장하는 방식으로 해결.
function solution(numbers, target) {
    const dp = new Map();
    dp.set(0, 1);  // 초기값: 합이 0인 경우 1가지 방법

    for (const number of numbers) {
        const nextDp = new Map();
        for (const [sum, count] of dp) {
              // 현재 숫자를 더한 경우
            nextDp.set(sum + number, (nextDp.get(sum + number) || 0) + count);
             // 현재 숫자를 뺀 경우
            nextDp.set(sum - number, (nextDp.get(sum - number) || 0) + count);
        }
        dp.clear(); // 기존 dp 초기화
        for (const [sum, count] of nextDp) {
            dp.set(sum, count);
        }
    }

    // target이 존재하면 반환, 없으면 0 반환
    return dp.get(target) || 0; 
}

전력망을 둘로 나누기

  • 문제 : n개의 송전탑이 전선으로 연결되어 하나의 트리 구조인데, 전선 하나를 끊어서 트리를 두 개의 전력망으로 나눌 때, 두 전력망이 가지는 송전탑의 개수가 최대한 비슷하도록 해야함.
  • 전선을 하나씩 제거하면서 DFS로 두 전력망의 송전탑 개수를 구하고, 가장 작은 개수 차이를 반환함.
function solution(n, wires) {
    const graph = Array.from({ length: n + 1 }, () => []); // 그래프 초기화
    // 그래프 구성
    for (const [v1, v2] of wires) {
        graph[v1].push(v2);
        graph[v2].push(v1);
    }

    let minDifference = Number.MAX_VALUE;

     // DFS 함수
    const dfs = (node, visited) => {
        let count = 1;
        visited[node] = true;

        for (const neighbor of graph[node]) {
            if (!visited[neighbor]) {
                count += dfs(neighbor, visited);
            }
        }

        return count;
    };

    // 전선을 하나씩 제거하면서 최소 차이 찾기
    for (const [v1, v2] of wires) {
        const visited = Array(n + 1).fill(false);
        visited[v1] = true; 
        const count = dfs(v2, visited); 
        const difference = Math.abs(count - (n - count)); 
        minDifference = Math.min(minDifference, difference);  // 최소값 갱신
    }

    return minDifference;
}

배달

  • N개의 마을이 있고, 각 마을은 도로로 연결되어 있음. 각 도로는 통행 시간이 다르며 양방향으로 이동 가능.
  • 문제 : 1번 마을에서 K 시간 이하로 배달이 가능한 마을의 개수를 구하는 문제
  • 1번 마을에서 각 마을까지의 최단 거리를 구한 후, K 이하인 거리의 마을 개수를 반환하도록 함.
function solution(N, road, K) {
    const graph = Array.from({ length: N + 1 }, () => []);
    // 양방향 : 그래프에 도로 정보 저장
    for (const [a, b, c] of road) {
        graph[a].push([b, c]);
        graph[b].push([a, c]);
    }

    const distances = Array(N + 1).fill(Infinity);
    distances[1] = 0; 
    const priorityQueue = [[0, 1]]; 

    while (priorityQueue.length > 0) {
        const [currentDistance, currentNode] = priorityQueue.shift();

        if (currentDistance > distances[currentNode]) {
            continue;
        }

        for (const [neighbor, time] of graph[currentNode]) {
            const newDistance = currentDistance + time;
            if (newDistance < distances[neighbor]) {
                distances[neighbor] = newDistance;
                priorityQueue.push([newDistance, neighbor]);
                priorityQueue.sort((a, b) => a[0] - b[0]); 
            }
        }
    }

    return distances.filter(distance => distance <= K).length;
}

@oh-chaeyeon oh-chaeyeon self-assigned this Feb 20, 2025
Copy link
Collaborator

@bona1122 bona1122 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

풀이한 것 잘 보았습니다. 타겟 넘버 문제에서 dp로 접근한게 굉장히 인상적이네요..! 좋은 관점 배워갑니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새 원소를 넣을때마다 sort를 진행하는 것 보다는 큐에서 원소를 꺼내기 전에 sort해주는게 더 효율적일 것 같습니다!

@JooKangsan JooKangsan merged commit bbf780e into main Feb 27, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants