계속지나가기
코딩스뮤
계속지나가기
전체 방문자
오늘
어제
  • 코딩스뮤:)
    • Algorithm
      • 백준 문제풀이
      • 프로그래머스 문제풀이
      • 알고리즘 이론
      • 자료구조
      • SW Expert Academy
    • 인공지능(AI)
      • LLMs
      • 자연어처리(NLP)
      • 컴퓨터비전(CV)
      • 딥러닝(DL)
      • 머신러닝(ML)
      • 인공지능기초수학
      • 선형대수학
    • 컴퓨터 세팅
    • Computer Science
      • 유닉스프로그래밍
      • 프로그래밍언어론
      • 디자인패턴
      • 클린코드
      • SW 영어
      • 리눅스
      • 논리회로
    • Server
      • Docker

블로그 메뉴

  • 홈
  • Who Am I(CV)
  • 태그

공지사항

인기 글

태그

  • LM
  • 머신러닝
  • 최대유량
  • 군집화
  • 파이썬 클린코드
  • ML
  • DIP
  • 비용함수
  • 결정경계
  • 컴퓨터비전
  • 경사하강법
  • 지도학습
  • DigitalImageProcessing
  • 기계학습
  • 언어모델
  • 네트워크플로우
  • SIFT
  • 디지털이미지처리
  • machinelearning
  • networkflow
  • 패턴인식
  • MaximumFlow
  • 손실함수
  • f1-score
  • ComputerVision
  • NLP
  • 에지검출
  • 선형회귀
  • 알고리즘
  • 비지도학습

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
계속지나가기

코딩스뮤

최소 공통 조상(Lowest Common Ancestor)
Algorithm/알고리즘 이론

최소 공통 조상(Lowest Common Ancestor)

2021. 10. 4. 21:38
반응형

최소 공통 조상(LCA)란?

LCA란 트리상에서 어떤 두 정점 u, v가 있을 때, u이거나 u의 조상이면서 동시에 v거나 v의 조상인 노드들 중 가장 깊은 노드이다

 

예를들어, 아래의 트리에서 노드 4와 3의 LCA는 1번 노드이다

다른 예로는 노드 9, 14의 LCA는 2번 노드이며, 노드 3, 13의 LCA는 3번 노드이다

여기서 한가지 사실을 알 수 있는데 노드 u,v와 이들의 LCA w의 관계이다

u,v의 최단 경로는 u-w-v 형태라는 것이다

이를 아래의 트리에서 빨간 경로로 확인할 수 있다

 

 

따라서, 트리 문제에서 최단경로를 빠르게 찾길 원한다면 가장먼저 LCA를 생각해봐야 한다

 

LCA 구현 방법

1. 노드 u,v 가 서로 만날때까지 부모노드를 따라서 두 노드를 옮겨 보는 방법 : 선형 탐색

  • 두 노드의 높이가 같은 경우, 가리키는 정점이 같아질 때까지 부모 노드로 거슬러 올라가면 된다
    • 정점 4와 7의 LCA를 찾는 경우
  • 두 노드의 높이가 다를 경우, 높이를 맞춰주고 나서 하나씩 올리는 방식으로 최악의 경우 O(N)의 시간을 가진다
    • 정점 14와 8의 LCA를 찾는 경우

 

구현 코드(c++) : 백준 11437 LCA 

https://www.acmicpc.net/problem/11437

 

11437번: LCA

첫째 줄에 노드의 개수 N이 주어지고, 다음 N-1개 줄에는 트리 상에서 연결된 두 정점이 주어진다. 그 다음 줄에는 가장 가까운 공통 조상을 알고싶은 쌍의 개수 M이 주어지고, 다음 M개 줄에는 정

www.acmicpc.net

#include <iostream>
#include <vector>
#include <algorithm>
#define MAX 50002

using namespace std;

int N, M;
vector<int> edge[MAX];
int parent[MAX] = {0,};
int level[MAX] = {0,};

int lca(int u,int v);
void init_tree(int cur, int pre);

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    cin >> N;
    while(--N){
        int u, v;
        cin >> u >> v;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    init_tree(1,0); // initialize tree
    
    cin >> M;
    while(M--){ // find out LCA for u and v
        int u, v;
        cin >> u >> v;
        cout << lca(u,v) << '\n';
    }
}
int lca(int u,int v){
    // if node <u> level is smaller than node <v>,
    // set the node<u> level's higher
    if (level[u] < level[v])
        swap(u,v);
    
    // make level of two nodes to same level
    while(level[u] != level[v])
        u = parent[u];
    
    // backtrace the tree when the pointers points to the same node
    while( u != v){
        u = parent[u];
        v = parent[v];
    }
    return u;
    
}
void init_tree(int cur, int pre){
    // set the tree for DFS
    parent[cur] = pre;
    level[cur] = level[pre] + 1;
    
    for(int i = 0; i < edge[cur].size(); i++){
        int child = edge[cur][i];
        if (child == pre)
            continue;
        init_tree(child, cur);
    }
    
}

 

 

