집합을 표현할 때 사용하는 유니온 파인드(Union Find)와 서로소 집합(Disjoint Set)에 대해서 알아보자


환경 및 선수조건

  • C++


유니온 파인드(Union Find)란?

정의

  • 유니온 파인드(Union Find): 서로소 집합(Disjoint Set) 그리고 병합 찾기 집합(Merge Find Set)이라고도 불리며 여러 서로소 집합의 정보를 저장하고 있는 자료구조를 의미합니다.

예시

  • 다음 아래와 같이 원소들이 있다고 가정을 했을 때


  • 각각의 원소들이 어떤 원소들과 연결이 되어있는지 입력을 받는다고 가정하면(1-2, 2-5, 5-6, 5-8, 3-4 이런 방식으로) 아래와 같이 3개의 서로소 집합이 나올 수 있다.


구현

원리

  • 배열을 이용해서 Tree 자료구조를 만들어 구현하며 위에 나와 있는 {1, 2, 5, 6, 8}, {3, 4}, {7}의 경우에는 아래처럼 표현 및 구현이 가능합니다.
  • 즉, 최상단 노드인 Root노드를 집합을 구분하는 ID처럼 생각하면 이해가 더 편합니다.

Tree

  • 주어진 두 원소 또는 집합을 합하는 Union부분과 원소가 어떤 집합에 있는지 찾는 Find함수로 이루어져있다.
  • parent[i]: 원소 i의 부모 노드를 가지고 있는 배열, parent[i]i의 부모 노드

초기화

  • 처음에 각각의 원소들은 연결된 정보가 없기 때문에 부모로 자기 자신을 가지고 있다.
  • 즉, parent[i] = i;

for(int i=1; i<=n; i++){
    parent[i] = i;
}

Find 함수

  • Find(x) 함수: x로 들어온 원소의 Root노드를 반환

int Find(int x){

    // Root인 경우 x를 반환
    if(x == parent[x]){
        return x;
    }
    else{
        // 아니면 Root를 찾아감
        int p = Find(parent[x]);
        parent[x] = p;
        return p;
    }

}

Union 함수

  • Union(x,y) 함수: x원소와 y원소를 합치는 함수로 y의 Root노드를 x로 한다.

// x와 y의 원소가 들어오면
// 각각 x에는 들어온 x의 Root 노드 y에는 들어온 y의 Root 노드를 저장해서 비교하고
// x에 y를 붙이는 방식 -> y의 Root 노드를 x로 설정

void Union(int x, int y){

    x = Find(x);
    y = Find(y);

    if(x!=y){
        parent[y] = x;
    }
}


관련 문제 및 예제


#include <iostream>
#include <stdio.h>

using namespace std;

int parent[1000001];

int Find(int x){

	if(x == parent[x]){
		return x;
	}
	else{
		int y = Find(parent[x]);
		parent[x] = y;
		return y;
	}

}

void Union(int x, int y){

	x = Find(x);
	y = Find(y);

	parent[y] = x;

}

int main(){

	int n,m;

	scanf("%d %d", &n, &m);

	// 배열 생성 및 초기화
	// 처음에는 자기 자신이 부모이다 -> 모두 떨어져있기 때문
	for(int i=0; i<=n; i++){
		parent[i] = i;
	}

	int op, a, b;
	while(m--){
		scanf("%d %d %d", &op, &a, &b);

		if(op == 0){
			Union(a,b);
		}

		else if(op == 1){
			int a_parent = Find(a);
			int b_parent = Find(b);

			if(a_parent == b_parent){
				printf("YES\n");
			}
			else{
				printf("NO\n");
			}
		}
	}
	return 0;
}


#include <stdio.h>
#include <iostream>

using namespace std;

int computers;
int connections;

int parent[101];

int Find(int x){

	if(x == parent[x]){
		return x;
	}
	else{
		int y = Find(parent[x]);
		parent[x] = y;
		return y;
	}

}

void Union(int x, int y){

	x = Find(x);
	y = Find(y);

	if(x!=y){
		parent[y] = x;
	}


}

int main() {

	scanf("%d", &computers);
	scanf("%d", &connections);

	// 초기화
	// 처음에는 각각 노드가 부모노드가 자기자신
	for(int i=0; i<=computers; i++){
		parent[i] = i;
	}

	int u,v;
	while(connections--){
		scanf("%d %d", &u, &v);

		Union(u,v);
	}

	// Root 노드가 1인 노드를 검색해서 cnt에 더해줌
	int cnt = 0;
	for(int i=2; i<=computers; i++){
		if(Find(1) == Find(i)) cnt++;
	}

	printf("%d\n", cnt);

	return 0;
}


