simDev1234
심플하고 차분하게
simDev1234
전체 방문자
오늘
어제
  • 분류 전체보기
    • Computer Science
      • Basic Math
      • Data Structure
      • Algorithm
      • Database
      • OS
    • Language
      • Java
      • Kotlin
      • SQL
    • Framework
      • Spring
      • Orm&Mapper
      • 프로젝트로 스프링 이해하기
      • 스프링 라이브러리
    • Infra
      • Cloud
      • Docker
      • Redis
      • AWS, Azure
      • Device
    • Etc
      • CleanCoding
    • Git,Github

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 참조타입
  • 자바프로그램
  • scanner #next() #nextLine()
  • controllerTest
  • 404
  • 자바
  • 참조변수
  • JVM메모리구조
  • 스프링
  • 자바메모리구조
  • 자바프로그래밍
  • 컨트롤러
  • null

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
simDev1234

심플하고 차분하게

Computer Science/Algorithm

[알고리즘] 최소신장트리(MST)

2022. 9. 1. 23:58

|  최소신장트리

- Mininum Spanning Tree

- 그래프의 모든 노드를 연결할 때 가중치 합이 최소가 나오는 트리

- 특징 : 

(1) 사이클 제외 -- 가중치의 합은 사이클이 포함될 때 최소가 될 수 없다. 

(2) N개의 노드에 대한 최소신장트리 간선 수는 늘 N - 1개이다.

 

|  종류

  크루스칼(Kruskal) 프림(Prim)
핵심 - 최소값의 간선 우선 연결
- 사이클 발생 시 다른 간선 선택
- 임의의 노드에서 시작
- 연결된 간선 중 낮은 가중치 선택
언제? - 간선 수가 비교적 적을 때 - 간선 수가 많을 때 유리
메모리 에지 리스트(or 배열)
유니온 파인드 배열
인접 리스트(or 배열)
방문 배열
속도 O(ElogE) O(ElogV)  - 우선순위 큐 사용 시

*O(v2) - 정렬되지 않은 배열을 사용할 때

 

|  크루스칼 구현

- 구현 순서

1 에지 리스트(그래프) & 유니온 파인드 배열 초기화
2 에지 리스트를 가중치 기준으로 정렬
3 가중치가 낮은 에지부터 연결하기 반복

 

[1] 에지 리스트(그래프) 정렬 & 유니온 파인드 배열 초기화

- 수업 시간의 경우, 아예 외부에서 입력값으로 미정렬된 간선 배열을 받는 것을 가정으로 했다.

- 이 경우에는 위의 [1][2]를 묶어 간선 배열을 정렬하면 된다.

// 전역변수로 유니온-파인드 배열 지정
static int[] parents;

// data : 간선배열, v : 노드 수, e : 간선 수
public static int kruskal(int[][] data, int v, int e) {
    int weightSum = 0; // 전체 가중치
    
    // 1. 그래프 및 유니온 파인드 배열 초기화
    // 1-1. 그래프 정렬
    Arrays.sort(data);
    
    // 1-2. 유니온 파인트 배열 초기화
    parents = new int[v + 1];
    
    for (int i = 1; i < v + 1; i++){
    	parents[i] = i; // 처음에는 자기 자신
    }
    
    return weightSum;
}

 

[2] 가중치가 낮은 에지부터 연결하기 x 반복

- 가중치가 낮은 에지부터 연결을 하려면 먼저 아래와 같은 메소드가 필요하다.

- 참고로 수업시간에 받은 간선 배열은 무방향 간선들로, 모두 (작은 숫자 노드 -> 큰 숫자 노드)로 연결되어 있었다.

 

2-1. find(int a) :: int 

public static int find(int a){
	// 자기 자신과 연결된 경우
	if (parents[a] == a){
    	return a;
    }
    // 자기 자신과 연결되지 않은 경우
    return parents[a] = find(parents[a]);
}

 

2-2. union(int a, int b) :: void

public static void union(int a, int b){
    int aP = parents[a];
    int bP = parents[b];
    
    if (aP != bP){ // 여기서는 크기비교x (필요할 경우 추가)
    	parents[aP] = bP;
    }
}

 

2-3. 최소값 우선으로 간선 연결

