DFS (Depth First Search)

 

  • 깊이 우선 탐색, 트리에서 깊이를 기준으로 하나씩 정복해 나간다.

  • Stack 자료구조를 사용, 컴퓨터는 내부적으로 스택 구조를 활용하므로 재귀함수를 통해 구현 가능.

  • 순열, 조합의 개념과 연관되는 것이 많으므로 아래 링크를 통해서 개념을 이해하고 먼저 구현 해보기.

https://yabmoons.tistory.com/99

 

BFS (Breadth First Search)

 

  • 너비 우선 탐색, 트리에서 너비를 기준으로 하나씩 정복해 나간다.

  • Queue 자료구조를 사용

백트래킹이란?

 

  • 쉽게 DFS + 가지치기라 생각할 수 있다. 

  • 대표적 예제 (N-Queen). 모든 경우의 수를 생각하지 않고 미리 조건을 확인하며 제외 해 나간다.

https://www.youtube.com/watch?v=_hxFgg7TLZQ

 

위 영상 내용을 통해 깔끔하게 개념 정리 가능. 

[C++ 벡터 구조 이해해기]

https://jhnyang.tistory.com/230

 

벡터는 배열과 매우 유사하지만, 길이 변화가 비교적 자유로운 동적 배열 구조라고 생각하자.

[벡터의 단점]

중간값 삭제, 검색, 중간 추가가 어려움.

[벡터의 장점]

마지막값 추가, 삭제가 쉬움.

구현이 쉬움.

주소를 알 경우 접근이 쉬움.

사진 출처 : http://myblog.opendocs.co.kr/archives/1347 

 



[벡터는 언제 사용하나요?]

저장하려는 데이터의 갯수가 가변적이고, 마지막에서 추가 삭제가 이루어지며

데이터가 적거나, 많으면 검색이 잦지 않으며, 랜덤하게 데이터 접근을 허용할 때.

 

[벡터 사용법]

벡터는 C++의 표준(Standard) 템플릿(Template) 라이브러리(Library) 줄여서 STL!

STL에서 제공되는 컨테이너는 일반적으로 헤더 파일 이름이 컨테이너 이름과 같다.

즉, #include <vector>

 

[iterator(반복자)의 개념]

컨테이너에 저장된 원소를 순회, 접근하는 일반화된 방법을 제공하는 것은 Iterator!

Iterator가 컨테이너에 범용적으로 사용되기 때문에 종속적이지 않고, 독립적으로 운용 가능.

일반적으로 begin(), end()를 활용해 반복자를 사용한다. 해당 element를 반환하는 것이 아닌 반복자의 위치를 설정하는 개념



벡터 예제 연습 코드.

#include <iostream>

#include <vector>


using namespace std;


int main(void)

