inflearn logo
강의

Course

Instructor

Do it! Algorithm Coding Test with JAVA

LCA 빠르게 구하기 Java 코드 시간초과

244

r45607989954

1 asked

0

P11438 문제 교재 코드 그대로 쳤는데 시간초과가 발생하네요 ㅜ

어딜 고쳐야 할까요 ㅠ

import java.util.*;
import java.io.*;

public class Main {
	static ArrayList<Integer>[] tree;
	static int[] depth;
	static int kmax;
	static int[][] parent;
	static boolean[] visited;
	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine()); // 노드의 수
		tree = new ArrayList[N + 1];
		for(int i = 1; i <= N; i++) {
			tree[i] = new ArrayList<Integer>();
		}
		StringTokenizer st;
		// 1. 인접리스트에 그래프 데이터 저장하기
		for(int i = 0; i < N - 1; i++) {
			st = new StringTokenizer(br.readLine());
			int s = Integer.parseInt(st.nextToken());
			int e = Integer.parseInt(st.nextToken());
			tree[s].add(e);
			tree[e].add(s);
		}
		depth = new int[N+1];
		visited = new boolean[N + 1];
		int temp = 1;
		kmax = 0;
		while (temp <= N) {  // 최대 가능 depth 구하기
			temp <<= 1;
			kmax++;
		}
		parent = new int[kmax + 1][N + 1];
		// 2. depth와 바로 윗 부모 bfs로 구하기
		bfs(1);
		// 3. 2^k 부모 구하기
		for(int k = 1; k <= kmax; k++) {
			for(int n = 1; n <= N; n++) {
				parent[k][n] = parent[k - 1][parent[k - 1][n]];
			}
		}
		int M = Integer.parseInt(br.readLine());
		// 4. 질의 수행하기
		for(int i = 0; i < M; i++) {
			st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			int LCA = excuteLCA(a, b);
			System.out.println(LCA);
		}
	}
	static int excuteLCA(int a, int b) {
		// 더 깊은 depth가 뒤에 오도록 변경
		if (depth[a] > depth[b]) {
			int temp = a;
			a = b;
			b = temp;
		}
		for(int k = kmax; k >= 0; k--) {  // 높이 빠르게 맞추기
			if(Math.pow(2, k) <= depth[b] - depth[a]) {
				if(depth[a] <= depth[parent[k][b]]) {
					b = parent[k][b];
				}
			}
		}
		for(int k = kmax; k >=0; k--) {  // 조상 빠르게 찾기
			// 최대 위로 올라가서 같은 부모를 가리키면 k를 1씩 감소하며 다른 지점을 찾음
			if(parent[k][a] != parent[k][b]) {
				a = parent[k][a];
				b = parent[k][b];
			}
		}
		// 여기 온 것은 k = 0일때 고려
		// case 1. k=0, 둘이 같은 노드를 가리킴 -> 그곳이 최소 공통 조상
		// case 2. k=0, 둘이 다른 노드를 가리킴 -> 바로 위에가 최초 공통 조상 -> 2^0 위에 보기
		int LCA = a;
		if(a != b) {
			LCA = parent[0][LCA];
		}
		return LCA;
	}
	// bfs 구현
	private static void bfs(int node) {
		Queue<Integer> queue = new LinkedList<>();
		queue.add(node);
		visited[node] = true;
		int level = 1;
		int now_size = 1;
		int count = 0;
		while(!queue.isEmpty()) {
			int now_node = queue.poll();
			for(int next : tree[now_node]) {
				if(!visited[next]) {
					visited[next] = true;
					queue.add(next);
					parent[0][next] = now_node;  // 부모 노드 저장하기
					depth[next] = level;  // 노드 depth 저장하기
				}
			}
			count++;
			// 자식 노드 모두 검사했는지 확인
			if(count == now_size) {
				count = 0;
				now_size = queue.size();
				level++;
			}
		}
	}
}

java 코딩-테스트 알고리즘

Answer 1

0

communityai8509

안녕하세요, 인프런 AI 인턴입니다.