참고자료

쉘에서 두 명령어의 차이를 알아보자.


환경 및 선수조건

  • Shell Command


>와 »의 차이

Linux를 기반으로 한 환경에서

  • >: 명령어 뒤에 나오는 파일에 쓸 때 사용(=write or overwrite)

  • >>: 명령어 뒤에 나오는 파일에 추가할 때 사용(=append)


예시

  • >의 경우

다음 아래 같이 사용하면 test.txt라는 파일이 없을 때는 생성하며 있다면 내용을 덮어쓰게 됩니다.

$ echo abcde > test.txt
  • >>의 경우

다음 아래 같이 사용하면 test.txt라는 파일이 없을 때는 생성하며 있다면 test.txt 파일에 내용을 추가하게 됩니다.

$ echo abcde >> test.txt


참고자료

업데이트(2021.03.26): 옥텟(octet) 관련 설명 추가

웹페이지에서 URL에 사용되는 문자열을 encode하고 decode 해보자.


환경 및 선수조건

  • Javascript


Percent-encoding

  • Percent-encoding이란 URI 혹은 URL에 문자를 표현하는 인코딩 방식으로 RFC 3986에 따라서 알파벳이나 숫자 등 몇몇 문자를 제외한 문자들에 대해서 옥텟(octet) 값으로 묶어서 16진수 값으로 코딩하는 방식
  • 옥텟(octet): 컴퓨팅에서 8개의 비트가 한데 모인 것을 의미하며 보통 1바이트를 의미한다.

  • 예시: "/internet url" -> "internet%20url"


encodeURI()와 decodeURI() 함수

  • encodeURI(): 일반 문자열을 퍼센트 인코딩된 문자열로 변환
  • decodeURI(): 인코딩된 문자열을 일반 문자열로 변환

var uri = "my test.asp?name=ståle&car=saab";
var enc = encodeURI(uri);
var dec = decodeURI(enc);

console.log(enc); //"my%20test.asp?name=st%C3%A5le&car=saab"
console.log(dec); //"my test.asp?name=ståle&car=saab"


참고자료

Python에서 연산자 ‘/’와 ‘//’의 차이를 알아보자


환경

  • Python


연산자 ‘/’와 ‘//’의 차이

  • /는 나눗셈을 의미하며 결과가 float로 나타납니다.
  • //는 나눗셈을 의미하며 결과가 int로 나타납니다.


코드

  • /의 경우
>>> type(5/2)
<class 'float'>
  • //의 경우
>>> type(5//2)
<class 'int'>

주어진 문자열이 palindrome(회문)인지 확인하는 코드를 작성해보자


환경 및 선수조건

  • Python
  • C++


Palindrome(회문)이란?

  • Palindrome(회문)은 문자열을 거꾸로 해도 원래의 문자열과 같은 문자열인 특징을 가지고 있는 문자열을 가리키는 말입니다.
  • 예시: 토마토, abdba, 토마토맛토마토, 1234567654321


코드

  • 시간복잡도: O(n)


C


#include <stdio.h>
#include <string.h>

int is_palindrome(char * s){

	int len = strlen(s);

	int left = 0;
	int right = len-1;


	// 왼쪽과 오른쪽을 하나씩 가운데로 향하면서 비교
	// (right - left) > 1
	// [홀수 길이의 경우] 가운데 원소만 남겨두거나
	// [짝수 길이의 경우] "left + 1==right"일 때까지!
	while ( (right - left) > 1 ) {

		if( s[right] != s[left] ){
			return 0;
		}
		left += 1;
		right -= 1;
	}

	return 1;

}


int main (){


	char * palindrome = "abcdcba";
	char * non_palindrome = "abcdefg";

	printf("%d\n", is_palindrome(palindrome));
	printf("%d\n", is_palindrome(non_palindrome));

	return 0;

}


C++


#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

bool is_palindrome(string s){

	string s_reverse = s;
	reverse(s_reverse.begin(), s_reverse.end());
	return s == s_reverse ? true : false;

}


int main (){

	string s1 = "abcde1edcba";
	string s2 = "asdlkfjaw;oefjao;iwefja;ow";

	cout << is_palindrome(s1) << '\n';
	cout << is_palindrome(s2) << '\n';

	return 0;

}


Python


def is_palindrome(s):
	return s == s[::-1]


s1 = "abcde1edcba"
s2 = "fjaw;"

print(is_palindrome(s1))
print(is_palindrome(s2))


참고자료