public static int kruskal(int[][] data, int v, int e){
    // 생략
    
    // 2. 최소값 우선 간선 연결
    for (int i = 0; i < e; i++){
    	// 서로 연결된 적이 없었던 경우
        if (find(data[i][0]) != find(data[i][1]){
        	// 연결
            union(data[i][0], data[i][1]);
            // 전체 가중치 값 갱신
            weightSum += data[i][2];
        }
    }
}

 

- 전체 코드

 

static int[] parents;
public static int kruskal(int[][] data, int v, int e) {
    int weightSum = 0;

    // 1. 그래프 및 유니온 파인드 배열 초기화
    // edge graph
    Arrays.sort(data, (x, y) -> x[2] - y[2]);

    // union-find
    parents = new int[v + 1];
    for (int i = 1; i < v + 1; i++) {
        parents[i] = i; // 최초에는 자기자신
    }

    // 2. 최소값 우선으로 간선 연결
    for (int i = 0; i < e; i++) {
        if (find(data[i][0]) != find(data[i][1])) {
            union(data[i][0], data[i][1]);
            weightSum += data[i][2];
        }
    }

    return weightSum;
}

public static void union(int a, int b) {
    int aP = find(a);
    int bP = find(b);

    if (aP != bP) {
        parents[aP] = bP;
    }
}

public static int find(int a) {
    if (a == parents[a]) {
        return a;
    }
    return parents[a] = find(parents[a]);
}

 

|  프림 구현

- 구현 순서

1 인접 리스트 및 방문 배열 초기화
2 우선 순위 큐를 통해 자동 정렬
3 임의의 노드(ex. 1)를 선택한 후 낮은 가중치의 간선 연결 x 노드 수만큼 반복

 

- 원리

입력받은 간선 데이터
int[][] graph = 
{{1, 3, 1}, {1, 2, 9}, {1, 6, 8}, 
{2, 4, 13}, {2, 5, 2}, {2, 6, 7}, 
{3, 4, 12}, 
{4, 7, 17}, {5, 6, 5}, {5, 7, 20}};

 

(1) 방문 배열 : 총 노드의 수(v)만큼 방문배열이 입력된다.

  1 2 3 4 5 6 7
1 t            
2 t   t        
3 t   t     t  
4 t   t   t t  
5 t t t   t t  
6 t t t t t t  
7 t t t t t t t

 

(2) 우선순위 큐

- 가중치의 크기를 기준으로 오름차순 자동 정렬

- 가장 작은 가중치의 노드를 꺼내, 방문 배열을 체크하고, 전체 가중치(weightSum)를 +하는 형태

  * 방문했던 노드는 다시 방문하지 않는다면, 결과적으로는 1-3-6-5-2-4-7 과 같이 노드가 연결될 것

- 타입 : Node (도착노드, 가중치)

흐름 우선순위 큐
초기 N(1, 0)        
1 N(3, 1) N(6, 8) N(2, 9)    
2 N(6, 8) N(2, 9) N(4, 12)    
3 N(5, 5) N(2, 7) N(2, 9) N(4, 12)  
4 N(2, 5) N(6, 5) N(2, 7) N(2, 9) N(4, 12)
5 N(6, 5) N(2, 7) N(2, 9) N(4, 12) N(4, 13)
6 N(4, 13) N(7, 17)      

 

- 구현

 

[1] 인접 리스트 및 방문 배열 초기화

static class Node{
    int to;
    int weight;
    
    Node(int to, int weight;){
    	this.to = to;
        this.weight = weight;
    }
}

public static int prim(int[][] data, int v, int e){
    // 인접리스트
    ArrayList<ArrayList<Node>> adjList = new ArrayList();
    for (int i = 0; i < v + 1; i++){
    	adjList.add(new ArrayList());
    }
    
    for (int i = 0; i < e; i++){
    	// 양방향으로 저장
    	adjList.get(data[i][0]).add(new Node(data[i][1], data[i][2]));
        adjList.get(data[i][1]).add(new Node(data[i][0], data[i][2]));
    }
    
    // 방문 배열
    boolean[] visited = new boolean[v + 1];
}

 

[2] 우선 순위 큐를 통해 자동 정렬

public static int prim(int[][] data, int v, int e){
    // 생략
    
    // 우선순위 큐
    PriorityQueue<Node> pq = new PriorityQueue((x, y) -> x.weight - y.weight);
}

 

[3] 임의의 노드(ex. 1)를 선택한 후 낮은 가중치의 간선 연결 x 노드 수만큼 반복

public static int prim(int[][] data, int v, int e){
    // 생략
    
    // 우선순위 큐
    PriorityQueue<Node> pq = new PriorityQueue((x, y) -> x.weight - y.weight);
    pq.add(new Node(1, 0)); // 임의의 노드를 시작점으로 지정
    
    int cnt = 0; // 노드만큼만 반복
    while (!pq.isEmpty()){
    	Node cur = pq.poll();
        cnt++;
        
        // 방문 여부확인
        if (visited[cur.to]){
        	continue;
        }
        visited[cur.to] = true; // 방문 체크
        weightSum += cur.weight; // 전체 가중치 갱신
        
        // 반복 수 확인(마지막 뽑는 노드)
        if (cnt == v) {
        	break;
        }
        
        // 인접한 정점을 작은 가중치 순으로 offer
        for (int i = 0; adjList.get(cur.to).size(); i++){
        	Node adjNode = adjList.get(cur.to).get(i);
            
            if (visited[adjNode.to]) {
            	continue;
            }
            
            pq.offer(adjNode);
        }
        
    }
    
    return weightSum;
}

 

- 전체 코드

static class Node {
    int to;
    int weight;

    public Node(int to, int weight) {
        this.to = to;
        this.weight = weight;
    }
}

public static int prim(int[][] data, int v, int e) {
    int weightSum = 0;

    ArrayList<ArrayList<Node>> adjList = new ArrayList();
    for (int i = 0; i < v + 1; i++) {
        adjList.add(new ArrayList<>());
    }

    for (int i = 0; i < e; i++) {
        // 양방향으로 연결
        adjList.get(data[i][0]).add(new Node(data[i][1], data[i][2]));
        adjList.get(data[i][1]).add(new Node(data[i][0], data[i][2]));
    }

    boolean[] visited = new boolean[v + 1];

    PriorityQueue<Node> pq = new PriorityQueue<>((x, y) -> x.weight - y.weight);
    pq.add(new Node(1, 0));

    int cnt = 0;
    while (!pq.isEmpty()) {
        Node cur = pq.poll();
        cnt++;

        if (visited[cur.to]) {
            continue;
        }
        visited[cur.to] = true;
        weightSum += cur.weight;

        if (cnt == v) {
            return weightSum;
        }

        // 인접한 정점을 작은 가중치 순으로 offer
        for (int i = 0; i < adjList.get(cur.to).size(); i++) {
            Node adjNode = adjList.get(cur.to).get(i);

            if (visited[adjNode.to]) {
                continue;
            }

            pq.offer(adjNode);
        }
    }

    return weightSum;
}

 

 

 

[ 참고 및 출처 ]

- 부트캠트 수업 내용 정리

- [Do it! 알고리즘 코딩 테스트 자바 편]

'Computer Science > Algorithm' 카테고리의 다른 글

문자열 알고리즘 - KMP와 Robin-Karp 알고리즘  (1) 2022.10.13
알고리즘 속도 구하기  (0) 2022.09.29
[알고리즘] 최단 경로 구하기 - 3. 플로이드-워셜 알고리즘  (0) 2022.09.01
[알고리즘] 최단 경로 구하기 - 2. 벨만 - 포드  (0) 2022.09.01
[알고리즘] 최단 경로 구하기 - 1. 다익스트라  (0) 2022.09.01
    'Computer Science/Algorithm' 카테고리의 다른 글
    • 문자열 알고리즘 - KMP와 Robin-Karp 알고리즘
    • 알고리즘 속도 구하기
    • [알고리즘] 최단 경로 구하기 - 3. 플로이드-워셜 알고리즘
    • [알고리즘] 최단 경로 구하기 - 2. 벨만 - 포드
    simDev1234
    simDev1234
    TIL용 블로그. * 저작권 이슈가 있는 부분이 있다면 댓글 부탁드립니다.

    티스토리툴바