상호 배타적 집합

모집합이 여러 작은 집합들로 나뉜 상황을 표현하는 독특한 트리

상호 배타적 집합(Disjoint-Set)을 표현하는 유니온 파인드(Union-Find) 자료구조

유니온-파인드는 '합집합 찾기'라는 의미를 가지고 있다.

서로소 집합 알고리즘 이라고도 한다. (1외에 공약수 없음)

n 명의 사람 중에서 생일이 같은 사람들끼리 팀을 구성하는 상황을 생각해보자.

생일이 여러 달에 속한 사람은 없다. 따라서 2개 팀에 속한 사람이 없다.

유니온 파인드 자료구조는 상호 배타적인 부분 집합들로 나눠진 원소들을 저장하고 조작하는 자료구조다.

유니온 파인드 연산 3가지

n명의 사람을 0번부터 n-1번 까지의 원소로 표현한다.

각 1개의 원소를 포함하는 n개의 집합을 만든다. (초기화)

두 사람 a, b가 생일이 같다는 것을 확인 할 때마다 두 사람이 포함된 집합이 합쳐진다.(Union 연산)

어떤 원소 b가 주어졌을 때, 이 원소가 속한 집합을 반환한다. (Find 연산)

트리로 상호 배타적 집합 표현하기

  • 한 집합에 속하는 원소를 하나의 트리로 묶어준다.
  • 트리의 루트에 있는 원소를 각 집합의 대표라고 부른다.
  • 각 트리의 루트를 찾은 뒤, 하나를 다른 한쪽의 자손으로 넣으면 된다.각 원소가 포함된 트리의 루트를 찾은 뒤 이들이 같은지 비교하기
  • 루트가 같다면 같은 트리에 속한 것
  • 두 원소가 같은 트리에 속해 있는지 어떻게 확인할까?

트리를 이용한 상호 배타적 집합의 표현

  • Find(찾기) 연산루트는 부모가 없으므로 자기 자신을 가리킨다.
  • 3의 부모는 4, 4의 부모는 5, 5의 부모는 5 자신이다. 재귀함수로 구현한다.
  • 모든 자식 노드가 부모에 대한 포인터를 가져야 한다.
  • Union(합치기) 연산한 노드를 다른 노드의 자식으로 넣기 전에 먼저 양 트리의 루트를 찾아야 한다.
  • 각 트리의 루트를 찾고, 하나를 다른 한쪽의 자손으로 이어준다.

그림으로 보는 Union-Find (벡터로 구현)

  1. 초기화노드 i의 부모 노드를 P[i]에 저장한다.
  2. 처음에 모든 노드의 부모는 자기 자신이다.
  3. 초기화
  4. Union
    )
    4의 부모는 3으로 변경된다.
    )
    Union 연산이 끝난 후, 배열은 다음과 같이 갱신된다.
  5. 1, 2, 3의 부모는 모두 1이므로, 세 정점은 같은 집합에 속한다.
  6. 3의 부모는 2가 된다.
  7. 1, 2, 3이 연결된다면?
  8. 2의 부모는 1로 변경된다.
  9. Union(1, 2) , Union(3, 4) 연산으로 노드를 2개씩 합친다.
  10. Find
  11. 자신이 속한 집합의 루트를 알아내기 위해 재귀 함수가 사용된다.

(코드) 트리로 상호 배타적 집합 구현


struct NaiveDisjointSet{

    vector<int> parent; // 자신의 부모의 번호를 저장  

    NaiveDisjointSet(int n): parent(n){
        for(int i = 0; i < n; i++){ // 처음에는 자기 자신이 자신의 부모다. (혼자니까)
            parent[i] = i; // 자신이 어떤 부모에 포함됬는지 기록 
        }
    } 

    // u 가 속한 트리의 루트의 번호를 찾아서 반환  // 트리의 높이에 비례하는 시간이 걸린다
    int find(int u) const{
        if(u == parent[u]){ // u가 부모라면 
            return u;
        }

        return find(parent[u]);
    } 

