SVN 주요 명령어들

 

1. SVN 주요 명령어

 

1. 기존 프로젝트 svn 올리기

>svn import {기존프로젝트} {Reposiroty URL} –m “메시지 내용

>svn import testSVN https://danlee/svn/practiceSVN -m “기존 프로젝트 SVN import ”

2.프로젝트 CheckOut

>svn co {SVN주소} {checkout할 대상}

>svn co https://danlee/svn/practiceSVN .

3. 소스 삭제하기

>svn delete {SVN에서 삭제할 파일명}

>svn delete test.txt

4. 소스 커밋하기

>svn  commit {커밋할 파일} –m “메시지 내용

>svn commit test.txt –m “테스트 파일

5. svn 변경 상태 체크

>svn status

6. svn 버전 확인

>svn –version

7. svn repository url 등 정보 확인

>svn info

8. 기존 소스에 새로운 파일이나 폴더 추가

>svn add {파일명}

>svn add test.txt

9. svn log 확인

>svn log {파일명 or 폴더명}

>svn log main.c

프로젝트 전체 로그

>svn log {Repository 주소}

>svn log https://danlee/svn/practiceSVN

10. 서버 주소 변경

>svn switch –relocate {oldURL} {newURL}


11.
svn 명령어 종류 및 설명

>svn help

 

2. Git vs SVN 명령어 비교

그리고 SVN과 Git을 사용하다보면 햇깔릴때가 있는데 보기쉽게 표로 정리!!

조작

Git

SVN

저장소 복제

git clone

svn checkout

커밋

git commit

svn commit

커밋의 상세 내용

git show

svn cat

상태 확인

git show

svn status

변경 내용 확인

git diff

svn diff

로그 확인

git log

svn log

추가

git add

svn add

이동

git mv

svn mv

삭제

git rm

svn rm

변경 취소

git reset

svn revert

* 브랜치 작성

git branch

svn copy

브랜치의 전환

git checkout

svn switch

병합

git merge

svn merge

태그 작성

git tag

svn copy

변경 사항 업데이트

git pull / git fetch

svn update

* 원격 저장소에 반영

git push

svn commit

무시할 파일 목록

.gitignore

.svnignore

* 브랜치 : svn에서 브랜치와 태그는 구조상 동일, Git에서는 branch, tag 의미가 다름

* 원격 저장소반영 : svn에는 원격저장소 개념이 없기 때문에 commit 하면 server 반영됨

'IT > SVN(Subversion)' 카테고리의 다른 글

SVN Repository 만들기  (0) 2019.09.26
SVN 입문  (0) 2019.09.26

Visual SVN Server로 Repository 만들어보기

 

사실 Create Repository 누르고 다음다음 누르면 생성이 되기때문에 따로 설명할것은 없지만,
각 옵션이 무엇을 의미하는지 정리하겠습니다.

 

 

1. Create Repository

우선 사진 1과 같이 Create New Repository를 클릭하면 Repository를 생성할 수 있습니다.

사진 1. Create Repository

 

 

 

 

2. Repository Type

Create New Repository를 클릭하면 나오는 사진 2와 같이 Repository Type 선택하기창이 나타납니다.

사진 2. Repository Type

  • FSFS (Fast Secure File System) : 표준 Subversion Repository로 기본적으로 사용하는 저장소 입니다.
  • VDFS (VisutalSVN Distributed File System) : 분산 파일 시스템과 유사한 형태를 지니며 특징은 다음과 같습니다.
     - Master / Slave 형태의 아키텍처로 구성
     - Commit할 경우 Master Server로 적용된 후 Slave Server로 자동 복제 됨
     - Slave Server로도 Commit 가능하며, 이 경우에 동이에 Master Server로도 자동 Commit 됨

▶ Distributed VDFS는 FSFS repository와 기능적으론 동일합니다. 그렇기 때문에 서버 구성을 어떻게 할 것인가에 따라 FSFS / VDFS를 선택하면 됩니다.

 

 

 

3. Repository Structure

Repository Type을 선택한 후에는 사진 3과 같이 Repository Structure를 선택해야 합니다.