{

    

    //벡터 생성. vector<자료형> 이름.

    vector<int> v1;

    vector<char> v2;

    vector<string> v3;

    

    //개수를 넣어 생성자 활용해 디폴트(char 빈값, int 0)값 선언 초기화.

    vector<char>v4(3);

    vector<int>v5(5);


    //vector for loop

    for(char x: v4) cout<< x;

    cout<<endl;

    for(int x : v5) cout<< x;

    cout<<endl;

    

    //다른 값으로 초기화 하고 싶다면? vector<자료형>이름(갯수, 초기화값)으로.

    vector<int> v6(5, 10);

    for(int x : v6) cout<< x<<",";

    cout<<endl;

    

    //여러값을 다르게 초기화 하고 싶다면? 배열처럼{}를 이용. 단, c++ 11이상

    vector<char>v7 = {'a', 'b', 'c', 'd'};

    for(char x : v7) cout<< x << " ";

    cout<<endl;

    //벡터의 제일 뒤에 값을 추가하고 싶다면? push_back 사용.

    vector<string>v8;

    v8.push_back("a11");

    v8.push_back("b11");

    v8.push_back("c11");

    for (string x : v8 ) cout << x << " ";

    cout<<endl;

    

    //응용버전.

    int size = 10; vector<int> v9;

    for(int i=0; i<size; i++) v9.push_back(i+1);

    cout<<endl;

    for(int x: v9) cout<< x << "!";

    cout<<endl;

    

    //벡터 복사하기 v9을 v10으로 복사.

    vector<int> v10(v9);

    for(int x : v10) cout<< x << " ?";

    cout<<endl;

    

    

    //컨테이너에 저장된 원소를 순회, 접근하는 일반화된 방법을 제공하는 것은 Iterator!

    //Iterator가 컨테이너에 범용적으로 사용되기 때문에 종속적이지 않고, 독립적으로 운용 가능.

    //일반적으로 begin(), end()를 활용해 반복자를 사용한다. 해당 element를 반환하는 것이 아닌 반복자의 위치를 설정하는 개념

    vector<int> v11(v10.begin(), v10.end()); // 전체 복사.

    vector<int> v12(v10.begin(), v10.begin()+5); // 앞에서 다섯 개 복사.

    for(int x : v11) cout<< x << "@";

    cout<<endl;

    for(int x : v12) cout<< x << "#";

    cout<<endl;



}

 

 지난 알고리즘 공부하기 Step1(뭘 공부해야 하지?)와 Step2(입출력 30문제 풀며 이해하기)에 이어서 Dynamic Programming 공부를 하려 한다. 

[공부 방법]

- 지적 호기심이 충분한 사람이나, 도전정신이 강한 사람이라면 이론 공부 후에 바로 문제에 도전 할 수 있을 것이다. 하지만 개인적으로는 강의 영상을 통해 동적 프로그래밍이 무엇이고 어떤 예제들이 있는지, 예제들을 어떻게 접근해서 어떻게 구현(코딩)해 나가는지 몇가지 예시를 보고 깊게 학습하는 것이 흥미를 잃지 않고 '지속 가능한' 학습 방법으로는 좋은 것 같다. 나는 아래의 나동빈님의 영상을 통해 동적 프로그래밍이 무엇인지 감을 잡았다.

- 위의 영상 1번을 시청하고, 2번은 시청하면서 잠시 멈추고 스스로 문제를 풀어보고 풀이를 보고 이해하고 이런 방식으로 하면 좋은 것 같다.

1번 : https://www.youtube.com/watch?v=YHZiWaL49HY

DP 개념 설명 및 예제 풀이 - 1

2번 : https://www.youtube.com/watch?v=kYoKLm8BZtI

DP 예제풀이 - 2

[동적 계획법]

- 수학과 컴퓨터 공학, 그리고 경제학에서 동적 계획법이란 복잡한 문제를 간단한 여러 개의 문제로 나누어 푸는 방법을 말한다. 이것은 부분 문제 반복과 최적 부분 구조를 가지고 있는 알고리즘을 일반적인 방법에 비해 적은 시간 내에 풀 때 사용한다. (위키피디아)

 

[동적 계획법을 이해하기 위한 가장 좋은 예시 문제는?]

- 피보나치 수열 계산을 재귀적으로 했을 경우 VS 동적 계획법을 활용 할 경우를 비교하면 정확히 동적 계획법이 무엇인지 알 수 있다.
* 피보나치 수열 (An = An-1 + An-2)

1. 재귀함수로 문제를 해결할 경우.