    // u가 속한 트리와 v가 속한 트리를 합친다  
    void merge(int u, int v){

        u = find(u); // 양 트리의 루트를 찾아낸다 
        v = find(v);

        if(u == v) return; // u와 v가 같은 트리에 속한 경우 종료. 

        parent[u] = v; 
    } 

}; 

union은 예약어이므로 함수명으로 사용할 수 없다.

상호 배타적 집합의 최적화

  • 트리를 사용하니까 연산 순서에 따라 한쪽으로 기울어질 수도 있다는 문제가 있다.
  • 그러면 Find 연산도 Union 연산도 시간 복잡도 O(n)이 된다.

랭크에 의한 합치기(union-by-rank)

기울어진 트리를 피하기 위해, 항상 높이가 더 낮은 트리를 더 높은 트리 밑에 집어넣자.

(코드) 최적화된 상호 배타적 집합의 구현

rank[] 는 해당 노드가 한 트리의 루트인 경우, 해당 트리의 높이를 저장하는 배열

두 노드를 합칠 때, 높이가 낮은 쪽을 높은 쪽 트리의 서브트리로 포함시킨다.

두 트리의 높이가 같다면, 결과 트리의 높이를 1 증가시킨다.


struct OptimizedDisjointSet{

    vector<int> parent; // 자신의 부모의 번호를 저장  
    vector<int> rank;   // 높이를 저장  

    OptimizedDisjointSet(int n): parent(n), rank(n, 1){
        for(int i = 0; i < n; i++){
            parent[i] = i;
        }
    } 

    // u 가 속한 트리의 루트의 번호를 찾아서 반환 
    int find(int u) const{
        if(u == parent[u]){ // u가 부모라면 
            return u;
        }

        return parent[u] = find(parent[u]); // 재귀호출 하면서 parent벡터 업데이트
    } 

    // u가 속한 트리와 v가 속한 트리를 합친다  
    void merge(int u, int v){

        u = find(u); // 양 트리의 루트를 찾아낸다 
        v = find(v);

        if(u == v) return; // u와 v가 같은 트리에 속한 경우 종료. 

        // v를 u의 자식으로 넣는다. 
        if(rank[u] > rank[v]) swap(u, v); 

        // u를 v의 자식으로 넣는다. 
        parent[u] = v; 

        // 높이가 같으면 결과 트리의 높이를 1 증가 
        if(rank[u] == rank[v]) ++rank[v];  

}; 

[출처]

트리를 이용한 상호 배타적 집합 그림 출처

유니온 파인드 그림 출처

유니온 파인드 개념

알고리즘 문제해결전략 (구종만 저) 트리 챕터 상호배타적집합 코드

728x90

'알고리즘 > 알고리즘 C' 카테고리의 다른 글

이진 탐색 트리 Binary Search Tree (linked list)  (0) 2020.03.18
이진 탐색  (0) 2020.03.18
수식 이진 트리 (Expression Binary Tree)  (0) 2020.03.11
이진 트리  (0) 2020.03.11
트리 기초  (0) 2020.03.11

오늘 한 일 

 

용역 요구사항 PT를 읽으며 분석을 했다.

Node.js 에서 글 목록 만들기, 본문 읽기 같은 라우팅을 처리하는 연습을 했다.  

express, jade, body-parser 사용에 조금 더 친해졌다.

간만에 oracle virtualbox를 재설치하고, ubuntu 16.04LTS를 설치했다. 

Node.js 와 npm 설치하고 우분투 다루는 방법을 다시 익히기 시작했다. 

 

 

생각거리

역시 요구사항 분석하고 확정짓는 것이 시간이 좀 걸릴 것 같다. 

내일도 화이팅!

:)

728x90

'일상 > Today I Learn(TIL)' 카테고리의 다른 글