사진 3. Repository Structure

 

이 두가지의 차이는 간단합니다. 하나의 Repository에 하나의 프로젝트를 관리하는가, 여러개의 프로젝트를 관리하는가에 따라 선택하면 됩니다.

  • Empty repository : Standard Project로, 한 개의 Repository에 여러 Project를 관리할 수 있는 구조로 Repository를 생성합니다.
  • Single-Project Repository : 한 개의 Repository에 하나의 Proeject를 관리할 수 있는 구조로 Repository를 생성합니다.

 

 

 

4. Repository Access Permissions

마지막 권한 설정입니다. 크게 설명할 것 없이, 작성되어 있는 그대로 원하는 것을 선택하여 생성하면 됩니다.

사진 4. Repository Access Permissions

 

  • Nobody has access : 아무나 접근 가능
  • All Subversion users have Read/Write access : SVN에 등록된 User들 접근 가능
  • Customize Permissions : 커스터마이징에 따라 접근 가능

 

 

5. Customize Permissions

Custom... 을 클릭하여 원하는 그룹 또는 계정마다 권한을 부여할 수 있는 옵션 입니다.

사진 5. Customize Permissions

 

여기까지 하면 SVN Repository가 생성 됩니다!

 

 

6. 생성 후 확인

생성이 완료되면 SVN Repository URL을 확인할 수 있습니다. 
SVN은 http,https,svn 프로토콜을 지원합니다.

사진 6. Repository URL 확인

 

이후 접근할 수 있는 계정을 사진 7과 같이 생성하면 해당 Repository로 접근 가능합니다.

사진 7. User 생성

 

웹 주소에 URL을 입력하면 사진 8과 같이 정상적으로 동작함을 확인할 수 있습니다.

사진 8. URL Repository 접근

 

 

'IT > SVN(Subversion)' 카테고리의 다른 글

SVN Shell 주요 명령어, Git 명령어 비교  (0) 2019.09.26
SVN 입문  (0) 2019.09.26

요즘 IT 회사들은 주로 Git을 많이 사용하지만, 형상관리 툴로 SVN도 많이 사용합니다. 물론 SI업체들은 SVN을 대체로 많이 사용합니다.

그래서 오늘은 SVN(Subversion)에 관해서 기본적인 개념을 정리하도록 하겠습니다!
(아래 SVN 개념들은 아파치에서 무료로 제공하는 SVN Book을 참고하여 정리했습니다)

 

 

1. SVN 개념

 

1.1 Repository

Repository는 저장소 입니다.  SVN은 중앙관리 방식으로 Repository를 생성하면, Client가 Repository에 연결해서 내려받아 Read/Write하는 방식입니다. (그림 1 참고)

 

 

1.2 충돌방지 LOCK

SVN의 특징 중 하나는 Lock 시스템입니다. 현재 작업중인 파일들은 다른사람이 사용하지 못하게 Lock을 걸어두어 
여러 사람들이 동시에 수정하여 발생하는 문제를 방지할 수 있습니다.

그림 2. SVN Lock System

 

1.3 Basic Work Cycle

그림 3을 참고하여 SVN의 기본 Cycle을 이해한다면 SVN 사용의 이해는 크게 무리가 없을 것 입니다.

 

그림 3. SVN basic Work Cycle

 

1.4 SVN 관리Tool

SVN은 관리 툴은 VisualSVNServer.msc, VisualSVNShell.exe을 통해 관리가 가능합니다.
SVN Server 관리 툴은 VisualSVNServer.msc 이며 https://www.visualsvn.com/server/download/ 에서 다운 받을 수 있습니다.

사진 1. VisualSVNServer.msc

 

물론 Linux의 경우 쉘로 관리하며, Windows에서도 VisualSVNShell.exe을 통해 쉘로 관리할 수 있습니다.
VisualSVNShell.exe은 VisualSVNServer를 설치하면 폴더내에 같이 설치 됩니다.

사진 2. VisualSVNShell.exe

 

SVN을 바로 사용하기 위해서 알아야 할 가장 기본적인 내용과 사용 Tool에 관해 정리했습니다.
용어 정리와 Command들은 나중에 정리하겠습니다!!