2. 이분탐색으로 LCA 구하기 :O(log(depth))

  • 위에서 살펴본 선형 탐색 방법은 구현이 간단하나 트리의 구조에 따라 시간적 측면에서 매우 비효율적이다
  • 양쪽 다 크기 N인 트리에서 이분탐색을 사용하면 O(log(depth))만에 두 특정 점점의 LCA를 찾을 수 있음

구현방법

    • 부모노드를 나타내는 parent배열을 2차원으로 둔다
    • Parent[x][k] = "x번 정점의 2^k번째 조상 노드의 번호"로 둔다
    • 이는 x = 13일때 아래와 같이 표현된다
    •  

  • DFS를 통해 root부터 트리 구성 시, 낮은 깊이의 노드를 반드시 먼저 탐색하므로 다음 식이 성립한다
  • Parent[x][k] = Parent[Parent[x][k-1]][k-1]
  • 이는 x = 13일때 아래와 같이 표현된다
  • 이 경우, 기존의 선형시간에 구하는 방법에서 부모 노드로 거슬러가는 과정을 2의 제곱수만큼 한 번에 건너뛸 수 있다.

구현 코드(c++) : 백준 11438 LCA2

https://www.acmicpc.net/problem/11438

 

11437번: LCA

첫째 줄에 노드의 개수 N이 주어지고, 다음 N-1개 줄에는 트리 상에서 연결된 두 정점이 주어진다. 그 다음 줄에는 가장 가까운 공통 조상을 알고싶은 쌍의 개수 M이 주어지고, 다음 M개 줄에는 정

www.acmicpc.net

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#define MAX 100002
#define MAX_N 18

using namespace std;

int N, M;
vector<int> edge[MAX];
int parent[MAX][MAX_N]; // MAX, 2^N == MAX
int level[MAX] = {0,};
int maxlevel;

int lca(int u,int v);
void init_tree(int cur, int pre, int level);

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    cin >> N;
    while(--N){
        int u, v;
        cin >> u >> v;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    
    maxlevel = (int)floor(log2(MAX));
    init_tree(1,0,1); // initialize tree
    
    cin >> M;
    while(M--){ // find out LCA for u and v
        int u, v;
        cin >> u >> v;
        cout << lca(u,v) << '\n';
    }
}
int lca(int u,int v){
    // find out lca of <u> and <v>, return
    if( u == 1 || v == 1)
        return 1;
    
    // if node <u> level is smaller than node <v>,
    // set the node<u> level's higher
    int tgt = u, cmp = v;
    if (level[u] < level[v])
        swap(tgt,cmp);
    
    // make level of two nodes to same level
    if( level[tgt] != level[cmp])
        for(int i = maxlevel; i >= 0; i--)
            if( level[parent[tgt][i]] >= level[cmp])
                tgt = parent[tgt][i];
    
    // backtrace the tree when the pointers points to the same node
    int lca = tgt;
    if(tgt != cmp){
        for(int i = maxlevel; i >= 0; i--){
            if(parent[tgt][i] != parent[cmp][i]){
                tgt = parent[tgt][i];
                cmp = parent[cmp][i];
            }
            lca = parent[tgt][i];
        }
    }
    return lca;
}
void init_tree(int cur, int pre, int l){
    // set the tree for DFS
    level[cur] = l;
    parent[cur][0] = pre;
    
    for(int i = 1; i <= maxlevel; i++)
        parent[cur][i] = parent[parent[cur][i-1]][i-1];

    
    for(int i = 0; i < edge[cur].size(); i++){
        int child = edge[cur][i];
        if (child == pre)
            continue;
        init_tree(child, cur, l + 1);
    }
    
}
반응형

'Algorithm > 알고리즘 이론' 카테고리의 다른 글

네트워크 플로우(Network Flow)  (2) 2021.11.03
이분 매칭(Bipartite Matching)  (0) 2021.11.02
단절점(Articulation Point), 단절선(Articulation Bridge)  (0) 2021.09.30
강한연결요소 : Strongly Connected Component  (0) 2021.09.05
위상 정렬 Topological Sort  (0) 2021.08.28
    'Algorithm/알고리즘 이론' 카테고리의 다른 글
    • 네트워크 플로우(Network Flow)
    • 이분 매칭(Bipartite Matching)
    • 단절점(Articulation Point), 단절선(Articulation Bridge)
    • 강한연결요소 : Strongly Connected Component
    계속지나가기
    계속지나가기
    NLP Engineer

    티스토리툴바