const fs = require("fs");
const input = fs
.readFileSync(process.platform === "linux" ? "/dev/stdin" : "input.txt")
.toString()
.trim()
.split("\n");
const V = Number(input[0]);
const graph = Array.from({ length: V + 1 }, () => []);
for (let i = 1; i <= V; i++) {
const line = input[i].split(" ").map(Number);
const u = line[0];
let idx = 1;
while (true) {
const v = line[idx++];
if (v === -1 || v === undefined) break;
const dist = line[idx++];
graph[u].push({ to: v, weight: dist });
}
}
결과
[
[],
[ { to: 3, weight: 2 } ],
[ { to: 4, weight: 4 } ],
[ { to: 1, weight: 2 }, { to: 4, weight: 3 } ],
[ { to: 2, weight: 4 }, { to: 3, weight: 3 }, { to: 5, weight: 6 } ],
[ { to: 4, weight: 6 } ]
]
Q. 어떻게 거리과 정점을 저장할 것인가
JS의 Array는 동적 배열이라 인접 리스트를 구현하기에 매우 편리하지만, 정점이 100,000개일 때는 관리를 잘해야한다.
Array 안에 Object를 담는 방식 (가독성 중시)가장 직관적인 방법입니다. 각 인덱스를 정점 번호로 쓰고, 연결된 정보를 객체 { node, dist } 형태로 push 합니다.
// 1번부터 V번까지 정점이 있을 때
const graph = Array.from({ length: V + 1 }, () => []);
// 입력 예시: 1번 노드에 2번 노드(거리 3)가 연결됨
graph[1].push({ to: 2, weight: 3 });
edge.to, edge.weight처럼 코드를 읽기 편합니다.TypedArray나 1차원 배열 활용 (메모리 최적화)만약 메모리 초과가 걱정되거나 더 빠른 성능을 원한다면, 객체 대신 숫자 배열 자체를 넣는 방식입니다.
const graph = Array.from({ length: V + 1 }, () => []);
// [연결노드1, 거리1, 연결노드2, 거리2, ...] 순서로 저장
graph[1].push(2, 3);
// 꺼내서 쓸 때
for (let i = 0; i < graph[curr].length; i += 2) {
const nextNode = graph[curr][i];
const distance = graph[curr][i + 1];
}
map(Number)의 중요성: input[i].split(" ")만 하면 요소들이 문자열("3") 상태입니다. 거리 계산을 위해 미리 숫자로 바꿔두는 것이 나중에 편합니다.idx++ 활용: line[idx]를 읽은 직후 바로 ++를 해주면, 다음 요소를 가리키게 되어 코드가 간결해집니다.graph[u].push([v, dist])처럼 배열로 넣는 게 객체{}보다 메모리를 조금 더 아낄 수 있습니다. (성능 차이는 미미하지만 메모리 제한이 빡빡할 때 유용한 팁입니다.)1 2 3 4 5
0 0 0 0 0
0 0 2 0 0
4 0 2 5 0
임의의 정점에서 간선 탐색 → 방문하지 않았고 가장 거리가 먼 정점 탐색 → 탐색할 정점이 없을 때까지 반복
function BFS(startNode, V, graph) {
let visited = Array(V + 1).fill(-1); // 방문 리스트 저장 -1: 미방문, 1: 방문
let distance = Array(V + 1).fill(0); // 가장 먼 노드와의 거리 저장
let maxWeight = 0;
let u = startNode;
visited[startNode] = 1; // 시작 노드에 대해서도 방문 처리
for (const edge of graph[u]) {
if (visited[edge.to] === -1) distance[edge.to] = maxWeight + edge.weight; // 미방문 노드에 대해서만 weight 추가
}
u = distance.indexOf(Math.max(...arr));
maxWeight = distance[u];
visited[u] = 1;
}
이 상태에서 탐색할 정점이 없다는 것을 어떻게 처리할 것인가?
while (queue.length > 0) 조건을 사용
function BFS(startNode, V, graph) {
let visited = Array(V + 1).fill(false); // 방문 여부만 체크
let distance = Array(V + 1).fill(0); // 거리 저장
let queue = [startNode]; // 탐색할 노드들을 담는 바구니(Queue)
visited[startNode] = true;
// 큐에 노드가 들어있는 동안 계속 반복 (더 이상 갈 곳 없으면 자동 종료)
while (queue.length > 0) {
let u = queue.shift(); // 현재 노드 꺼내기
for (const edge of graph[u]) {
if (!visited[edge.to]) { // 미방문 노드라면
visited[edge.to] = true;
distance[edge.to] = distance[u] + edge.weight; // 현재 노드 거리 + 간선 무게
queue.push(edge.to); // 다음 차례를 위해 큐에 넣기
}
}
}
// 탐색 종료 후, distance 배열에서 가장 큰 값과 그 인덱스 찾기
let maxDist = Math.max(...distance);
let maxNode = distance.indexOf(maxDist);
return { node: maxNode, dist: maxDist };
}
const fs = require("fs");
const input = fs
.readFileSync(process.platform === "linux" ? "/dev/stdin" : "input.txt")
.toString()
.trim()
.split("\n");
const V = Number(input[0]);
const graph = Array.from({ length: V + 1 }, () => []);
for (let i = 1; i <= V; i++) {
const line = input[i].split(" ").map(Number);
const u = line[0]; // 실제 노드 번호를 가져옵니다.
let idx = 1;
while (true) {
const v = line[idx++];
if (v === -1 || v === undefined) break;
const dist = line[idx++];
graph[u].push({ to: v, weight: dist });
}
}
function BFS(startNode, V, graph) {
let visited = Array(V + 1).fill(false); // 방문 리스트 저장 -1: 미방문, 1: 방문
let distance = Array(V + 1).fill(0); // 거리 저장
let queue = [startNode]; // 탐색할 노드들을 담는 바구니(Queue)
let u = startNode;
visited[startNode] = true; // 시작 노드에 대해서도 방문 처리
// 큐에 노드가 들어있는 동안 계속 반복 (더 이상 갈 곳 없으면 자동 종료)
while (queue.length > 0) {
let u = queue.shift(); // 현재 노드 꺼내기
for (const edge of graph[u]) {
if (!visited[edge.to]) {
// 미방문 노드라면
visited[edge.to] = true;
distance[edge.to] = distance[u] + edge.weight; // 현재 노드 거리 + 간선 무게
queue.push(edge.to); // 다음 차례를 위해 큐에 넣기
}
}
}
let maxDist = Math.max(...distance);
let maxNode = distance.indexOf(maxDist);
return { node: maxNode, dist: maxDist };
}
const firstNode = BFS(1, V, graph).node;
const treeRadial = BFS(firstNode, V, graph).dist;
console.log(treeRadial);
function bfs(startNode, V, graph) {
const dist = new Array(V + 1).fill(-1); // 거리를 저장할 배열, 인덱스에 해당 노드 방문여부 체크
const queue = [];
queue.push(startNode);
dist[startNode] = 0;
let farthestNode = startNode;
let maxDist = 0;
let head = 0;
while (head < queue.length) {
const u = queue[head++];
for (const edge of graph[u]) {
if (dist[edge.to] === -1) {
// 방문 안 했으면
dist[edge.to] = dist[u] + edge.weight;
queue.push(edge.to);
// 더 먼 노드를 발견하면 갱신
if (dist[edge.to] > maxDist) {
maxDist = dist[edge.to];
farthestNode = edge.to;
}
}
}
}
return { node: farthestNode, dist: maxDist };
}
head 변수를 이용한 큐(Queue) 최적화가장 큰 차이점은 queue.shift() 대신 head 포인터를 사용한 것입니다.
이전 로직 (shift()): 배열의 첫 번째 요소를 제거할 때마다 나머지 모든 요소를 한 칸씩 앞으로 당깁니다. 배열 크기가 N일 때 의 시간이 걸려 전체 BFS는 이 될 위험이 있습니다.
현재 로직 (head++): 요소를 삭제하지 않고 인덱스만 옮깁니다. 이는 의 작업이므로 전체 탐색이 로 매우 빠르게 끝납니다. 노드 개수(V)가 10만 개쯤 된다면 이 차이는 어마어마합니다.
maxDist와 farthestNode 실시간 갱신Math.max(...dist)와 indexOf를 사용하여 결과값을 찾았습니다.while문 안에서 새로운 거리를 계산할 때마다 즉시 최댓값을 비교하여 갱신합니다.차이점: 루프가 끝난 뒤 배열을 다시 전체 순회(V번)할 필요가 없으므로 연산 횟수가 줄어들고 코드가 더 효율적입니다.