'IT > SVN(Subversion)' 카테고리의 다른 글

SVN Shell 주요 명령어, Git 명령어 비교  (0) 2019.09.26
SVN Repository 만들기  (0) 2019.09.26

기존 프로젝트를 기반으로 새로운 프로젝트를 덧붙여서 진행하려고 하다보니

git tag를 그어야 할 일이 생겼네요.

 

- 급할때 보기용 요약정리 -

tag 작성 : git tag -a {tag id} -m "{tag message}"
tag 확인 : git tag
tag push : git push {Remote Address} {tag id}
commit log에서 tag 확인 : git log --decorate
tag 삭제 : git tag -d {tag id} OR git tag --delete {tag id}

 

 

 

그래서 오늘은 속성 git tag 긋기!

tag를 긋는 이유는 아주아주 간단 합니다.

어느 버전인지를 알아보기 위함이죠

현재 어느 Commit까지 Release가 되었는지, 아니면 어느 시점부터 프로젝트가 다른 프로젝트와 Merge가 되었는지 등을 확인하기 위한 용도 입니다.

다음 아래 사진과 같은 commit log에 git tag를 작성하는 방법을 알아보겠습니다.

그림 1. git tag 긋기 01

 

 

1. tag 작성하기

위 사진에 화살표로 표기 된 commit에 새로운 Project의 시작점이라고 표기하기 위해서 "ERP_HR/Salary_Management_ver.1.0.1"를 그어보겠습니다

$git tag -a {tag id 입력} -m "{사용할 메세지 입력}"
예제 사용 명령어 :  git tag -a ERP_HR/Salary_Management_ver.1.0.1 -m "ERP_HR/Salary_Management_ver.1.0.1"

그림 2. git tag 작성하기

 

 

 

2. tag 확인하기

이상이 없이 태그가 작성되면 작성되었는지 확인 합니다.
확인 명령어는 : git tag

그림 3. git tag 확인하기

 

3. 작성한 tag commit log에서 확인

이제 다시 commit log를 보면 아래 사진과 같이 tag가 표기 됩니다.
참고로 tag log까지 같이 보려면 $git log --decorate 이렇게 옵션을 줘야 표기 됩니다.

그림 4. commit log에서 작성한 tag 보기

 

 

 

4. tag push 하기

현재는 저의 작업 폴더에만 tag가 작성된것 이기 때문에, git tag를 작성해도 Remote로 Push 해주어야 합니다.

명령어는 일반 push와 동일합니다만, 작성한 tag만 push할 경우
$ git push {remote name} {tag id}
로 작성 합니다.

위 예제의 경우
$git  push origin ERP_HR/Salary_Management_ver.1.0.1
라고 입력 하면 되겠져?

그림 5. tag push

이렇게 뜨면 완료!

 

tag의 장점은 checkout할 경우 tag id로도 가능하다는 것 입니다.
그래서 체크 포인트를 잘 작성하면 트러블 이슈 관리에 도움이 되겠죠!

 

 

'IT > Git' 카테고리의 다른 글

reset, revert, stash  (0) 2020.05.03
Git submodule?  (0) 2020.03.07
Git 개념 (2) - pull / commit / push  (0) 2019.05.22
기존에 있던 프로젝트 Git으로 관리하기  (0) 2019.05.20
Git 개념 - init, clone  (1) 2019.05.20

BFS의 가장 기초인 최단거리 구하기문제!

이 문제를 통해서 최단거리 구할때는 BFS가 유리하다는 것을 깨닫게 되었습니다!

 

일단 문제 풀기

 

1. 문제

N×M크기의 배열로 표현되는 미로가 있다.

1 0 1 1 1 1
1 0 1 0 1 0
1 0 1 0 1 1
1 1 1 0 1 1

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

입력

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

출력

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.

 

 

2. 코드 구현

 

package java_BFS_DFS_basic;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;

class location{
	int w,h,num;
	
	location(int h, int w,int num){
		this.h = h;
		this.w = w;
		this.num = num;
	}
}
public class findMinPath {
	