1
2
3
4
5
6
7
8
9
10
11
function 피보나치(i){
    
    if(i == 1 || i == 2return 1;
    else return  피보나치(i-1+ 피보나치(i-2);
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

피보나치(5) 를 계산하기 위해서는?
-> 피보나치(4)와 피보나치(3)을 계산해야 함.

피보나치(3)을 계산하기 위해서는?
-> 피보나치(2)와 피보나치(1)을 계산해야 함.

피보나치(4)를 계산하기 위해서는?
-> 피보나치(3)과 피보나치(2)를 계산해야 함

....
이렇게 나아가다 보면, 피보나치 (4), 피보나치(3), 피보나치(2), 피보나치(1)을 반복 계산하게 되고 이는 계산에 심각한 낭비를 가져온다.
예를들면 피보나치(50)을 계산하기 위해서 49,48를 계산하고 49를 위해서 48,47을 계산하는데 이는 트리구조로 아래로 내려갈 수록 2배의 계산이 필요하다. 총 피보나치(N)을 계산하기 위해 2^50을 계산해야 한다.

"그럼 중복을 기억해서 줄이면 되지 않나?" 이 생각이 바로 동적 계획법이다.

2. 피보나치 수열을 동적 계획법으로 계산할 경우.

1
2
3
4
5
6
7
8
9
10
11
int 기억장소[N];
 
function 피보나치(i){
    
    if(i == 1 || i == 2return 1;
    else if (기억장소[i] != 0return 기억장소[i];
    else return 기억장소[i] = 피보나치(i-1+ 피보나치(i-2);
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 위와 같이 피보나치 수열 계산을 동적 계획법을 활용하면, '기억장소'를 활용하여 memorize 시키고 이미 푼 문제의 답을 다시 활용한다.
이런식으로 코드를 짜면 각각 한 번만 계산하면 되니 총 N번의 실질적 계산이 이뤄질 뿐이다.

알고리즘 공부하기 (Step 1, 뭐 부터 어떻게 공부해야 하지?)

백준 사이트에서 공부를 시작하려고 하는데 추천 받은 문제 카테고리에 따라

입출력 - DP - 기타 - bfs,dfs - binary search - 분할 정복 - 그리디 - 완전탐색 순서로 풀어 보려고 한다.

이번 주말에 풀었던 입출력 문제들은 다음과 같다. (boj.kr/2557 형태로 검색!) 약 30문제, 5시간 정도 걸린 것 같다.
BOJ - 2557, 1000, 2558, 10950, 10951, 10952, 10953, 11021, 11022, 11718, 11719, 11720, 11721, 2741, 2742, 2739, 1924, 8393, 10818, 2438, 2439, 2440, 2441, 2442, 2445, 2522, 2446, 10991, 10992

 시간 여유가 없으면 힘들겠지만, 주말에 몰아서 문제들을 풀다보니 확실히 하루에 긴 시간을 투자해서 공부하는 것이 짧게 여러번 공부하는 것 보다 학습 효율이 좋은 것 같다. 실수 했던 부분이나 배운 부분을 바로 다음 문제에 활용 가능해서 기억 -> 체화로 넘어갈 수 있는 것 같다.

 또 더블 모니터를 활용하면 한쪽에 문제 한쪽에 코딩을 할 수 있어서 확실히 더 효율이 높은 것 같다.

문제번호

배운 사항 & 실수 한 부분

10951

cin.eof()를 활용하는 방법 https://takeknowledge.tistory.com/20

[주어진 테스트 케이스의 갯수 없이 입력이 끝날 때 까지 받아야 하는경우, cin.eof()==true 일때 까지 받아준다.]

string 입,출력 https://giantpark197cm.tistory.com/142

[string s; getline(cin, s);]

11720

char를 받은 후, char값에서 -’0’을 처리해줘서 값을 만들기.

11721

strlen(char) 활용. 끝이 null 값이라 배열의 길이와 상관 없이 활용할 수 있다.

대신 #include <string.h>

2741

for문을 활용해서 입,출력을 사용할 때 주의 사항

단순히 cin과 cout을 활용하면 입,출력 시간이 오래 걸리기 때문에 scanf나 printf를 활용하자. 또한 endl; 대신 개행문자(\n)을 활용하자.

혹은 https://www.acmicpc.net/problem/15552 를 참조하면, cin과 cout은 서로 입,출력이 묶여있어서 cin.tie(NULL)을 활용하면 입, 출력을 따로 활용 가능하여 속도 향상이 가능하다고 한다.

1924


scanf 로 두개의 변수를 받으려 할 때, scanf(%d, %d) 이런식으로 중간에 ,를 넣으면 안된다.

scanf(“%d %d”, &a, &,b);

10818

int형의 범위는 -21억~21억 정도로 생각하자. 숫자를 쉽게 읽기 위해서는 (1억은 0이 8개)

100,000,000 = 1억

2445

for loop의 variable scope 는 for loop 내부만.

2522

별찍기, 중심을 기준으로 대칭 구조는 for문의 step 을 활용해서 step 이 N일때 -1로 바꾸어 주면 쉽다.

2446

scanf()에는 뒤에 꼭 주소값을 넣어줘야 한다! “&a”

 

그럼 다음은 DP 이론을 정리하고, DP 문제를 풀어 봐야겠다.

DP - 1463, 11726, 11727, 9095, 10844, 11057, 2193, 9465, 2156, 11053, 11055, 11722, 11054, 1912, 2579, 1699, 2133, 9461, 2225, 2011, 11052

 

 

Reference : https://plzrun.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4PS-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0

Data Structure

새로운 개념이나 분야를 공부해 나갈 처음으로 해야 일은무엇을 모르는지 아는 이다. 요즘같은 영상 시대에서 주로 사용하는 방법은 유튜브에 정리된 개요 강의가 있는지 확인하는 것이다. 10분이라는 짧은 시간동안 자료구조가 무엇인지 ~ 어떤 자료구조들이 있고 있는지를 알려주는 좋은 개요 강의를 찾아서 요약해 보려 한다.아래의 80%는 해당 유튜브 영상을 참조하여 정리한 것이다.

 

자료구조?

상황에 맞게 데이터를 읽고 불러오기 쉽게 정리할 있는 데이터의 구조

 

1. 배열 (Array)

다른 말로는 리스트나 벡터로 불린다. 메모리에 저장된 연속된 . 배열에 저장되어 있는 값을 찾기 위해 우리는 “INDEX” 알아야 한다.

배열이 메모리에 저장되는 방식

 

 

 

2. 문자열(STRING)

문자나 숫자, 특수문자 등으로 만들어진 배열.

문자열이 메모리에 저장되는 방식, 마지막 (zero)가 NULL을 뜻한다.

STRING 메모리 저장 방식은 기본적으로 배열과 동일하지만 마지막 문자 뒤에 Null Caracter Binary 0 넣는다. (-어디서 끝나는지 알기 위함.)

 

3. MATRIX

배열의 배열, 스프레드 시트의 표나 화면의 픽셀 처럼 2차원적으로 확장된 자료를 다루고 싶을 사용. 

 

[ARRAY, STRING, MATRIX] 각각 하나의 숫자나 문자를 저장하는 구조.

하지만 연관있는 데이터를 연결시켜 저장하고 싶을 때가 있다. 예를들면 아이디와 비밀번호처럼 이때 활용할 있는 자료구조들이 아래와 같은 것들이다.

 

4. 구조체 Struct

단일 숫자가 아닌 여러 데이터를 번에 저장 가능한 복합 데이터로 변수

구조체는 이처럼 서로 연관있는 변수를 함께 저장할 때 쓰인다.

STRUCT 배열을 만들 있다. 구조체 배열은 크기가 고정되고 확장이 불가능 하다. 특성 때문에 새로운 값을 중간에 넣기가 힘들다. ( 찼을 경우) 하지만 이런 STRUCT 구조를 활용해서 유연한 데이터 구조를 만들 있다. (=> NODE)

 

5. 노드(NODE)

NODE 변수임과 동시에 포인터(메모리 주소를 가리키는 변수)이다.

많은 자료 구조의 기본인 노드는 변수하나와 포인터(들)을 가지고 있다.

NODE 여러개 저장 있는 유연한 구조체가 바로 LINKED LIST이다.

 

6. LINKED LIST

여러개의 NODE 저장하는 데이터 구조. 크기를 미리 지정해야 하는 Array 달리 링크드리스트는 단순히 포인터를 이동시키는 만으로 상황에 따라서 크기를 늘리고 줄일 있다.

링크드리스트를 추상화해 보여주는 사진, 위는 연결되어 Circular한 Linked List이다.
위처럼 포인터의 재 연결만으로 링크드 리스트는 쉽게 데이터를 추가, 삭제 할 수 있다.

여러개의 NODE 저장하는 데이터 구조. 크기를 미리 지정해야 하는 Array 달리 링크드리스트는 단순히 포인터를 이동시키는 만으로 상황에 따라서 크기를 늘리고 줄일 있다.

LINKED LIST가 저장되는 방식

위의 메모리 구조는 LINKED LIST 유연함을 나타냈는데, 포인터의 이동을 보면 1008- 1002 - 1000으로 이동하며 “CIRCULAR” LINKED LIST임을 있다.

다음 포인터의 값을 0(NULL)으로 해주면 연결 리스트를 끝낼 있다.

 유연성(단순히 포인터의 이동으로 추가, 삭제가 쉬운) 가졌기 때문에 순서를 바꾸거나, Trim(쪼개기), Reverse(뒤집기) 쉽게 있고 이로인해 복잡한 자료 구조들이 LINKED LIST 활용해서 구축되었다. 가장 보편적인 것이 "Queue Stack"이다.

 

7. 큐(Queue) : First In First Out

 먼저 사람이 먼저 나간다. FIFO 구조를 가진 Queue 링크드리스트로 만들어진다. 나가는 것을 어떻게 구현할까? 단순히 앞의 제일 앞의 포인터를 한칸 뒤로 이동 시키면 된다.  

Queue를 추상화 해서 설명하는 그림

위의 그림에서 Dequeue(먼저 온 사람인 HANK를 뺀다면)하기 위해서는 아래처럼 포인터를 하나 옮겨주면 된다.

Dequeue 후의 모습

위의 그림에서 새로운 사람이 추가된다면? INQUEUE

Inqueue후의 모습

 

8. 스택 : Last In First Out (LIFO)

마지막으로 들어온 사람이 먼저 나가려면 어떤 구조를 만들어야 할까? 자료구조가 LIFO, STACK이다.

Stack에서 나가는 것은 POP , 들어오는 것은 PUSH 부른다.

 

9. 트리(Tree)

Node에서 포인터를 두개 갖기만 한다면 Tree 구조를 만들 있다.

트리 노드의 구조체
값이 저장되어 있는 트리를 추상화한 그림

 트 구조에서 제일 위의 Node 루트(Root)라고 하며, 제일 아래 Node 리프(Leaf) 하며, 노드(A) 밑의 노드들은(A) 자식 노드(Children Node)라고 한다. 반대의 경우는 부모 노드(Parent Node)라고 한다.

노드의 자식 노드의 개수는 제한이 없지만, 위의 그림 예시처럼 노드의 자식이 두개까지인 것을 Binary Tree라고 한다.

트리의 하나의 특징은 뿌리에서 잎까지 한가지 경로만 존재 한다는 것이다.

 

 그럼 (규칙적인 트리와 달리)서로 제멋대로 연결되어 있는 자료들은 어떻게 표현할까? 그걸 표현하기 위한 구조가 아래의그래프(Graph)”구조이다.

 

10. 그래프(Graph)

 

그래프를 추상화한 모습

 위에서 설명한 처럼 제멋대로 연결되어 있는(리프, 루트, 부모, 자식의 개념이 없는) 자료 구조를 그래프로 표현 있다.

  이는 단순히 하나의 노드에서 여러개의 포인터를 포함시킴으로써 만들 있다. 

 

 

위의 구조 아니라 다양한 변형된 구조(Heap, Red Black Tree) 있으나 영상에선 다루지 않았다. 하지만 Heap 기본 구조중 하나이니 같이 정리한다.

 

11. Heap () : 우선순위 Queue

Max Heap예시 위로갈수록 커진다.

 최댓값 최솟값을 찾아내는 연산을 빠르게 하기 위해 고안된 완전 이진 트리(Complete Binary Tree) 기본으로 자료 구조(Tree Based-Structure)

 Max Heap Min Heap 있는데, 그대로 Max Heap 부모 노드가 항상 자식 노드의 값보다 특징을 같고 있으며  Min Heap 반대의 특성을 갖는다.

 Insert Delete Upheap, Downheap으로 부르며 다음에 구체적으로 리하겠다.

 

 위의 내용으로 어느정도 전체적인 개요를 머릿속에 넣을 수 있었다. 자료 구조들을 직접 코딩해서 만들어야 할까? 아니다 이미 언어들에서 만들어놓은 구조들이 있는데 이게 유명한 C++ STL(Standard Template Library) / Java Java Class Library등이다. 앞으로는 각각의 자료구조에 대해 구체적으로 공부하면서 내용을 정리 해야겠다.

 

 

 

취린이인 나는 알고리즘을 공부하기 위해 무엇을 해야 하는지

무엇을 모르는지 모르는 상태이다. 전체적인 프레임을 잡기 위해, 다른 고수 분들의 참고 자료를 모아 본 것 중 도움이 된 글들을 정리해 보려 한다.

 

Search

[킹갓제네럴충무공 박트리의 티스토리]

알고리즘 공부 방법/순서 : https://baactree.tistory.com/14

알고리즘 공부, 어떻게 해야 하나요? : https://baactree.tistory.com/52

 

알고리즘 공부, 어떻게 해야하나요?

오랜만에 정상적인 포스팅을 쓴다. 메일로 가장 많이 물어 보는 질문들이 [알고리즘 공부 어떻게 해야하나요? 어떻게 하셨어요? 뭘 공부해야 할 지 모르겠어요.] 와 같은 질문들이다. 위 질문에 가장 심플한 답변..

baactree.tistory.com

 

Action 1

글에서 알려준대로 먼저 언어 설정을 C++로 잡고, C++에 대한 구현력을 올려야 겠다.

그 전에 개발 환경설정 부터 해야 하는데 나는 MAC을 사용해서 VS CODE로 C++ 컴파일을 하려고 한다.

[참조한 블로그]

Mac에서 C++ 개발환경 VSCODE 만들기  : https://younghk.github.io/VS-Code-C++-Configuration-For-Mac/

연습은 국룰인 Hello World를 찍어보자.

국룰인 Hello World 출력하기

Action 2

우선 글에 나와있는 순서대로, 알고리즘 초급인 나는

'알고리즘 기법, 자료구조'중 초급 단계에 해당하는 '완전 탐색 / 초급 DP / 큐, 스택 / DFS / BFS / 탐욕 법'등을 정리하며 공부해야겠다.

이를 공부하기 위해서 알고리즘, 자료구조의 기본서가 필요할 텐데 기본서를 구매해야 겠다.

 

 

 

Action 3 : 알고리즘 문제 풀이 시작하기

알고리즘을 공부하려는 사람들은 대부분 백준 온라인 저지 사이트에 대해서 들어봤을 것이다.
예전에 들어가서 2-3문제를 푼 경험이 있지만 이렇게 많은 양의 문제 중에 내가 어떤 문제부터 풀고, 틀린건 어떻게 해야 학습을 할 수 있는 것인지 궁금했었다. 이걸 잘 정리해준 사이트가 있으니 위의 링크를 참고하자.

+ Recent posts