님의 Java 코드 시간 초과 문제에 대해 조언드리고자 합니다. P11438 문제의 경우 깊이가 깊은 트리에 대해 LCA(Lowest Common Ancestor, 최소 공통 조상)를 구하는 문제입니다. 주어진 코드는 LCA를 찾기 위해 BFS와 DP를 사용하여 올바른 접근 방식을 취하고 있습니다. 그러나 실제로 문제에서 시간 초과가 발생하는 주된 원인은 다음과 같은 부분에서 개선이 필요할 수 있습니다.

  1. Math.pow의 사용을 피하기: 코드에서 Math.pow(2, k) <= depth[b] - depth[a]로 조건을 검사할 때, Math.pow 함수 대신 비트 연산을 사용해보세요. 예를 들어, 1 << k를 사용하면 Math.pow(2, k)와 동일한 결과를 얻을 수 있지만 훨씬 빠른 연산이 가능합니다. Math.pow 함수는 내부적으로 더 복잡한 연산을 수행하기 때문에 불필요하게 실행 시간이 길어질 수 있습니다.

  2. 최적화 가능한 부분 확인: 전체 코드를 살펴보면서 각 반복문과 조건문에서 불필요한 연산이 없는지, 더 효율적으로 처리할 수 있는 부분이 있는지 확인해보세요. 예를 들어, BFS 부분에서 큐를 사용하여 노드의 깊이와 부모를 설정하는 로직은 상대적으로 최적화하기 어렵지만, excuteLCA 함수 내부의 높이와 조상을 찾는 로직에서 비트 연산 등을 적절히 활용하여 최적화할 수 있는 여지가 있습니다.

  3. 입력 처리 최적화: 문제의 입력 크기가 큰 경우, BufferedReaderStringTokenizer의 사용이 올바르나, 입력 처리 과정에서도 성능 차이가 발생할 수 있습니다. 예를 들어, 입력이 매우 많은 경우, 입력받은 데이터를 처리하는 방식에 따라 실행 시간에 영헥을 줄 수 있으니 이 부분도 점검해볼 필요가 있습니다.

코드의 특정 부분에 초점을 맞추기 보다는 전반적인 실행 시간을 줄이는 방향으로 접근하는 것이 좋습니다. 코드 최적화에 초점을 맞추어 각 부분을 세심하게 검토하고 조정해보세요.

시간 초과 문제는 종종 작은 최적화들이 모여 큰 성능 개선을 이루기 때문에 여러 방법을 시도해보는 것이 중요합니다.

백준 1940 주몽의 명령 시간복잡도

0

59

0

다음영상이 문제 풀이 영상이라고 하셨는데 문제풀이 영상이 누락되어있는 것 같습니다

0

127

1

코딩테스트 디버깅

0

347

1

탐색 순서 질문

0

148

1

[P11726 2*N 타일채우기] top down 방식을 사용하니 런타임 에러가 발생합니다.

0

105

1

2018 연속된 자연수의 합 구하기 백준 사이트에서 메모리 초과 오류가 발생합니다.

0

201

1

1강 시간복잡도 중간에 중첩for문 직전에 상수는 상관없어요 하신 부분이 이해가 안됩니다

0

159

1

왜 int, long은 안되는지 궁금합니다.

0

224

1

DNA 비밀번호 (백준 12891) 통과가 안됩니다.

0

525

2

스택문제 백준 1874

1

459

1

백준11659 구간합 런타임 에러

0

306

1

백준 2178 미로탐색 질문 입니다.

0

448

1

구간합구하기1 (백준11659)

0

422

1

혹시 다른 ide에서 잘 돌아가는 프로그램이

0

349

1

내림차순으로 정렬하기 강의에서..

0

267

1

백준 11720 숫자의 합 질문 있습니다

0

433

1

(숫자의 합)1<=N <=100 사이의 값

0

383

1

소수구하기-백준 1929 질문

0

350

1

12891_DNA비밀번호

0

633

3

숫자의 합 구하기

0

391

1

안녕하세요 질문있습니다.

0

336

0

union 코드에 질문 있습니다.

0

401

2

[그리디 실전 문제] 최솟값을 만드는 괄호 배치 찾기 (백준 1541) - 반례를 못찾겠습니다 ㅠㅠ

1

309

1

[이진 탐색 실전 문제] 원하는 정수 찾기 편 질문

0

505

1