	static Queue<location> q = new LinkedList<location>();
	static Stack<location> s = new Stack<location>();
	public static void findPath_dfs(int[][] map, boolean[][] ck,int sum) {
	
		location start;
		while(!s.isEmpty()) {
			start = s.peek();
			map[start.h][start.w] = start.num;
			s.pop();
			
			
			//아래로 이동
			if(start.h+1 < map.length && !ck[start.h+1][start.w] && map[start.h+1][start.w] !=0) {
				s.push(new location(start.h+1,start.w,map[start.h][start.w]+1));
				ck[start.h+1][start.w] = true;
			}
			//오른쪽 이동
			if(start.w+1 < map[start.h].length && !ck[start.h][start.w+1] && map[start.h][start.w+1] !=0) {
				s.push(new location(start.h,start.w+1,map[start.h][start.w]+1));
				ck[start.h][start.w+1] = true;
			}
			//왼쪽 이동
			if(start.w-1 >= 0 && !ck[start.h][start.w-1] && map[start.h][start.w-1] !=0) {
				s.push(new location(start.h,start.w-1,map[start.h][start.w]+1));
				ck[start.h][start.w-1] = true;
			}
			//위로 이동
			if(start.h-1 >= 0 && !ck[start.h-1][start.w] && map[start.h-1][start.w] != 0) {
				s.push(new location(start.h-1,start.w,map[start.h][start.w]+1));
				ck[start.h-1][start.w] = true;
			}
			
		}
	}
	
	public static void findPath_bfs(int[][] map, boolean[][] ck,int sum) {
		location start;
		while(!q.isEmpty()) {
			start = q.peek();
			map[start.h][start.w] = start.num;
			q.poll();
			
			
			//아래로 이동
			if(start.h+1 < map.length && !ck[start.h+1][start.w] && map[start.h+1][start.w] !=0) {
				q.add(new location(start.h+1,start.w,map[start.h][start.w]+1));
				ck[start.h+1][start.w] = true;
			}
			//오른쪽 이동
			if(start.w+1 < map[start.h].length && !ck[start.h][start.w+1] && map[start.h][start.w+1] !=0) {
				q.add(new location(start.h,start.w+1,map[start.h][start.w]+1));
				ck[start.h][start.w+1] = true;
			}
			//왼쪽 이동
			if(start.w-1 >= 0 && !ck[start.h][start.w-1] && map[start.h][start.w-1] !=0) {
				q.add(new location(start.h,start.w-1,map[start.h][start.w]+1));
				ck[start.h][start.w-1] = true;
			}
			//위로 이동
			if(start.h-1 >= 0 && !ck[start.h-1][start.w] && map[start.h-1][start.w] != 0) {
				q.add(new location(start.h-1,start.w,map[start.h][start.w]+1));
				ck[start.h-1][start.w] = true;
			}
			
		}
		
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//int[][] map = {{1,1,1,1},{0,0,1,0},{1,1,1,0},{0,0,1,2}};
		Scanner sc = new Scanner(System.in);
		int n,m;
		n = sc.nextInt();
		m = sc.nextInt();
		
		String[] temp = new String[n];
		int map[][] = new int[n][m];
		for(int i=0; i<n; i++) {
				temp[i] = sc.next();
		}
		
		for(int i=0; i<n; i++) {
			for(int j=0; j<m; j++) {
				map[i][j]=Integer.parseInt(temp[i].substring(j, j+1));
			}
		}
		  
		
		boolean[][] ck = new boolean[n][m];
		
		/*DFS 탐색*/
		//s.push(new location(0,0,1));
		//ck[0][0] = true;
		//findPath_dfs(map,ck,1);
		
		/*BFS 탐색*/
		q.add(new location(0,0,1));
		ck[0][0] = true;
		findPath_bfs(map,ck,1);
		
		System.out.println(map[n-1][m-1]);
		
		for(int i=0; i<n; i++) {
			for(int j=0; j<m; j++) {
				System.out.print(map[i][j]+" ");
			}
			System.out.println();
		}
	}
}

 

 

3. 문제 풀이

 