2020-03-21 TIL  (0) 2020.03.21
2020-03-18 TIL  (0) 2020.03.18
2020-03-15 TIL  (0) 2020.03.15
2020-03-13 TIL  (0) 2020.03.13
2020-03-12 TIL  (0) 2020.03.12

MySQL

가장 대중적인 오픈소스 DB

MySQL은 SUN에 인수되었는데, SUN이 오라클에게 인수되면서 같이 넘어갔다.  

MySQL이 만든 분이 오라클의 정책에 반발하면서 MySQL과 완전히 호환되는 MariaDB를 만들게 된다.

 

MariaDB

MySQL의 지지부진한(?)개발을 개선하였고, 진보적으로 발전해온 오픈소스 DB

 

DB만 바꾸면, MySQL이 갖고 있었던 단점을 버리고 MariaDB의 장점을 취할 수 있다. 

 

아마존에서 만든 DB 오로라(Aurora) 도 MariaDB와 호환된다. 


Ubuntu 에서 MySQL 설치 

 

터미널에서 sudo apt-get install mysql  엔터치지 말고 tab을 두번 친다. 

현재 설치할 수 있는 server, client, workbench 등이 나온다. 

 

 

그 중에 최신 버전 server와 cilent 버전을 치고 설치한다.

sudo apt-get install mysql-server-5.7 mysql-client-5.7 

패스워드는 제일 쉽게 1을 6개 친다. (혼자쓰는 것이니까)

 

설치가 끝났으면 제대로 설치되었는지 확인하기 위해 로그인 해본다. 

 

패스워드를 치고 아래와 같이 welcome 이 뜨면 제대로 된 것이다. 

728x90

'프로그래밍 > Node.js' 카테고리의 다른 글

MySQL select insert delete update  (0) 2020.03.21
MySQL 사용  (0) 2020.03.18
코드 개선  (0) 2020.03.16
본문 읽기 - 전체 코드 포함  (0) 2020.03.16
글 목록 만들기  (0) 2020.03.16

라우트 

 

비슷한 기능을 하는 app.get() 함수 2개를 중복만 제거하고 하나로 합쳐보자. 

 

 

여러 경로를 한번에 처리 

 

get() 함수에서 파일명, 파일 내용을 가져오는 코드의 중복을 제거. 

 

새 글 쓰는 버튼을 누르면, 글목록 하위에 form 이 나오도록 jade 템플릿 변경

 

새 글을 쓰고 나서, 글을 제대로 썼는지 작성된 글 페이지로 '리다이렉션' 하여 이동.

res.redirect( 경로 ) 함수를 이용 

 

작성 후에 바로 작성내용을 볼 수 있다. 


app_file.js 

// 파일로 만든 웹앱 (03-15)

var express = require('express');
var app = express();
var bodyParser = require('body-parser')
var fs = require('fs')

// POST 방식 처리할 때, body 속성을 가공해준다 
app.use(bodyParser.urlencoded({extended: false}))

//템플릿 엔진 (views 들을 위치할 디렉토리 적어준다)
app.set('views', './views_file');
//템플릿 엔진 종류 명시 
app.set('view engine', 'jade');

app.locals.pretty = true;

app.get('/topic/new', function(req, res){
    res.render('new');
})


app.get(['/topic', '/topic/:id'], function(req,res){

    // 글 목록 보이기 
    fs.readdir('data', function(err,files){
        if(err){
            console.log(err);
            res.status(500).send('Internal Server Error')
        }
        var id = req.params.id

        // id 값이 있는지 없는지! 
        if(id){
            fs.readFile('data/'+id, 'utf8', function(err, data){
                // 파일 내용을 가져옵니다. 
                if(err){
                    console.log(err);
                    res.status(500).send('Internal Server Error')
                }
                // render 인자 : 템플릿파일명, 주입하려는 객체 
                // 파일목록, 제목, 내용
                res.render('view', {topics:files, title: id, description:data});
            })
        }else{ // id 값이 없으면, 
            res.render('view', {topics:files, title:'Welcome', description:'hello'})
        }
    })


})