이 문제를 보면 왜 BFS가 최단거리 탐색에 유리한지 알 수 있습니다.

그림을 볼 필요도 없이..
최단거리에 BFS가 유리한 이유는 사실 DFS는 한 방향만 있는힘껏 탐색하기 때문입니다.

 

아래 그림과 같은 문제가 있다고 가정하겠습니다.

그림 1. 미로 문제

 

 

 

3-1. BFS 탐색

먼저 BFS 탐색을 보겠습니다.

BFS는 큐(Queue)로 구성되어 있기 때문에 분기점으로 나누어져도 분할된 채로 한칸씩 이동합니다.

(1,0) 지점에서 2갈래 길로 나누어지는 시점에서 큐의 상태를 보겠습니다.

그림 2. BFS 탐색 (1)

 

위 그림과 같이 코드에 작성했던 순서대로 아래->오른쪽 탐색을 통해 (2,0)과 (1,1) 두개가 큐에 삽입 됩니다.

그 다음 poll 값 2,0에서 탐색을 진행합니다.

그림 3. BFS 탐색 (2)

 

(2,0)에서는 (3,0)만 갈 수 있으므로, 큐에 (3,0)을 삽입 후 다음으로 진행합니다.

그 다음 poll 값은 (1,1) 입니다.

그림 4. BFS 탐색(3)

 

(1,1)에서 갈 수 있는 길은 (1,2)뿐 이므로 (1,2)를 큐에 삽입합니다.

이런식으로 동작하기 때문에 BFS는 분할 되어도 하나씩 진행이 가능한 것이죠.

 

 

 

3-2. DFS 탐색

같은 시점 DFS의 상태를 보겠습니다.

DFS는 스택으로 구현되기 때문에 아래 그림과 같이 동작합니다.

 

그림 5. DFS 탐색(1)

마찬가지로 (1,0)에서 갈 수 있는 2가지 길 모두 Stack에 Push 합니다.
설정한 탐색 순서에 따라 (2,0), (1,0)을 삽입 합니다.

그 다음 하나를 pop을 합니다.

그림 6. DFS 탐색(2)

(1,1)에서 갈 수 있는 (1,2)를 stack에 push 합니다.

그리고 하나를 pop 합니다.

 

그림 7. DFS 탐색(3)

 

이렇듯 DFS 탐색은 한쪽 길을 끝까지 탐색합니다.

 

 

 

4. 결과

결과적으로 BFS/DFS 탐색은 아래 그림과 같은 결과를 보여줍니다.

 

그림 8. DFS BFS 결과 값

 

 

위 결과에서 보여주듯이 DFS 탐색은 한 방향을 쭉 탐색하기 최소 거리 구하기에는 적합하지 않습니다.

그래서 최소거리를 구할땐 BFS를 사용하는 것이죠!

 

'알고리즘 공부 > 알고리즘 문제' 카테고리의 다른 글

트리 dfs 탐색  (0) 2021.04.23
나누어 떨어지는 숫자 배열  (0) 2019.03.12
문자열 내 맘대로 정렬하기  (0) 2019.03.12
빙고 개수 카운트하기  (0) 2019.01.31
배열 회전 결과값 확인하기  (1) 2019.01.26

 

DFS / BFS 탐색 알고리즘 코드로 구현하기

 

알고리즘은 형태로 구현합니다.

1. Queue/Stack 에 있는 노드를 꺼내 출력한다.
2. 방금 꺼낸 노드의 자식들을 Queue/Stack에 add/push 한다.
3. Queue/Stack이 비어있지 않다면 1번으로 이동

 

 

 

1. 행렬 구현

아래와 같잉 이진트리를 행렬로 표현하면 그림 1의 표의 내용과 같습니다.

그림 1. 이진 트리 행렬로 표현

 

 

 

2. 코드 구현

그림 1의 내용을 하나씩 탐색하는 코드는 아래와 같습니다.

package java_BFS_DFS_basic;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class Matrics_DFS_BFS {
	static Stack<Integer> s = new Stack<Integer>();
	static Queue<Integer> q = new LinkedList<Integer>();
	
	public static void bfs(int start, int[][] map, boolean[] visitied) {
		
		// Queue가 빌때까지 반복
		while(!q.isEmpty()) {
			start = q.peek();
			System.out.print((q.poll()+1)+" ");
			// 1. 자식 노드를 모두 Queue에 삽입
			for(int i=0; i<map.length; i++) {
				if(map[start][i] == 1 && !visitied[i]) { q.add(i); visitied[i] = true;}
			}
		}
	}
	
	public static void dfs(int start, int[][] map, boolean[] visitied) {
		//범위 넘어가면 return
		if(start < 0 || start > map.length) {return ;}
		
		// 1. 출력한다.
		System.out.print((s.pop()+1) +" ");
		
		// 2. 자식 노드를 모두 Stack에 삽입
		for(int i=0; i<map.length; i++) {
			if(map[start][i] == 1 && !visitied[i]) { s.push(i); visitied[i] = true;}
		}
		
		// 3. Stack이 빌때까지 반복
		if(!s.isEmpty()) {
			dfs(s.peek(),map,visitied);
		}
		
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		int[][] map = {{1,1,1,0,0,0,0},{1,1,0,1,1,0,0},{1,0,1,0,0,1,1},{0,1,0,1,0,0,0},{0,1,0,0,1,0,0},{0,0,1,0,0,1,0},{0,0,1,0,0,0,1}};
		boolean[] visitied; 
		
		System.out.println("DFS 시작");
		visitied = new boolean[map.length];
		s.push(0);
		visitied[0] = true;
		dfs(0,map,visitied);
		System.out.println();
		System.out.println();
		
		System.out.println("BFS 시작");
		visitied = new boolean[map.length];
		q.add(0);
		visitied[0] = true;
		bfs(0,map,visitied);
	}

}

 

 

3. 결과 값

그림 2. 행렬 순회 결과

 

이렇게 해서 BFS DFS 탐색은 끝!

'알고리즘 공부 > 코딩테스트 준비' 카테고리의 다른 글

진수 변환  (0) 2019.05.20
피보나치 수열  (0) 2019.05.20
행렬의 회전  (0) 2019.05.20

DFS에 이어서 BFS 개념 보기

DFS는 트리 행부터 탐색(=세로로 탐색)하는 반면
BFS는 트리 열부터 탐색(=가로로 탐색) 하는 방법 입니다.

 

구현 부분에서는 DFS랑 다를게 없습니다. 단지, Stack -> Queue로 변경되는 것 뿐이죠.

 

----------------------오늘의 요약 정리 ---------------------

BFS 알고리즘 순서!
1. Queue에 있는 노드를 꺼내 출력한다.
2. 방금 꺼낸 노드의 자식들을 Queue에 Add 한다.
3. Queue가 비어있지 않다면 1번으로 이동

---------------------------------------------------------------

 

 

바로 그림으로 정리하겠습니다.

이진 트리 BFS 순회는 아래 트리를 이용해서 진행하겠습니다.

그림 1. 예제로 사용 할 이진 트리

 

 

 

 

시작

알고리즘 순서는 간단합니다.

1. Queue에 있는 노드를 꺼내 출력한다.
2. 방금 꺼낸 노드의 자식들을 Queue에 Add 한다.
3. Queue가 비어있지 않다면 1번으로 이동

 

 

1부터 방문할 것이기 때문에 1을 Queue에 삽입한 상태로 시작합니다.

그림 1. BFS 탐색(1)

 

 

 

 

1. Queue에 있는 노드를 하나 꺼내면서 출력합니다.

그림 2. BFS 탐색(2)

 

 

 

2. 노드 1을 방문처리 하고, 1의 자식 노드를 Queue에 삽입한다.

 

그림 3. BFS 탐색(3)

 

 

 

3. 자식노드 2,3을 방문처리 후 Queue에서 하나 꺼내 출력합니다.

그림 4. BFS 탐색(4)

 

 

 

 

4. 꺼낸 노드 2의 자식들을 Queue에 삽입 합니다.

그림 5. BFS 탐색(5)

 

 

 

5. Queue에 삽입한 노드를 방문처리 후 Queue에서 다시 하나 꺼내 출력 합니다.