// POST 처리
app.post('/topic',function(req, res){
    var title = req.body.title
    var description = req.body.description
    fs.writeFile('data/'+title, description, function(err){
        if(err){
            console.log(err)
            res.status(500).send('Internal Server Error')
        }// 콜백함수 실행완료 후에 응답 가능하므로, res.send 가 여기에 위치.
        res.send('Success!')
    });
})

// 애플리케이션이 특정 포트를 listen() 하도록.
app.listen(3000, function(){
    console.log('Connected 3000 port ! ');
}) 

 

view.jade

홈버튼과 새글쓰기 버튼을 추가함 

doctype html
html
    head
        meta(charset='utf-8')
    body
        h1
            a(href='/topic') Server Side JavaScript
        ul  
            each topic in topics
                li
                    a(href='/topic/'+topic)=topic
        article
            h2=title
            =description

        
        div 
            a(href='/topic/new') new

 

new.jade 

doctype html
html
    head
        meta(charset='utf-8')
    body
        h1
            a(href='/topic') Server Side JavaScript
        ul  
            each topic in topics
                li
                    a(href='/topic/'+topic)=topic

        article
            form(action='/topic', method='post')
                p
                    input(type='text' name='title' placeholder='title')
                p
                    textarea(name='description')
                p
                    input(type='submit')

                

 

728x90

'프로그래밍 > Node.js' 카테고리의 다른 글

MySQL 사용  (0) 2020.03.18
MySQL 설치  (0) 2020.03.16
본문 읽기 - 전체 코드 포함  (0) 2020.03.16
글 목록 만들기  (0) 2020.03.16
POST방식 GET방식  (0) 2020.03.15

아래 포스팅 '글 목록 만들기' 글과 이어지는 포스팅 입니다. 

 


1. GET 방식에 따라 URL 이 변경되는데 파일명을 받아옵니다. 

 

2. 글 목록을 보여줍니다. 

fs.readdir() 함수로 files 목록을 받아오고, 

jade 템플릿 view.jade 로 topics 이름의 files 목록을 주입합니다. 

 

3. http://127.0.0.1:3000/topic 로 접속하면 파일명 링크만 나옵니다.

 

파일명 링크를 누르면, URL이 http://127.0.0.1:3000/topic/javascript 로 변경되고, 파일 내용도 출력됩니다. 

 

파일명과 내용을 보여주는 view.jade 템플릿을 만듭니다.  

( article 부분만 추가했습니다) 

title, description 이름으로 넘어오는 변수값을 출력합니다. 

 

 

4.

 

2개의 기능이 동시에 필요합니다. 

파일명을 가져오고 파일 이름을 가져오는 함수 

render() 함수의 파라미터로 넘길 객체의 이름과 값을 {} 로 감싸고 콤마로 구분합니다. 

 

 


전체코드 

 

app_file.js

// 파일로 만든 웹앱 (03-15)

var express = require('express');
var app = express();
var bodyParser = require('body-parser')
var fs = require('fs')

// POST 방식 처리할 때, body 속성을 가공해준다 
app.use(bodyParser.urlencoded({extended: false}))

//템플릿 엔진 (views 들을 위치할 디렉토리 적어준다)
app.set('views', './views_file');
//템플릿 엔진 종류 명시 
app.set('view engine', 'jade');

app.locals.pretty = true;

app.get('/topic/new', function(req, res){
    res.render('new');
})