그림 6. BFS 탐색(6)

 

 

 

 

6. 꺼낸 3의 자식노드를 Queue에 삽입한다.

그림 7. BFS 탐색(7)

 

 

 

 

7. 삽입한 노드를 방문처리 후 Queue에 있는 노드(4)를 하나 꺼내 출력합니다.

그림 8. BFS 탐색(8)

 

 

 

 

8. 자식노드가 없기때문에 다시 Queue에서 노드(5)를 하나 꺼내서 출력합니다.

그림 9. BFS 탐색(9)

 

 

 

 

9. 5는 자식이 없기때문에 Queue에서 다음 노드(6)을 꺼내 출력합니다.

그림 10. BFS 탐색(10)

 

 

 

 

 

10. 6은 자식 노드가 없기 때문에 Queue에서 다음 노드(7)를 꺼내서 출력합니다.

그림 11. BFS 탐색(11)

 

 

 

 

 

(마지막)11. Queue에 더이상 값이 없으므로, 탐색을 종료합니다!

그림 12. BFS 탐색(12)

 

 

이렇듯 BFS 탐색에 대해 알아보았습니다.

알고리즘 순서만 지킨다면 어렵지 않은 알고리즘입니다.

 

코드로 구현하는 것은 아래 링크로 연결하겠습니다!

https://mr-dan.tistory.com/45

 

저번 Union Find에 이어서

Kruskal 알고리즘 정리하겠습니다.

 

목차

- 크루스칼 알고리즘 이란?
- 예제를 통해 크루스칼 이론 학습하기
- 코드로 구현하기

 

- 6줄 요약 -
크루스칼 알고리즘은 최소비용 신장트리를 만드는 알고리즘이다. 알고리즘은 다음과 같다.

1) 간선 가중치 중 가장 작은 값을 선택한다.
2) 간선을 연결하여 순환(Cycle)이 생기지 않는다면 연결 후 1)로 이동.
3) 연결 시 순환(Cycle)이 생기면 연결하지 않고 1)로 이동.
(순환이 여부는 Union Find 알고리즘을 활용하여 알아냅니다!)
최소비용 신장트리의 간선의 수는 "노드의 수 - 1" 

 

시작 합시다!

 

 

1. Kruskal 알고리즘이란?

Kruskal 알고리즘이란 모든 노드를 싸이클이 없도록 연결하는 트리를 만들기 위해 사용되는 알고리즘 입니다.

즉, 최소비용 신장트리를 찾는 알고리즘 이죠.

 

각 노드를 연결하는 간선에는 가중치가 있고, 해당 가중치 중 가장 적은 비용으로 모두 연결해야 할때 이 알고리즘을 사용합니다.

아래 예시를 보면서 이해를 해보져

그림 1. 크루스칼 알고리즘 예제

 

먼저 그림 1과 같이 5개의 노드가 있고, 연결 된 간선에는 가중치가 포함되어있을때,

크루스칼 알고리즘을 이용해서 가장 적은 비용으로 최소신장 트리만드는 방법을 알아보겠습니다.

 

크루스칼 알고리즘 적용 방식은 아래 3가지 입니다.

1) 간선 가중치 중 가장 작은 값을 선택한다.
2) 간선을 연결하여 순환(Cycle)이 생기지 않는다면 연결 후 1)로 이동.
3) 연결 시 순환(Cycle)이 생기면 연결하지 않고 1)로 이동.
(순환이 여부는 Union Find 알고리즘을 활용하여 알아냅니다!)

간단하죠?

그림 1 예제를 통해 진행하겠습니다.

 

 

 

2. 예제를 통해 크루스칼 이론 학습하기

그림 1 그래프를 최소 비용으로 연결한다면 아래 그림 2와 같이 연결 될 것 입니다. 

그림 2. 예상 결과 값

 

그럼 이렇게 연결이 되는지 확인해봅시다.

보기 편하도록 아래 그림 3과 같이 정리하고 시작할게요

그림 3. 최소비용 신장트리 만들기

 

 

최소비용 신장트리 만들기 시작!