// 본문 읽기 
app.get('/topic/:id', function(req, res){
    var id = req.params.id

    fs.readdir('data', function(err,files){
        // 파일명을 가져옵니다.
        if(err){
            console.log(err);
            res.status(500).send('Internal Server Error')
        }

        fs.readFile('data/'+id, 'utf8', function(err, data){
            // 파일 내용을 가져옵니다. 
            if(err){
                console.log(err);
                res.status(500).send('Internal Server Error')
            }
            // render 인자 : 템플릿파일명, 주입하려는 객체 
            // 파일목록, 제목, 내용
            res.render('view', {topics:files, title: id, description:data});
        })
    })
    
})


// 글 목록 보이기 
app.get('/topic', function(req,res){
    fs.readdir('data', function(err,files){
        if(err){
            console.log(err);
            res.status(500).send('Internal Server Error')
        }
        // render 인자 : 템플릿파일명, 주입하려는 객체 
        res.render('view', {topics:files})
    })
})

// POST 처리
app.post('/topic',function(req, res){
    var title = req.body.title
    var description = req.body.description
    fs.writeFile('data/'+title, description, function(err){
        if(err){
            console.log(err)
            res.status(500).send('Internal Server Error')
        }// 콜백함수 실행완료 후에 응답 가능하므로, res.send 가 여기에 위치.
        res.send('Success!')
    });
})

// 애플리케이션이 특정 포트를 listen() 하도록.
app.listen(3000, function(){
    console.log('Connected 3000 port ! ');
}) 

 

템플릿 코드  view.jade

doctype html
html
    head
        meta(charset='utf-8')
    body
        h1 Server Side JavaScript
        ul  
            each topic in topics
                li
                    a(href='/topic/'+topic)=topic
        article
            h2=title
            =description

            

 

새 파일 만드는 new.jade 

doctype html
html
    head
        meta(charset='utf-8')
    body
        form(action='/topic', method='post')
            p
                input(type='text' name='title' placeholder='title')
            p
                textarea(name='description')
            p
                input(type='submit')

                

 

728x90

'프로그래밍 > Node.js' 카테고리의 다른 글

MySQL 설치  (0) 2020.03.16
코드 개선  (0) 2020.03.16
글 목록 만들기  (0) 2020.03.16
POST방식 GET방식  (0) 2020.03.15
쿼리스트링과 시맨틱URL  (0) 2020.03.15

파일을 이용하여 글 목록을 만들어보자. 

(나중에는 DB를 이용한다.)

 

form 화면에서 사용자의 입력을 받아서 파일을 만듭니다. 

파일의 이름과 파일의 내용으로 글의 목록이 만들어집니다. 

링크를 클릭하면 글의 내용이 바뀝니다. 


필요한 모듈 

 

express, body-parser, fs (파일시스템) 모듈

 

 

필요한 파일과 디렉토리 

 

app_file.js : entry point 파일

views_file 디렉토리 : jade 템플릿 파일 저장 

data 디렉토리 : 글목록을 만들기 위한 파일을 저장

 

 

 

1. 필요한 모듈을 require()로 가져옵니다. 

 

2. 포트 설정하여 서버 listen()

entry point 파일인 app_file 에서, 애플리케이션이 3000 번 port 를 listen 하도록 만듭니다. 

 

3. /topic 경로로 들어오면, 화면에 문자열을 출력해봅니다. 

URL로 접근하는 것이니까 app.get() 함수를 사용합니다. 

 

4. jade 템플릿 사용 설정을 합니다. 

--> set() 함수 사용 

jade 템플릿 파일들이 위치할 디렉토리를 지정합니다. 

jade 라는 'view engine' 을 사용할 것이라고 지정합니다. 

 

5. /topic/form 경로에서 form.jade 템플릿을 쓰도록 render() 함수를 써줍니다. 

 

6. form 이 있는 form.jade 템플릿 파일을 만듭니다. 

제목과 내용.

두 칸을 만들어서 입력을 받습니다. 

'제출' 버튼을 누르면, action= '/topic' 이므로, topic 경로로 전송합니다. 방식은 POST.

 

7.  템플릿 엔진의 html 태그를 예쁘게 정렬하기 위한 코드를 추가합니다.

 