2-1. 우선 첫번째로 가장 작은 가중치를 갖는 간선  '2<->5'를 선택 후 연결합니다.

그림 4. 최소비용 신장트리 만들기(1)

 

2-2. 이제 계속 반복 합니다. 다음 가장 작은 가중치 '4 <-> 5'를 선택하고 연결 합니다.

그림 5. 최소비용 신장트리 만들기(2)

 

2-3. 다음 작은 가중치 '1 <-> 3'을 선택하고 연결합니다.

그림 6. 최소비용 신장트리 만들기(3)

 

2-4 다음 작은 가중치 '2 <-> 4'를 선택합니다.

그런데! 2와 4를 연결하면 2 - 4 - 5 노드에 순환이 생기기 때문에 넘어가고 그 다음 작은 값 '1 <-> 2'를 선택 합니다.

그림 7. 최소비용 신장트리 만들기(4)

 

2-5 이제 어떤 간선을 선택해도 Cycle이 생성됩니다. 그렇기 때문에 최소비용 신장트리가 완성되었기에
종료!


그림 8. 최소비용 신장트리 만들기 (완성)

 

맨 처음 예상했던 결과값이랑 동일한 것을 확인할 수 있습니다.

그럼 바로 코드 구현으로 들어가 보져

 

 

 

3. 코드 구현하기

사실 크루스칼 알로리즘은

Union Find 알고리즘에  
 * 1. 간선의 최솟값을 찾기 => Arrays.sort (정렬 이용)
 * 2. 정렬된 배열 순서대로, 간선을 연결할 때 Cycle이 생기지 않으면 연결하도록 하기

를 추가하면 Kruskal 알고리즘이 됩니다.
(아래 예제는 최소 비용 신장트리의 간선의 합을 구하는 코드 입니다.)

	//부모 노드를 찾을 수 있는 함수
	public int getParent(int parent[],int x) {
		if(parent[x] == x) return x;
		return parent[x] = getParent(parent,parent[x]);
	}
	//노드 연결해서 부모 값 작은걸로 변경하기
	public int[] changeParent(int parent[], int a, int b) {
		a = getParent(parent, a);
		b = getParent(parent, b);
		if(a<b) parent[b] = a;
		else parent[a] = b;
		
		return parent;
	}
	//부모 값이 동일한지 확인
	public boolean findParent(int parent[], int a, int b) {
		a = getParent(parent, a);
		b = getParent(parent, b);
		if (a==b) return true;
		return false;
	}
	
	
	public int solution(int n, int[][] costs) {
        int answer = 0;
        int[] parent = new int[n]; //1,2,3,4,5,6
        for(int i=0; i<n; i++) {parent[i] = i;}
        
        //간선 정렬
        Arrays.sort(costs, new Comparator<int[]>() {
			@Override
			public int compare(int[] arg0, int[] arg1) {
				//오름차순 정렬
				return arg0[2]-arg1[2];
			}
        });
        
        //간선 오름차순으로 정렬되었기 때문에 Cycle이 아니라면 하나씩 더해주면 됨.
        //0->1= 1 , 0->2 =2, 2->3 = 1 인경우 배열에 3의정보는 주지 않기때문에 costs.length 기준으로 하는것임
        for(int i=0; i<costs.length; i++) {
        	//Cycle이 아닌경우 간선 가중치 더하기
        	if(!findParent(parent,costs[i][0],costs[i][1])) {
        		//연결하기
        		System.out.print(costs[i][2]+" ");
        		answer += costs[i][2];
            	changeParent(parent,costs[i][0],costs[i][1]);
        	}
        }
        System.out.println();
        System.out.println(answer);
        
        return answer;
    }

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		island_connection ic = new island_connection();
		int n = 5;
		int[][] costs = {{0,1,3},{0,2,4},{0,3,9},{1,3,3},{1,4,1},{2,3,5},{3,4,2}};
		ic.solution(n, costs);
	}

 

실행하면 결과 값 : 10이 나오는 것을 확인할 수 있습니다.

 

이렇게 하면 Kruskal 알고리즘 정리 완료!

+ Recent posts