8.  POST 방식으로 만든 form 의 body 객체 데이터에 접근해야 title과 description 값을 가져올 수 있습니다. 

 

파일을 생성하는 모듈이 필요합니다.

파일 시스템에 대한 Node.js Document를 읽어봅니다. 

 

파라미터를 봅니다. 

파일명, 데이터, 그리고 옵션, 콜백함수 순으로 받습니다. 

 

콜백함수의 파라미터가 err 라고 되어있는데, 혹시 에러가 발생하면 err 변수에 내용을 알려준다는 뜻입니다. 

 

app_file.js 에 app.post() 함수를 추가합니다. 라우팅은 /topic 으로 합니다. 

 

title이 파일명이 되고, description은 파일의 내용이 됩니다. 

 

파일은 data 라는 디렉토리 하위에 만들꺼니까, 경로를 'data/' + title 로 해줍니다. 

디렉토리명 뒤에 / 슬래시를 붙여줘야 합니다. 

 

 

 POST 방식으로 body 태그 내용을 처리하기 위해 bodyParser 사용 설정을 합니다.

글 제목과 내용을 입력받을 html의 body 태그내용에 접근해야 합니다. 

form 에 내용을 쓰고 제출 버튼을 눌러보면, data 디렉토리 하위에 title과 똑같은 파일명이 생긴것을 확인할 수 있습니다. 

 

 

 

9. 이제 data 하위의 파일명으로 글목록을 만듭니다. 

파일을 쓰는게 아니라 디렉토리 내의 파일을 읽을 것이기 때문에 fs.readdir() 함수를 이용합니다. 

 

app.get() 함수에 /topic으로 라우팅 하고, 콜백함수 내에 readdir() 함수를 작성합니다. 

readdir() 함수의 콜백함수는 err 가 있다면 err를 알려주고,  files 이름으로 파일명들을 리턴합니다. 

err가 있다면 console.log() 로 출력하고, 응답으로 500 코드와 메시지를 출력하도록 합니다. 

 

콜백함수가 끝나기 전 마지막에 render() 를 작성합니다. 

render() 함수의 첫째 파라미터는 jade 템플릿명을 쓰고, 둘째 부터는 돌려주고 싶은 객체값 files를 topics 라는 이름으로 씁니다.

 

10. 파일목록 jade 템플릿은 이런 모양입니다. 이것을 jade iterator로 수정합니다. 

 

 

jade에서 객체를 반복하는 것은 iterator 입니다. jade document를 참조합니다. 

 

 

 

 

11. 폼에 내용 입력해서 파일 생성하고, 파일목록 읽어서 글목록 조회해보기! 

 

http://127.0.0.1:3000/topic/form 에서 제목과 내용을 입력합니다. 

 

http://127.0.0.1:3000/topic 에서 보이는 글목록 화면 입니다. 

 

728x90

'프로그래밍 > Node.js' 카테고리의 다른 글

코드 개선  (0) 2020.03.16
본문 읽기 - 전체 코드 포함  (0) 2020.03.16
POST방식 GET방식  (0) 2020.03.15
쿼리스트링과 시맨틱URL  (0) 2020.03.15
query string  (0) 2020.03.15

오늘 한 일 

 

Node.js 템플릿 엔진, 쿼리스트링, Express 를 다뤄봤다. 그리고 MySQL 설치를 하고 마쳤다.  

 

생각거리

 

이대로 착착 잘 해보자.

내일은 용역 일이 시작된다. 

아자아자 화이팅! :)

 

 

728x90

'일상 > Today I Learn(TIL)' 카테고리의 다른 글

2020-03-18 TIL  (0) 2020.03.18
2020-03-16 TIL  (0) 2020.03.16
2020-03-13 TIL  (0) 2020.03.13
2020-03-12 TIL  (0) 2020.03.12
2020-03-11 TIL  (0) 2020.03.11

+ Recent posts