├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md └── contents ├── algorithm ├── README.md ├── basic.md ├── code │ ├── BellmanFordTest.java │ ├── DijkstraTest.java │ ├── FloydWarshallTest.java │ ├── KMPTest.java │ ├── KruskalTest.java │ ├── LDS_bs.cpp │ ├── MergeSort.java │ ├── QuickSort.java │ ├── lis_brute.cpp │ ├── lis_bs.cpp │ ├── lis_dp.cpp │ └── two_pointer.cpp ├── graph.md ├── greedy.md ├── img │ └── chess_board_1.png ├── materials │ ├── 유니온파인드.pdf │ ├── 이세명_algorithm_sorting.pdf │ ├── 최단경로알고리즘.pdf │ ├── 최장감소수열_두포인터.pdf │ └── 최장증가수열.pdf ├── sort.md ├── string.md └── two-pointer.md ├── data-structure ├── README.md ├── advanced.md ├── basic.md ├── code │ ├── LinkedList │ │ ├── DoublyLinkedList.java │ │ ├── ILinkedList.java │ │ ├── LinkedListExample.java │ │ └── SinglyLinkedList.java │ ├── Queue │ │ ├── ArrayQueue.java │ │ ├── IQueue.java │ │ ├── LinkedQueue.java │ │ └── QueueExample.java │ ├── Stack │ │ ├── ArrayStack.java │ │ ├── IStack.java │ │ ├── LinkedStack.java │ │ └── StackExample.java │ ├── Tree │ │ └── Tree.ts │ └── Trie │ │ └── TrieExample.java ├── img │ ├── 그래프.001.jpeg │ ├── 그래프.002.jpeg │ └── 그래프.003.jpeg └── materials │ └── 세그먼트트리.pdf ├── database ├── README.md └── materials │ ├── 윤가영_database_&Index.pdf │ └── 이세명_database_NoSQL.pdf ├── design-pattern ├── README.md ├── adapter.md ├── anti-pattern.md ├── code │ ├── FactoryJava │ │ └── FactoryTest.java │ ├── MVCPython │ │ ├── mvc_sample.py │ │ └── mvc_uml.py │ ├── ObserverJava │ │ ├── Main.java │ │ ├── Netflix.java │ │ ├── NetflixUser.java │ │ ├── Observable.java │ │ └── Observer.java │ ├── SingletonJava │ │ └── SingletonTest.java │ └── SingletonPython │ │ ├── borg_singleton.py │ │ ├── classical_singleton.py │ │ ├── lazy_instantiation.py │ │ ├── metaclass_ex.py │ │ ├── metaclass_singleton.py │ │ ├── real_world_scenario_1.py │ │ └── real_world_scenario_2.py ├── factory.md ├── materials │ └── .gitkeep ├── mvc-python.md ├── observer.md ├── singleton-java.md ├── singleton.md └── template-method.md ├── language ├── README.md ├── c++ │ ├── STL.md │ ├── moderncpp.md │ └── multithread-programming.md ├── coroutine.md ├── java │ ├── 1.md │ ├── 2.md │ └── appendix.md └── materials │ └── .gitkeep ├── network ├── README.md ├── img │ ├── 3-way-handshake.png │ ├── 4-way-handshake.png │ ├── osi-7-layer.png │ └── osi-and-tcp-ip.png └── materials │ └── yoongoing_networkflow.pdf ├── operating-system ├── README.md └── materials │ ├── process-state-diagram.png │ ├── queueing-diagram.jpeg │ ├── 동시성.jpeg │ ├── 멀티스레드.png │ └── 이세명_operating-system_memory-management.pdf └── software-engineering ├── README.md ├── eXtremeProgramming.md └── materials └── CS_(Agile).pdf /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### 변경 사항 2 | 3 | > Issue 참고 : [Note Status Check](https://github.com/Seogeurim/CS-study/issues/2) 4 | 5 | ### Point of discussion 6 | 7 | ### Reference 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS 2 | .DS_Store 3 | 4 | # IntelliJ 5 | out/ 6 | .idea/ 7 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Seogeurim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic Knowledge of Computer Science 2 | 3 | > Since 2020.09.04 4 | 5 |

6 | coding 7 |

8 | 9 | ## Table of Contents 10 | 11 | - [About](#about) 12 | - [Repository Rule](#repository-rule) 13 | - [Collaborator](#collaborator) 14 | - [Reference](#reference) 15 | - [Data Structure (자료구조)](#data-structure-자료구조) 16 | - [Algorithm (알고리즘)](#algorithm-알고리즘) 17 | - [Operating System (운영체제)](#operating-system-운영체제) 18 | - [Database (데이터베이스)](#database-데이터베이스) 19 | - [Network (네트워크)](#network-네트워크) 20 | - [Design Pattern (디자인 패턴)](#design-pattern-디자인-패턴) 21 | - [Software Engineering (소프트웨어 공학)](#software-engineering-소프트웨어-공학) 22 | - [Language](#language) 23 | 24 | ## About 25 | 26 | 알고리즘과 CS 기초 지식의 이론부터 구현까지, 컴퓨터공학 전공자 및 예비 개발자로서 알아야 할 필수 전공 지식들을 공부하고 기록한 저장소입니다. 매주 스터디한 흔적인 **발표 자료**들이 업로드되어 있으며, 더 나아가 **글**로, **질의응답** 형태로 문서화하는 것을 목표로 합니다. 27 | 28 | ### Repository Rule 29 | 30 | > [CS-study Repo 가이드](https://www.notion.so/CS-study-Repo-3428a7e4213345ffa08362c7abea8528) 31 | 32 | - **주제별 정리** : 이론정리, 구현, 자료업로드, 질의응답 33 | - **Commit convention rule** : [대주제] 소주제 분류(이론정리/구현/...) _ex) [DataStructure] Stack 자료정리_ 34 | - **Branch naming convention** : 대주제/닉네임 _ex) DataStructure/Nickname_ 35 | 36 | ### Collaborator 37 | 38 |

39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |

61 | 62 | ### Reference 63 | 64 | - [JaeYeopHan/Interview_Question_for_Beginner](https://github.com/JaeYeopHan/Interview_Question_for_Beginner) 65 | - [gyoogle/tech-interview-for-developer](https://github.com/gyoogle/tech-interview-for-developer) 66 | - [WeareSoft/tech-interview](https://github.com/WeareSoft/tech-interview) 67 | - [jobhope/TechnicalNote](https://github.com/jobhope/TechnicalNote) 68 | 69 | ## Data Structure (자료구조) 70 | 71 | ### [📖 정리노트](./contents/data-structure) 72 | 73 | #### 기본 자료 구조 74 | 75 | - Array 76 | - Linked List 77 | - Stack 78 | - Queue 79 | - Tree 80 | - Binary Tree 81 | - Graph 82 | 83 | #### 응용 자료 구조 84 | 85 | - Deque 86 | - Heap & Priority Queue 87 | - Indexed Tree (Segment Tree) 88 | - Trie 89 | 90 | [🔝 목차로 돌아가기](#table-of-contents) 91 | 92 | ## Algorithm (알고리즘) 93 | 94 | ### [📖 정리노트](./contents/algorithm) 95 | 96 | #### 알고리즘 기본 97 | 98 | - 시간복잡도와 공간복잡도 99 | - 완전 탐색 알고리즘 (Brute Force) 100 | - DFS와 BFS 101 | - 순열, 조합, 부분집합 102 | - 백트래킹 (Backtracking) 103 | - 분할 정복법 (Divide and Conquer) 104 | - 탐욕 알고리즘 (Greedy) 105 | - 동적 계획법 (Dynamic Programming) 106 | 107 | #### 알고리즘 응용 108 | 109 | - 정렬 알고리즘 110 | - 그래프 111 | - 최단 경로 알고리즘 112 | - Union Find & Kruskal 113 | - 두 포인터 (two-pointer) 114 | - 문자열 처리 알고리즘 115 | - KMP 알고리즘 116 | 117 | [🔝 목차로 돌아가기](#table-of-contents) 118 | 119 | ## Operating System (운영체제) 120 | 121 | ### [📖 정리노트](./contents/operating-system) 122 | 123 | - 프로세스와 스레드 124 | - 멀티 프로세스와 멀티 스레드 125 | - 프로세스 스케줄링 126 | - CPU 스케줄링 127 | - 동기와 비동기의 차이 128 | - 프로세스 동기화 129 | - 메모리 관리 전략 130 | - 가상 메모리 131 | - 캐시 132 | 133 | [🔝 목차로 돌아가기](#table-of-contents) 134 | 135 | ## Database (데이터베이스) 136 | 137 | ### [📖 정리노트](./contents/database) 138 | 139 | - 데이터베이스 140 | - 정규화 141 | - Index 142 | - Transaction 143 | - NoSQL 144 | 145 | [🔝 목차로 돌아가기](#table-of-contents) 146 | 147 | ## Network (네트워크) 148 | 149 | ### [📖 정리노트](./contents/network) 150 | 151 | - OSI 7 계층 152 | - TCP 3-way-handshake & 4-way-handshake 153 | - TCP 와 UDP 154 | - HTTP 요청 방식 - GET, POST 155 | - HTTP 와 HTTPS 156 | - DNS round robin 방식 157 | - 웹 통신의 큰 흐름 158 | 159 | [🔝 목차로 돌아가기](#table-of-contents) 160 | 161 | ## Design Pattern (디자인 패턴) 162 | 163 | ### [📖 정리노트](./contents/design-pattern) 164 | 165 | - 디자인 패턴의 개념과 종류 166 | - Singleton 패턴 167 | - Factory 패턴 168 | - MVC 패턴 169 | 170 | [🔝 목차로 돌아가기](#table-of-contents) 171 | 172 | ## Software Engineering (소프트웨어 공학) 173 | 174 | ### [📖 정리노트](./contents/software-engineering) 175 | 176 | - 프로그래밍 패러다임 177 | - 명령형 프로그래밍 vs 선언형 프로그래밍 178 | - 함수형 프로그래밍 179 | - 객체지향 프로그래밍 180 | - 애자일 개발 프로세스 181 | 182 | [🔝 목차로 돌아가기](#table-of-contents) 183 | 184 | ## Language 185 | 186 | ### [📖 정리노트](./contents/language) 187 | 188 | - Java 189 | - C++ 190 | 191 | [🔝 목차로 돌아가기](#table-of-contents) 192 | -------------------------------------------------------------------------------- /contents/algorithm/README.md: -------------------------------------------------------------------------------- 1 | # Algorithm (알고리즘) 2 | 3 | ## 알고리즘 기본 [▶︎ 🗒](basic.md) 4 | 5 | - [시간복잡도와 공간복잡도](basic.md#시간복잡도와-공간복잡도) 6 | - 완전 탐색 알고리즘 (Brute Force) 7 | - [DFS와 BFS](basic.md#dfs와-bfs) 8 | - [순열, 조합, 부분집합](basic.md#순열-조합-부분집합) 9 | - [백트래킹 (Backtracking)](basic.md#백트래킹-backtracking) 10 | - [분할 정복법 (Divide and Conquer)](basic.md#분할-정복법-divide-and-conquer) 11 | - [탐욕 알고리즘 (Greedy)](basic.md#탐욕-알고리즘-greedy) 12 | - [동적 계획법 (Dynamic Programming)](basic.md#동적-계획법-dynamic-programming) 13 | 14 | ## 알고리즘 응용 15 | 16 | - [정렬 알고리즘](sort.md) (Insertion / Selection / Bubble / Merge / Quick / Heap / Radix / Counting Sort) 17 | - [그래프](graph.md) 18 | - [최단 경로 알고리즘](graph.md#최단-경로-알고리즘) (다익스트라, 벨만-포드, 플로이드-워셜) 19 | - [분리 집합(Union Find)과 크루스칼(Kruskal) 알고리즘](graph.md#분리-집합Union-Find과-크루스칼Kruskal-알고리즘) 20 | - [두 포인터 (two-pointer)](two-pointer.md) 21 | - [문자열 처리 알고리즘](string.md) 22 | - [KMP 알고리즘](string.md#문자열-패턴-매칭) 23 | 24 | --- 25 | 26 | ## 질의응답 27 | 28 | 29 | 30 |
31 | DFS와 BFS의 장단점에 대해 각각 설명해 주세요. 32 |

33 | 34 | 1. BFS 장점 35 | 1. 너비를 우선으로 탐색하기 때문에 답이 되는 경로가 여러개인 경우에도 최단경로임을 보장합니다. 36 | 2. 최단 경로가 존재한다면, 어느 한 경로가 무한히 깊어진다 해도 최단경로를 반드시 찾을 수 있습니다. 37 | 3. 노드의 수가 적고 깊이가 얕은 해가 존재할 때 유리합니다. 38 | 2. BFS 단점 39 | 1. 재귀 호출을 사용하는 DFS와는 달리 큐를 이용해 다음에 탐색할 노드들을 저장합니다. 이 때, 노드의 수가 많을 수록 필요없는 노드들까지 저장해야하기 때문에 더 큰 저장공간이 필요합니다. 40 | 2. 노드의 수가 늘어나면 탐색해야하는 노드 또한 많아지기 때문에 비현실적입니다. 41 | 3. DFS 장점 42 | 1. BFS에 비해 저장공간의 필요성이 적고 백트래킹을 해야하는 노드들만 저장해주면 됩니다. 43 | 2. 찾아야하는 노드가 깊은 단계에 있을 수록, 그 노드가 좌측에 있을 수록 BFS보다 유리합니다. 44 | 4. DFS 단점 45 | 1. 답이 아닌 경로가 매우 깊다면, 그 경로에 깊이 빠질 우려가 있습니다. 46 | 2. 내가 지금까지 찾은 최단경로가 끝까지 탐색 했을 때의 최단경로가 된다는 보장이 없습니다. 47 | 48 |

49 |
50 | 51 | --- 52 | 53 | 54 | 55 |
56 | 라이브러리 없이 정렬을 구현하려고 할 때 어떤 정렬 방식을 사용해 구현할 것이고 왜 그렇게 생각하는지 성능 측면에서 얘기를 해주세요. 57 |

58 | 59 | (예시 답안) 60 | 퀵소트로 구현할 것입니다. 퀵소트는 average case에서 nlgn의 시간복잡도를 가지며 공간복잡도 측면에서도 제자리 정렬이기 때문에 좋은 성능을 가집니다. worst case의 경우 n^2의 시간복잡도를 가지지만 worst case가 나타날 경우는 확률적으로 매우 낮습니다. (자료가 n개일 때 오름차순 또는 내림차순 -> 2/n!) 61 | 62 |

63 |
64 | 65 |
66 | 퀵소트에서 최악의 경우 시간복잡도를 개선시킬 수 있는 방법이 있을까요? 67 |

68 | 69 | 피벗의 위치를 다르게 설정함으로써 시간복잡도를 개선시킬 수 있습니다. 일정한 위치에 대해서만(ex. 첫번째 element) 피벗을 설정하는 것보다 첫번째, 마지막 element 중 무작위로 선택한다거나 첫번째, 가운데, 마지막 element 중 중간값을 계산하여 피벗을 설정했을 때 시간복잡도를 더 개선시킬 수 있습니다. 70 | 71 |

72 |
73 | 74 |
75 | 머지소트의 분할 정복 과정에 대해 단계별로 설명해주세요. 76 |

77 | 78 | - Divide : 초기 배열을 2개의 배열로 분할 79 | - Conquer : 각 부분 배열을 정렬 80 | - Combine : 부분 배열을 하나의 배열로 결합 81 | 82 |

83 |
84 | 85 | --- 86 | 87 | 88 | 89 |
90 | MST(Minimum Spanning Tree)를 구하는 알고리즘에 대해 설명하고, 각각 어떤 상황에서 사용하는 것이 적절한지 설명해 주세요. 91 |

92 | 93 | > 정점의 개수 : V, 간선의 개수 : E 94 | 95 | 대표적으로 프림 알고리즘과 크루스칼 알고리즘이 있습니다. 프림 알고리즘은 정점을 선택하고 그것과 연결된 가장 적은 비용의 정점을 선택하는 방식이며, O(ElogV)의 시간복잡도를 가집니다. 크루스칼 알고리즘은 모든 간선에 대하여 가장 비용이 적은 간선을 선택하는 방식이며 O(ElogE)의 시간복잡도를 가집니다. 프림 알고리즘은 정점의 개수에 비해 간선이 많이 주어진 경우 사용하는 것이 좋고, 크루스칼 알고리즘은 간선의 개수에 비해 정점이 많이 주어진 경우 사용하는 것이 좋습니다. 96 | 97 |

98 |
99 | 100 |
101 | Minmum Spanning Tree를 찾기 위한 프림 알고리즘의 동작원리 또는 특징에 대해 설명해주세요 102 |

103 | 104 | 1. 시작 단계에서는 시작 정점만이 MST(최소 비용 신장 트리) 집합에 포함된다. 105 | 2. 앞 단계에서 만들어진 MST 집합에 인접한 정점들 중에서 최소 간선으로 연결된 정점을 선택하여 트리를 확장한다.즉, 가장 낮은 가중치를 먼저 선택한다. 106 | 3. 위의 과정을 트리가 (N-1)개의 간선을 가질 때까지 반복한다. 107 | 108 |

109 |
110 | 111 |
112 | Minmum Spanning Tree를 찾기 위한 크루스칼 알고리즘의 동작원리 또는 특징에 대해 설명해주세요 113 |

114 | 115 | 1. 그래프의 간선들을 가중치의 오름차순으로 정렬한다. 116 | 2. 정렬된 간선 리스트에서 순서대로 사이클을 형성하지 않는 간선을 선택한다. 117 | 1. 즉, 가장 낮은 가중치를 먼저 선택한다. 118 | 2. 사이클을 형성하는 간선을 제외한다.(Union-Find 사용) 119 | 3. 해당 간선을 현재의 MST(최소 비용 신장 트리)의 집합에 추가한다. 120 | 121 |

122 |
123 | 124 | --- 125 | 126 | 127 | 128 |
129 | 최단경로를 구하기 위한 알고리즘을 두 가지 이상 말하고 어떤 차이점이 있는지 설명해주세요. 130 |

131 | 132 | - 다익스트라 : 하나의 시작 정점 ~ 모든 다른 정점까지의 최단 경로를 구한다. 133 | - 벨만포드 : 하나의 시작 정점 ~ 모든 다른 정점까지의 최단 경로를 구한다. + 가중치가 음수일 때도 사용이 가능하다. (음의 사이클 검사 가능) 134 | - 플로이드 와샬 : 모든 정점 ~ 모든 정점까지의 최단 경로를 구한다. 135 | 136 |

137 |
138 | 139 |
140 | 다익스트라 알고리즘 동작원리 또는 특징을 시간복잡도와 연관지어 설명해주세요. 141 |

142 | 143 | (방법 1) 144 | 145 | 1. 출발 노드 S에서 모든 노드들까지의 최단 거리를 저장하는 배열 D를 초기화한다. 146 | 2. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다. (D 배열 검사) 147 | 3. 선택한 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 배열 D를 갱신한다. 148 | 4. 모든 노드를 방문할 때까지 3, 4 과정을 반복한다. 149 | 5. 노드의 개수를 V라고 할 때, 총 V\*V번 연산이 필요하므로 `O(V^2)`의 시간복잡도를 가진다. 150 | 151 | (방법 2 - 힙/우선순위큐 사용) 152 | 153 | 1. 출발 노드 S에 대하여 D 배열을 초기화할 때 D[S] = 0을 해준다. 이와 동시에 힙에 노드 정보(번호, 거리 : [S, 0])를 넣어준다. 154 | 2. 힙에서 맨 위에 있는 노드 I를 꺼낸다. 155 | 3. 만일 꺼낸 노드 I의 거리 정보가 현재 D[I]보다 크다면 이미 방문한 노드일 것이므로 무시한다. 156 | 4. I를 대상으로 다익스트라 알고리즘을 수행하는데, D 배열이 갱신될 경우 그 노드 정보를 힙에 넣는다. 157 | 5. 힙에 노드가 없을 때까지 2-4 과정을 반복한다. 158 | 6. 노드의 개수를 V, 간선의 개수를 E라고 할 때 시간 복잡도는 `O(ElogV)` 이다. 159 | 160 |

161 |
162 | 163 |
164 | 다익스트라 알고리즘에서 힙(우선순위큐)을 사용할 경우 어떤 점에서 시간복잡도가 개선이 되나요? 165 |

166 | 167 | 다익스트라 알고리즘에서 방문하지 않은 노드 중 최단 거리가 가장 짧은 노드를 선택하는 과정이 있는데, 이 과정에서 O(`노드의 개수`)만큼의 비용이 발생하게 됩니다. 힙(우선순위큐)을 사용할 경우 그 비용을 O(`log{힙에 저장한 노드의 개수}`)로 줄일 수 있습니다. 168 | 169 |

170 |
171 | 172 |
173 | 벨만포드 알고리즘 동작원리 또는 특징을 시간복잡도와 연관지어 설명해주세요. 174 |

175 | 176 | 1. 음의 가중치를 가지는 간선도 가능하므로, 음의 사이클의 존재 여부를 따져야 한다. 177 | 2. 최단 거리를 구하기 위해서 V - 1번 E개의 모든 간선을 확인한다. 178 | 3. 음의 사이클 존재 여부를 확인하기 위해서 한 번 더 (V번째) E개의 간선을 확인한다. 179 | 4. 이 때 거리 배열이 갱신되었다면, 그래프 G는 음의 사이클을 가진다. 180 | 5. 따라서 총 V x E 번 연산하므로 O(VE)의 시간복잡도를 가진다. 181 | 182 |

183 |
184 | 185 |
186 | 플로이드 와샬 알고리즘 동작원리 또는 특징을 시간복잡도와 연관지어 설명해주세요. 187 |

188 | 189 | 1. 사이클이 없다면 음수 가중치를 가져도 적용 가능하다. 190 | 2. 동적 계획법(Dynamic Programming)으로 접근한다. 191 | 3. 모든 가능한 경유지에 대해서 모든 정점 -> 모든 정점으로 가는 최단 거리를 확인하므로 연산 횟수는 V^3이고, 따라서 시간복잡도는 O(V^3) 192 | 193 |

194 |
195 | 196 | --- 197 | 198 | 199 | 200 |
201 | KMP 알고리즘에 대해 설명해주세요. 202 |

203 | 204 | Kunth, Morris, Prett이 만든 알고리즘이라서 각 이름의 앞자리를 따서 KMP라고 지어졌습니다. 문자열이 불일치할 때 그 다음 문자부터 다시 탐색을 시작하는 것이 아니라 지금까지 일치했던 정보들을 버리지 말고 재사용 함으로써 몇칸 정도는 건너 뛰어서 탐색하자는 아이디어에서 알고리즘이 탄생했습니다. 접두사와 접미사 정보를 가지고 문자열을 점프해가며 탐색하는데, Naive한 문자열 탐색 알고리즘이 O(NM)의 시간복잡도를 갖는 반면에 KMP알고리즘은 O(N+M)의 시간복잡도를 갖습니다. 205 | 206 |

207 |
208 | 209 |
210 | 라빈 카프 알고리즘에 대해 설명해주세요. 211 |

212 | 213 | 문자열의 해시함수값을 이용합니다. 탐색 대상 문자열의 길이를 M이라고 했을 때 글을 M칸씩, 한칸 한칸 옮겨가며 부분 문자열을 떼어내고 해시함수값을 구하여 탐색 문자열의 해시함수값과 비교합니다. 해시함수값 충돌이 없다는 가정하에 글의 길이를 N이라고 하면 O(N-M)의 시간복잡도를 갖습니다. 214 | 215 |

216 |
217 | 218 |
219 | 라빈 카프 알고리즘이 KMP보다 빠른가? 220 |

221 | 222 | 사실상 그렇지 않습니다. 탐색 문자열의 길이가 길어질 수록 해시함수값에 충돌이 생길 확률이 높습니다. 따라서 해시함수값이 일치한다고 무조건 문자가 일치한다고 보장할 수 없기 때문에 해시함수값이 일치했을 때 문자열을 직접 비교하는 2차적인 검증이 필요합니다. 따라서 평균적으로 라빈 카프 또한 O(N+M)의 시간복잡도가 요구됩니다. 223 | 224 |

225 |
226 | 227 |
228 | 자료구조의 한 종류인 트라이를 설명해주세요. 229 |

230 | 231 | 트라이는 문자열을 저장하고 효율적으로 탐색하기 위한 트리 형태의 자료구조이다. 기본적으로 k진트리 구조를 띠고 어떤 문자열 집합 S와 문자열 A가 있다고 할 때 A가 S안에 존재하는지 찾는데에 사용되는 자료구조이다. 232 | 233 |

234 |
235 | 236 |
237 | 트라이의 장점과 단점을 설명해주세요. 238 |

239 | 240 | 이분탐색은 탐색하는데에 있어 검색어의 최대 길이 M * 전체 데이터 N 중 O(M log N)을 사용하게 되는데 이에 반해 트라이는 문자열 탐색에서의 전체 데이터의 길이인 시간복잡도 O(N)을 가지게 되어 매우 효율적이다. 하지만 트라이의 단점은 공간 복잡도가 높다. 알파벳을 저장하는 형태라면 1 depth당 26개의 공간이 사용될 수 있다. 241 | 242 |

243 |
244 | 245 |
246 | 트라이 자료구조를 사용한 적이 있나요? 경험을 이야기해주세요. 247 |

248 | 249 | 모범 답안) 알고리즘을 공부하는 과정에서 트라이 자료구조를 이용하여 아호코라식 알고리즘을 작성하여 문제를 해결했던 경험이 있습니다. 아호코라식 알고리즘은 KMP에서 사용하는 Failure Function을 트라이로 확장시킨 알고리즘으로 문자열 탐색에 사용하였습니다. 250 | 251 |

252 |
253 | 254 | --- 255 | 256 | 257 |
258 | 투포인터라는 알고리즘 문제 해결 전략이 있습니다. 어떤 알고리즘이고 주로 어떤 문제를 해결하는데 사용되나요? 259 |

260 | 261 | 배열을 가리키는 포인터 2개를 이용해서 포인터를 한칸 씩 움직이며 특정 구간 내에서 원하는 값을 얻을 때 사용합니다. 예를 들면 연속되는 배열의 구간 중 특정 조건을 만족하는 가장 짧은 구간을 구하는 문제에서 사용될 수 있습니다. 262 | 263 |

264 |
265 | -------------------------------------------------------------------------------- /contents/algorithm/code/BellmanFordTest.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | public class BellmanFordTest { 4 | 5 | public static void main(String[] args) { 6 | BellmanFord bf = new BellmanFord(5); 7 | bf.addEdge(3, 5, 3); 8 | bf.addEdge(3, 4, 3); 9 | bf.addEdge(4, 2, 3); 10 | bf.addEdge(5, 3, 2); 11 | bf.addEdge(4, 5, -6); 12 | bf.addEdge(1, 4, 3); 13 | bf.addEdge(1, 3, 5); 14 | bf.addEdge(1, 2, 7); 15 | 16 | // bf.printEdges(); 17 | bf.getShortestDistance(1); 18 | } 19 | } 20 | 21 | class BellmanFord { 22 | private int N; 23 | private ArrayList edges; 24 | 25 | private final static int INF = Integer.MAX_VALUE; 26 | private int[] D; 27 | 28 | public BellmanFord(int N) { 29 | this.N = N; 30 | edges = new ArrayList<>(); 31 | D = new int[N+1]; 32 | } 33 | 34 | public void addEdge(int from, int to, int weight) { 35 | edges.add(new Edge(from, to, weight)); 36 | } 37 | 38 | public void printEdges() { 39 | for (Edge edge : edges) System.out.println(edge); 40 | } 41 | 42 | public void getShortestDistance(int S) { 43 | /* init */ 44 | for (int i = 1; i <= N; i++) { 45 | if (i != S) D[i] = INF; 46 | } 47 | 48 | /* Bellman-Ford */ 49 | boolean negCycle = false; 50 | for (int i = 1; i <= N; i++) { 51 | for (Edge edge : edges) { 52 | int from = edge.from; 53 | int to = edge.to; 54 | int weight = edge.weight; 55 | if (D[from] != INF && D[to] > D[from] + weight) { 56 | if (i == N) { 57 | negCycle = true; 58 | break; 59 | } 60 | D[to] = D[from] + weight; 61 | } 62 | } 63 | } 64 | 65 | if (negCycle) System.out.println("음의 사이클이 존재합니다."); 66 | else printDistance(); 67 | } 68 | 69 | private void printDistance() { 70 | for (int i = 1; i <= N; i++) { 71 | if (D[i] == INF) System.out.print("∞ "); 72 | else System.out.print(D[i] + " "); 73 | } 74 | System.out.println(); 75 | } 76 | 77 | static class Edge { 78 | private int from; 79 | private int to; 80 | private int weight; 81 | 82 | private Edge(int from, int to, int weight) { 83 | this.from = from; 84 | this.to = to; 85 | this.weight = weight; 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return from + " --(" + weight + ")--> " + to; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /contents/algorithm/code/DijkstraTest.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.PriorityQueue; 3 | 4 | public class DijkstraTest { 5 | 6 | static int[][] weights = { 7 | {1, 2, 7}, 8 | {1, 3, 5}, 9 | {1, 4, 3}, 10 | {3, 4, 3}, 11 | {3, 5, 3}, 12 | {4, 5, 9}, 13 | {4, 7, 7}, 14 | {5, 3, 2}, 15 | {5, 7, 1}, 16 | {6, 2, 3}, 17 | {6, 4, 1}, 18 | {6, 7, 7} 19 | }; 20 | 21 | public static void main(String[] args) { 22 | /* Simple Dijkstra */ 23 | Dijkstra d = new Dijkstra(7); 24 | for (int[] w : weights) d.setGraph(w[0], w[1], w[2]); 25 | // d.printGraph(); 26 | d.getShortestDistance(1); 27 | 28 | /* Improved Dijkstra */ 29 | ImprovedDijkstra d2 = new ImprovedDijkstra(7); 30 | for (int[] w : weights) d2.setGraph(w[0], w[1], w[2]); 31 | // d2.printGraph(); 32 | d2.getShortestDistance(1); 33 | } 34 | } 35 | 36 | class Dijkstra { 37 | private int N; 38 | private int[][] graph; 39 | 40 | private final static int INF = Integer.MAX_VALUE; 41 | private int[] D; 42 | private boolean[] visited; 43 | 44 | public Dijkstra(int N) { 45 | this.N = N; 46 | graph = new int[N+1][N+1]; 47 | D = new int[N+1]; 48 | visited = new boolean[N+1]; 49 | } 50 | 51 | public void setGraph(int i, int j, int w) { 52 | graph[i][j] = w; 53 | } 54 | 55 | public void printGraph() { 56 | for (int i = 1; i <= N; i++) { 57 | for (int j = 1; j <= N; j++) { 58 | System.out.print(graph[i][j] + " "); 59 | } 60 | System.out.println(); 61 | } 62 | } 63 | 64 | public void getShortestDistance(int S) { 65 | /* init */ 66 | for (int i = 1; i <= N; i++) { 67 | if (i != S) D[i] = INF; 68 | } 69 | 70 | /* 출발 정점 초기화 */ 71 | visited[S] = true; 72 | for (int i = 1; i <= N; i++) { 73 | if (i != S && graph[S][i] > 0) D[i] = graph[S][i]; 74 | } 75 | 76 | /* dijkstra */ 77 | for (int i = 0; i < N-1; i++) { 78 | int current = getNextNode(); 79 | visited[current] = true; 80 | for (int j = 1; j <= N; j++) { 81 | if (graph[current][j] > 0) { 82 | D[j] = Math.min(D[j], D[current] + graph[current][j]); 83 | } 84 | } 85 | } 86 | 87 | printDistance(); 88 | } 89 | 90 | private int getNextNode() { 91 | int min_value = INF; 92 | int node_idx = 0; 93 | for (int i = 1; i <= N; i++) { 94 | if (!visited[i] && D[i] < min_value) { 95 | min_value = D[i]; 96 | node_idx = i; 97 | } 98 | } 99 | return node_idx; 100 | } 101 | 102 | private void printDistance() { 103 | for (int i = 1; i <= N; i++) { 104 | if (D[i] == INF) System.out.print("∞ "); 105 | else System.out.print(D[i] + " "); 106 | } 107 | System.out.println(); 108 | } 109 | } 110 | 111 | class ImprovedDijkstra { 112 | private int N; 113 | private ArrayList[] graph; 114 | 115 | private final static int INF = Integer.MAX_VALUE; 116 | private int[] D; 117 | 118 | public ImprovedDijkstra(int N) { 119 | this.N = N; 120 | graph = new ArrayList[N+1]; 121 | for (int i = 1; i <= N; i++) graph[i] = new ArrayList<>(); 122 | D = new int[N+1]; 123 | for (int i = 1; i <= N; i++) D[i] = INF; 124 | } 125 | 126 | public void setGraph(int i, int j, int w) { 127 | graph[i].add(new Node(j, w)); 128 | } 129 | 130 | public void printGraph() { 131 | for (int i = 1; i <= N; i++) { 132 | for (Node n : graph[i]) { 133 | System.out.println(i + " --" + n.distance + "--> " + n.index); 134 | } 135 | } 136 | } 137 | 138 | public void getShortestDistance(int S) { 139 | PriorityQueue pq = new PriorityQueue<>(((o1, o2) -> o1.distance - o2.distance)); 140 | 141 | D[S] = 0; 142 | pq.offer(new Node(S, 0)); 143 | 144 | while (!pq.isEmpty()) { 145 | Node current = pq.poll(); 146 | if (current.distance > D[current.index]) continue; 147 | 148 | for (Node next : graph[current.index]) { 149 | if (D[next.index] > D[current.index] + next.distance) { 150 | D[next.index] = D[current.index] + next.distance; 151 | pq.offer(new Node(next.index, D[next.index])); 152 | } 153 | } 154 | } 155 | 156 | printDistance(); 157 | } 158 | 159 | public void getShortestDistance2(int S) { // INF = -1 로 초기화했을 때 160 | /* @Hee-Jae 161 | - D 배열을 INF 대신 -1 로 초기화시킨다. 162 | - 한 번도 방문하지 않은 노드에 대해서만 D 배열을 갱신하고 우선순위 큐에 넣어가며 탐색한다. 163 | (우선순위 큐의 특성상 최저 비용으로 갈 수 있는 노드가 계속 큐의 제일 앞에 있기 때문에 '이 노드를 방문했는가?' 라는 조건만 판단해 준다면 Distance를 비교하지 않고도 최단경로를 구할 수 있다.) 164 | - INF 값을 설정해주지 않아도 되기 때문에 Distance가 INF를 넘어가게 되는 경우를 생각하지 않아도 된다는 장점이 있다. 165 | */ 166 | PriorityQueue pq = new PriorityQueue<>(((o1, o2) -> o1.distance - o2.distance)); 167 | 168 | pq.offer(new Node(S, 0)); 169 | while (!pq.isEmpty()) { 170 | Node current = pq.poll(); 171 | if (D[current.index] != -1) continue; 172 | 173 | D[current.index] = current.distance; 174 | for (Node next : graph[current.index]) { 175 | if (D[next.index] == -1) { 176 | pq.offer(new Node(next.index, D[current.index] + next.distance)); 177 | } 178 | } 179 | } 180 | 181 | printDistance(); 182 | } 183 | 184 | private void printDistance() { 185 | for (int i = 1; i <= N; i++) { 186 | if (D[i] == INF) System.out.print("∞ "); 187 | else System.out.print(D[i] + " "); 188 | } 189 | System.out.println(); 190 | } 191 | 192 | static class Node { 193 | int index; 194 | int distance; 195 | 196 | public Node(int index, int distance) { 197 | this.index = index; 198 | this.distance = distance; 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /contents/algorithm/code/FloydWarshallTest.java: -------------------------------------------------------------------------------- 1 | public class FloydWarshallTest { 2 | 3 | public static void main(String[] args) { 4 | FloydWarshall fw = new FloydWarshall(5); 5 | fw.setWeight(1, 3, 5); 6 | fw.setWeight(1, 2, 7); 7 | fw.setWeight(1, 4, -3); 8 | fw.setWeight(3, 4, 3); 9 | fw.setWeight(3, 5, 3); 10 | fw.setWeight(4, 2, 3); 11 | fw.setWeight(4, 5, 6); 12 | fw.setWeight(5, 3, 2); 13 | 14 | fw.getShortestDistance(); 15 | } 16 | } 17 | 18 | class FloydWarshall { 19 | private int N; 20 | private int[][] D; 21 | private final static int INF = Integer.MAX_VALUE; 22 | 23 | public FloydWarshall(int N) { 24 | this.N = N; 25 | D = new int[N+1][N+1]; 26 | initDistance(); 27 | } 28 | 29 | private void initDistance() { 30 | for (int i = 1; i <= N; i++) { 31 | for (int j = 1; j <= N; j++) { 32 | if (i != j) D[i][j] = INF; 33 | } 34 | } 35 | } 36 | 37 | public void setWeight(int i, int j, int w) { 38 | D[i][j] = w; 39 | } 40 | 41 | public void getShortestDistance() { 42 | /* Floyd-Warshall */ 43 | for (int k = 1; k <= N; k++){ 44 | for (int i = 1; i <= N; i++) { 45 | for (int j = 1; j <= N; j++) { 46 | if (D[i][k] == INF || D[k][j] == INF) continue; 47 | D[i][j] = Math.min(D[i][j], D[i][k] + D[k][j]); 48 | } 49 | } 50 | } 51 | 52 | printDistance(); 53 | } 54 | 55 | private void printDistance() { 56 | for (int i = 1; i <= N; i++) { 57 | for (int j = 1; j <= N; j++) { 58 | if (D[i][j] == INF) System.out.print("∞ "); 59 | else System.out.print(D[i][j] + " "); 60 | } 61 | System.out.println(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contents/algorithm/code/KMPTest.java: -------------------------------------------------------------------------------- 1 | public class KMPTest { 2 | 3 | static String T, P; 4 | static int N; 5 | static int[] fail; 6 | static StringBuilder match_index; 7 | 8 | public static void main(String[] args) { 9 | 10 | T = "ABABABABBABABABABC"; 11 | P = "ABABABC"; 12 | 13 | N = P.length(); 14 | fail = new int[N]; 15 | makeFail(); // fail 함수 만들기 16 | 17 | match_index = new StringBuilder(); 18 | System.out.println(kmp()); // kmp 함수 : 문자열 T 안에 패턴 P가 몇 번 나타나는지 찾기 19 | System.out.print(match_index.toString()); // 패턴이 일치한 시작 인덱스 찾기 20 | } 21 | 22 | static void makeFail() { 23 | for (int i = 1, j = 0; i < N; i++) { 24 | while (j > 0 && P.charAt(i) != P.charAt(j)) { 25 | j = fail[j-1]; 26 | } 27 | if (P.charAt(i) == P.charAt(j)) { 28 | fail[i] = ++j; 29 | } 30 | } 31 | } 32 | 33 | static int kmp() { 34 | int count = 0; 35 | for (int i = 0, j = 0; i < T.length(); i++) { 36 | while (j > 0 && T.charAt(i) != P.charAt(j)) 37 | j = fail[j-1]; 38 | if (T.charAt(i) == P.charAt(j)) { 39 | if (j == N-1) { // 끝까지 왔음 40 | j = fail[j]; 41 | count ++; 42 | match_index.append(i-N+2).append("\n"); 43 | } else { 44 | j ++; 45 | } 46 | } 47 | } 48 | return count; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contents/algorithm/code/KruskalTest.java: -------------------------------------------------------------------------------- 1 | import java.util.PriorityQueue; 2 | 3 | public class KruskalTest { 4 | 5 | public static void main(String[] args) { 6 | Kruskal kruskal = new Kruskal(); 7 | 8 | kruskal.setV(3); 9 | kruskal.setEdges(1, 2, 1); 10 | kruskal.setEdges(2, 3, 2); 11 | kruskal.setEdges(1, 3, 3); 12 | 13 | System.out.println(kruskal.getMSTCost()); 14 | } 15 | } 16 | 17 | class Kruskal { 18 | 19 | private int V; 20 | private PriorityQueue pq; 21 | private int[] root; 22 | 23 | public Kruskal() { 24 | pq = new PriorityQueue<>(((o1, o2) -> o1.c - o2.c)); 25 | } 26 | 27 | public void setV(int v) { 28 | V = v; 29 | setRoot(); 30 | } 31 | 32 | public void setEdges(int a, int b, int c) { 33 | pq.offer(new Edge(a, b, c)); 34 | } 35 | 36 | private void setRoot() { 37 | root = new int[V+1]; 38 | for (int i = 1; i <= V; i++) root[i] = i; 39 | } 40 | 41 | public int getMSTCost() { 42 | int cost = 0; 43 | while (!pq.isEmpty()) { 44 | Edge e = pq.poll(); 45 | if (find(e.a) == find(e.b)) continue; 46 | merge(e.a, e.b); 47 | cost += e.c; 48 | } 49 | return cost; 50 | } 51 | 52 | private int find(int n) { 53 | if (root[n] == n) return n; 54 | return root[n] = find(root[n]); 55 | } 56 | 57 | private void merge(int a, int b) { 58 | root[find(b)] = find(a); 59 | } 60 | 61 | static class Edge { 62 | int a, b, c; 63 | 64 | public Edge(int a, int b, int c) { 65 | this.a = a; 66 | this.b = b; 67 | this.c = c; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /contents/algorithm/code/LDS_bs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int N; 6 | int arr[1001]; 7 | int LDS[1001]; 8 | 9 | int search(int start, int end, int target){ 10 | int ans; 11 | 12 | while(start <= end){ 13 | int mid = (start + end) / 2; 14 | 15 | if(LDS[mid] <= target){ 16 | ans = mid; 17 | end = mid - 1; 18 | } 19 | else 20 | start = mid + 1; 21 | } 22 | 23 | return ans; 24 | } 25 | 26 | int main(void){ 27 | ios::sync_with_stdio(false); 28 | cin.tie(0); 29 | 30 | cin>>N; 31 | for(int i=0; i>arr[i]; 33 | 34 | int end = 0; 35 | LDS[end] = arr[0]; 36 | for(int i=1; i mid) { // 왼쪽 배열 값 다 사용함 , 오른쪽 다 복사 21 | for (int idx = j; idx <= end; idx++, k++) { 22 | temp[k] = arr[idx]; 23 | } 24 | } else { // 오른쪽 배열 값 다 사용함, 왼쪽 다 복사 25 | for (int idx = i; idx <= mid; idx++, k++) { 26 | temp[k] = arr[idx]; 27 | } 28 | } 29 | System.out.println(Arrays.toString(temp)); 30 | 31 | // 임시 배열 -> 원래 배열 32 | for (int num : temp) { 33 | arr[start++] = num; 34 | } 35 | 36 | } 37 | 38 | static void mergeSort(int[] arr, int start, int end) { 39 | if (start < end) { 40 | int mid = (start + end) / 2; 41 | mergeSort(arr, start, mid); 42 | mergeSort(arr, mid + 1, end); 43 | merge(arr, start, mid, end); 44 | } 45 | 46 | } 47 | 48 | public static void main(String[] args) { 49 | int[] arr = { 8, 7, 6, 5, 4, 3, 2, 1 }; 50 | mergeSort(arr, 0, 7); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contents/algorithm/code/QuickSort.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | public class QuickSort { 4 | 5 | static void quickSort(int[] arr, int start, int end) { 6 | if (start < end) { // 배열의 크기가 충분히 작아 질 때 까지 나눔 7 | int p = partition(arr, start, end); // 파티션을 적용 했을 때 피봇의 인덱스를 구함 8 | 9 | quickSort(arr, start, p - 1); // 처음 부터 피봇 전, 10 | quickSort(arr, p + 1, end); // 피봇 후 부터 마지막 까지 다시 퀵소트를 함 11 | } 12 | } 13 | 14 | static int partition(int[] arr, int start, int end) { 15 | int low = start + 1; // pivot을 맨 왼쪽 값으로 할것이기 때문에 그 다음 값부터 확인 16 | int high = end; 17 | int pivot = arr[start]; // 가장 왼쪽 값을 pivot으로 설정 18 | 19 | while (low <= high) { // 양쪽에서 탐색하면서 둘이 겹쳐져 지나칠때 까지 한다. 20 | while (low <= end && arr[low] < pivot) { // 앞에서 부터 비교중 pivot 보다 크면 stop 21 | low++; 22 | } 23 | while (high >= start && arr[high] > pivot) { // 뒤에서 부터 비교중 pivot 보다 작으면 stop 24 | high--; 25 | } 26 | if (low < high) { // low , high 가 겹쳐져 지나친게 아니면 둘을 바꿔줌 27 | swap(arr, low, high); 28 | } 29 | } 30 | swap(arr, start, high); // 마지막으로 pivot과 high index의 값을 바꾸면 high index 가 pivot의 index가 됨 31 | 32 | System.out.println(Arrays.toString(arr) + " pivot: " + pivot + " result index: " + high); 33 | 34 | return high; // pivot 위치 반환 35 | } 36 | 37 | static void swap(int[] arr, int i, int j) { 38 | int temp = arr[j]; 39 | arr[j] = arr[i]; 40 | arr[i] = temp; 41 | } 42 | 43 | public static void main(String[] args) { 44 | int[] arr = { 3, 7, 6, 5, 1, 4, 2 }; 45 | System.out.println(Arrays.toString(arr) + " start"); 46 | 47 | quickSort(arr, 0, 6); 48 | 49 | System.out.println(Arrays.toString(arr) + " finish"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contents/algorithm/code/lis_brute.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define pb push_back 5 | #define mp make_pair 6 | #define st first 7 | #define nd second 8 | #define lli long long int 9 | using namespace std; 10 | 11 | 12 | 13 | int lis(vector arr) { 14 | 15 | if(arr.empty()) return 0; 16 | 17 | int len = arr.size(), answer = 1; 18 | 19 | for(int i=0; i < len; i++) { 20 | 21 | vector next; 22 | 23 | for(int j=i+1; j < len; j++) { 24 | 25 | if(arr[j] > arr[i]) next.push_back(arr[j]); 26 | 27 | } 28 | 29 | answer = max(answer, 1 + lis(next)); 30 | 31 | } 32 | 33 | return answer; 34 | 35 | } 36 | 37 | 38 | int main(){ 39 | ios::sync_with_stdio(0); 40 | cin.tie(0); 41 | vector arr; 42 | int n, num; 43 | cin >> n; 44 | for(int i=0;i> num; 46 | arr.pb(num); 47 | } 48 | // arr.pb(10); 49 | // arr.pb(20); 50 | // arr.pb(10); 51 | // arr.pb(30); 52 | // arr.pb(20); 53 | // arr.pb(50); 54 | // arr.pb(40); 55 | // arr.pb(25); 56 | // arr.pb(20); 57 | // arr.pb(50); 58 | // arr.pb(30); 59 | // arr.pb(70); 60 | // arr.pb(85); 61 | 62 | // cout << lis(arr) << "\n"; 63 | } 64 | -------------------------------------------------------------------------------- /contents/algorithm/code/lis_bs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | int arr[1000001],ans[1000001]={0,}; 4 | 5 | int bs(int low,int high,int key){ 6 | if(low > high) return low; 7 | int mid = (low+high)/2; 8 | if(key < ans[mid]) return bs(low,mid-1,key); 9 | else if(key > ans[mid]) return bs(mid+1,high,key); 10 | else return mid; 11 | } 12 | 13 | int main(){ 14 | int n,idx=1; 15 | cin >> n; 16 | for(int i=0;ians[idx-1]){ 20 | ans[idx++] = arr[i]; 21 | } 22 | else{ 23 | int a = bs(0,idx-1,arr[i]); 24 | ans[a] = arr[i]; 25 | } 26 | } 27 | cout << idx; 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /contents/algorithm/code/lis_dp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define pb push_back 3 | using namespace std; 4 | 5 | int dp[1001]; 6 | int arr[1001]; 7 | 8 | int main(){ 9 | ios::sync_with_stdio(0); 10 | cin.tie(0); 11 | int n, top = -1, top_i; 12 | cin >> n; 13 | dp[0] = 0; dp[0] = 0; 14 | for(int i=1;i<=n;i++) cin >> arr[i]; 15 | 16 | for(int i=1;i<=n;i++) { 17 | dp[i] = 1; 18 | for(int j=1;j<=i;j++) { 19 | if(arr[j] < arr[i] && dp[j] +1 > dp[i]) { 20 | dp[i] = dp[j]+1; 21 | } 22 | 23 | if(dp[i] > top) { 24 | top = dp[i]; 25 | top_i = i; 26 | // LIS길이 최댓값을 갖는 인덱스를 top_i에 저장한다. 27 | } 28 | } 29 | } 30 | 31 | cout << top << '\n'; 32 | 33 | // 위에서 저장한 top_i 인덱스부터 시작해서 거꾸로 탐색하면 LIS를 구성하는 요소들을 출력할 수 있다. 34 | for(int i=top_i;i>=1;i--) { 35 | if(dp[i] == top) { 36 | s.push(arr[i]); 37 | top--; 38 | } 39 | } 40 | 41 | while(!s.empty()) { 42 | cout << s.top() << ' '; 43 | s.pop(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contents/algorithm/code/two_pointer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | int N; 7 | long long S; 8 | int arr[100001]; 9 | 10 | int main(void){ 11 | ios::sync_with_stdio(false); 12 | cin.tie(0); 13 | 14 | cin>>N>>S; 15 | for(int i=0; i>arr[i]; 17 | } 18 | 19 | int left = 0; 20 | int right = 0; 21 | int ans = 987654321; 22 | int sum = arr[0]; 23 | while(left<=right && right S){ 32 | ans = min(ans, right-left+1); 33 | sum -= arr[left++]; 34 | } 35 | } 36 | 37 | if(ans == 987654321) 38 | cout<<"0\n"; 39 | else 40 | cout< 작성자 : [서그림](https://github.com/Seogeurim), [정희재](https://github.com/Hee-Jae) 4 | 5 |
6 | Table of Contents 7 | 8 | - [다익스트라 알고리즘 (Dijkstra Algorithm)](#다익스트라-알고리즘-dijkstra-algorithm) 9 | - [벨만-포드 알고리즘 (Bellman-Ford-Moore Algorithm)](#벨만-포드-알고리즘-bellman-ford-moore-algorithm) 10 | - [플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm)](#플로이드-워셜-알고리즘-floyd-warshall-algorithm) 11 | - [분리 집합(Union Find)과 크루스칼(Kruskal) 알고리즘](#분리-집합Union-Find과-크루스칼Kruskal-알고리즘) 12 | 13 |
14 | 15 | --- 16 | 17 | # 최단 경로 알고리즘 18 | 19 | - 작성자 서그림 | [최단 경로 알고리즘](./materials/최단경로알고리즘.pdf) 20 | 21 | ## 최단 경로 문제 22 | 23 | 최단 경로 문제란, 가중 그래프에서 간선의 가중치의 합이 최소가 되는 경로를 찾는 문제이다. 최단 경로 문제는 다음과 같은 유형들이 있다. 24 | 25 | - 단일 출발 (single-source) 최단 경로 : 어떤 하나의 정점에서 출발하여 나머지 모든 정점까지의 최단 경로를 찾는다. 26 | - 단일 도착 (single-destination) 최단 경로 : 모든 정점에서 출발하여 어떤 하나의 정점까지의 최단 경로를 찾는다. 27 | _(그래프 내의 간선들을 뒤집으면 단일 출발 최단 경로 문제로 바뀔 수 있다.)_ 28 | - 단일 쌍 (single-pair) 최단 경로 : 어떤 정점 v에서 v'로 가는 최단 경로를 찾는다. 29 | - 전체 쌍 (all-pair) 최단 경로 : 모든 정점 쌍들 사이의 최단 경로를 찾는다. 30 | 31 | 최단 경로 문제를 해결하는 알고리즘으로는 대표적으로 **다익스트라, 벨만-포드, 플로이드-워셜 알고리즘**이 있다. 각 알고리즘을 적용하기 적합한 유형은 다음과 같다. 32 | 33 | - 다익스트라 알고리즘 : **음이 아닌** 가중 그래프에서의 **단일 출발, 단일 도착, 단일 쌍** 최단 경로 문제 34 | - 벨만-포드 알고리즘 : 가중 그래프에서의 **단일 출발, 단일 도착, 단일 쌍** 최단 경로 문제 35 | - 플로이드-워셜 알고리즘 : **전체 쌍** 최단 경로 문제 36 | - _BFS : 가중치가 없거나 가중치가 동일한 그래프에서 최단 경로를 찾는 경우 가장 빠르다._ 37 | 38 | ## 다익스트라 알고리즘 (Dijkstra Algorithm) 39 | 40 | 그래프 G = (V, E) 에서 특정 출발 정점(S)에서 다른 모든 정점까지의 최단 경로를 구하는 알고리즘이다. 음의 가중치를 가지는 간선이 없을 때 정상적으로 동작한다. 41 | 42 | ### 알고리즘 설계 및 구현 43 | 44 | 1. 출발 노드 S를 설정한다. 45 | 2. 출발 노드 S에서 모든 노드들까지의 최단 거리를 저장하는 배열 D를 초기화한다. 46 | 3. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다. (D 배열 검사) 47 | 4. 해당 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 배열 D를 갱신한다. 48 | 5. 모든 노드를 방문할 때까지 3, 4 과정을 반복한다. 49 | 50 | ![다익스트라](https://user-images.githubusercontent.com/22045163/106482569-eca38480-64f0-11eb-9c52-28a886a9f947.gif) 51 | 52 | 다익스트라 알고리즘 구현 ▶️ [DijkstraTest.java](./code/DijkstraTest.java) 53 | 54 | ### 특징 55 | 56 | - 각 정점을 최대 한 번씩만 방문하여 최단 거리를 확정한다. 57 | - 아직 방문하지 않은 정점들 중 최단 거리인 정점을 찾아 방문하는 식으로 진행된다. 58 | - 이 때, 최단 거리가 최소인 정점을 찾는 과정에서 PriorityQueue 또는 Heap 자료구조를 이용하면 더욱 개선된 알고리즘이 가능하다. 59 | - 매 순간마다 최단 거리의 정점을 선택하는 과정을 반복하므로 그리디 알고리즘으로 분류된다. 60 | - 총 V x V 번 연산이 필요하므로 **O(V^2)** 의 시간복잡도를 가진다. 61 | 62 | ## 개선된 다익스트라 알고리즘 63 | 64 | 다익스트라 알고리즘에는 **방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택**하는 과정이 있다. 이 과정에서 다음과 같은 비용이 발생한다. 65 | 66 | - 방문했는지 여부에 대한 정보를 저장하는 배열을 노드의 크기(V) 만큼 생성하고 접근해야 한다. 67 | - D 배열을 모두 순회하여 최단 거리가 짧은 노드를 선택해야 한다. 68 | 69 | 이 과정을 PriorityQueue 또는 Heap 자료구조를 이용하면 노드를 선택하는 비용을 O(V)에서 O(log{`힙에 저장한 정점의 개수`})로 줄일 수 있다. 70 | 최단 거리가 가장 짧은 노드를 선택해야 하므로 **최소 힙**을 사용하면 되며, 힙을 통해 구현된 **Priority Queue**를 사용해도 좋다. 71 | 동작 과정은 다음과 같다. 72 | 73 | 1. 출발점에 대하여 D 배열을 초기화할 때 `D[S] = 0`을 해준다. 이와 동시에 힙에 노드 정보(번호, 거리 : `[S, 0]`)를 넣어준다. 74 | 2. 힙에서 맨 위에 있는 노드 I를 꺼낸다. 75 | 3. 만일 꺼낸 노드 I의 거리 정보가 현재 D[I]보다 크다면 이미 방문한 노드일 것이므로 무시한다. 76 | 4. I를 대상으로 다익스트라 알고리즘을 수행하는데, D 배열이 갱신될 경우 그 노드 정보를 힙에 넣는다. 77 | (D[J] = D[I] + W 로 갱신될 경우 힙에 노드 J(`[J, D[J]]`)를 삽입한다.) 78 | 5. 힙에 노드가 없을 때까지 반복한다. 79 | 80 | 개선된 다익스트라 알고리즘의 시간 복잡도는 **O(ElogV)** 이다. (O(ElogE) → O(ElogV²) → O(2ElogV) → O(ElogV)) 81 | 82 | ![개선다익스트라](https://user-images.githubusercontent.com/22045163/106778119-f9f37700-6688-11eb-8e6d-6e824596e184.jpg) 83 | 84 | 개선된 다익스트라 알고리즘 구현 ▶️ [DijkstraTest.java > class ImprovedDijkstra](./code/DijkstraTest.java) 85 | 86 | ## 벨만-포드 알고리즘 (Bellman-Ford-Moore Algorithm) 87 | 88 | 그래프 G = (V, E) 에서 특정 출발 정점(S)에서 다른 모든 정점까지의 최단 경로를 구하는 알고리즘이다. 다익스트라 알고리즘과 달리, 음의 가중치를 가지는 간선도 가능하다. 89 | 90 | ### 아이디어 91 | 92 | - 가중 그래프 (V, E)에서 **어떤 정점 A에서 정점 B까지의 최단 거리는 최대 V - 1개의 간선을 사용**한다. (= 시작 정점 A를 포함하여 최대 V개의 정점을 지난다.) 93 | 94 | ### 알고리즘 설계 및 구현 95 | 96 | 1. 출발 노드 S를 설정한다. 97 | 2. 출발 노드 S에서 모든 노드들까지의 최단 거리를 저장하는 배열 D를 초기화한다. 98 | 3. 그래프의 모든 간선을 돌면서 각 노드로 가는 비용을 계산하여 최단 거리 배열 D를 갱신한다. 99 | 4. 3 과정을 (노드의 개수 - 1)번, 즉 V-1번 반복한다. 100 | 5. 3 과정을 한 번 더 반복하였을 때, 배열 D가 갱신되면 음의 사이클이 있는 것으로 판단한다. 101 | 102 | ![벨만포드](https://user-images.githubusercontent.com/22045163/106553083-f6160680-655b-11eb-8da9-cda67af0493e.gif) 103 | 104 | 벨만-포드 알고리즘 구현 ▶️ [BellmanFordTest.java](./code/BellmanFordTest.java) 105 | 106 | ### 특징 107 | 108 | - 음의 가중치를 가지는 간선도 가능하므로, 음의 사이클의 존재 여부를 따져야 한다. 109 | - 최단 거리를 구하기 위해서 V - 1번 E개의 모든 간선을 확인한다. 110 | - 음의 사이클 존재 여부를 확인하기 위해서 한 번 더 (V번째) E개의 간선을 확인한다. 이 때 거리 배열이 갱신되었다면, 그래프 G는 음의 사이클을 가진다. 111 | - 따라서 총 V x E 번 연산하므로 **O(VE)** 의 시간복잡도를 가진다. 112 | 113 | ## 플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm) 114 | 115 | 그래프 G = (V, E) 에서 모든 정점 사이의 최단 경로를 구하는 알고리즘이다. 116 | 117 | ### 아이디어 118 | 119 | - 어떤 두 정점 사이의 최단 경로는 **어떤 경유지(K)를 거치거나, 거치지 않는 경로 중 하나**이다. 즉 정점 A와 정점 B 사이의 최단 경로는 A-B 이거나 A-K-B 이다. 120 | - 만약 경유지(K)를 거친다면 최단 경로를 이루는 부분 경로 역시 최단 경로이다. 즉 A-B의 최단 경로가 A-K-B라면 A-K와 K-B도 각각 최단 경로이다. 121 | 122 | ### 알고리즘 설계 및 구현 123 | 124 | 1. 각 노드들 사이의 최단 거리를 저장하는 2차원 배열 D를 초기화한다. 125 | 2. 각 노드가 경유지 K를 지날 때마다 최단 경로를 계산하여 배열 D를 갱신한다. 126 | 3. 동적 계획법으로 해결하며, 점화식은 `D[A][B] = min(D[A][B], D[A][K] + D[K][B]` 이다. 127 | 128 | ![플로이드워셜](https://user-images.githubusercontent.com/22045163/106489876-95091700-64f8-11eb-91fa-bd903685f284.gif) 129 | 130 | 플로이드-워셜 알고리즘 구현 ▶️ [FloydWarshallTest.java](./code/FloydWarshallTest.java) 131 | 132 | ### 특징 133 | 134 | - 사이클이 없다면 음수 가중치를 가져도 적용 가능하다. 135 | - 동적 계획법(Dynamic Programming)으로 접근한다. 136 | - 모든 가능한 경유지에 대해서 모든 정점 -> 모든 정점으로 가는 최단 거리를 확인하므로 연산 횟수는 V^3이고, 따라서 시간복잡도는 **O(V^3)** 이다. 137 | 138 | --- 139 | 140 | # 분리 집합(Union Find)과 크루스칼(Kruskal) 알고리즘 141 | 142 | > 스터디 자료 - 작성자 정희재 | [Union Find & Kruskal Algorithm](./materials/유니온파인드.pdf) 143 | 144 | ## 서로소 집합(Disjoint Set)과 Union-Find 145 | 146 | 서로소 집합(Disjoint Set)은 교집합이 없는, 즉 공통되는 원소가 없는 집합을 말한다. Union-Find는 서로소 집합을 표현할 때 사용되는 알고리즘으로, 서로소 집합의 정보를 확인(Find)하고 조작(Union)한다. Union Find 알고리즘을 이용하면 **서로 다른 두 노드가 같은 집합 내에 속해 있는지 확인**할 수 있다. 147 | 148 | ### Union Find 구현 149 | 150 | #### 초기화 (initialize) 151 | 152 | root 배열에 i 원소의 부모 노드 번호를 저장한다. i 원소가 루트 노드라면, 자기 자신의 번호를 저장한다. 153 | 154 | ```java 155 | void initialize() { 156 | for (int i = 1; i <= N; i++) { 157 | root[i] = i; 158 | } 159 | } 160 | ``` 161 | 162 | #### n번 노드의 root 노드 번호 찾기 (find) 163 | 164 | ```java 165 | int find(int n) { 166 | if (root[n] == n) return n; 167 | return root[n] = find(root[n]); 168 | } 169 | ``` 170 | 171 | #### a 노드와 b 노드를 같은 집합으로 묶기 (merge, union) 172 | 173 | ```java 174 | void merge(int a, int b) { 175 | root[find(b)] = find(a); 176 | } 177 | ``` 178 | 179 | ## Union Find를 활용한 MST 찾기 - Kruskal Algorithm 180 | 181 | 무향 그래프 G가 순환이 없는 연결 그래프이면 그래프 G는 트리(Tree)이다. 182 | 183 | 신장 트리 (Spanning Tree)란 무향 연결 그래프 G의 부분 그래프이며, G의 모든 정점을 포함하는 트리(Tree)인 그래프이다. 184 | 185 | 여기서 최소 신장 트리 (Minimum Spanning Tree, MST)란 무향 연결 그래프 G에서 간선의 가중치의 합이 최소인 신장 트리이다. 이 최소 신장 트리 MST를 구할 수 있는 알고리즘으로는 크루스칼 알고리즘, 프림 알고리즘이 있다. 그 중 프림 알고리즘은 크루스칼 알고리즘에 비해 느리고 잘 안 쓰이므로 크루스칼 알고리즘에 대해 알아보겠다. 크루스칼 알고리즘은 Union-Find를 사용해 구현할 수 있다. 186 | 187 | ### Kruskal Algorithm 구현 188 | 189 | 1. (Cost, A, B) 리스트를 만들어서 모든 간선들의 정보를 Priority Queue에 저장한다. (최소 힙) 190 | 2. Priority Queue에서 하나씩 pop하면서 **만약 A와 B가 연결되어 있지 않다면 A와 B를 연결**하고 전체 비용에 Cost를 더한다. 191 | 3. **만일 A와 B가 연결되어 있다면** 무시한다. 192 | 193 | 굵은 글씨 부분을 Union-Find로 구현한다. 194 | 195 | > 시간 복잡도 (E: 간선의 개수) : ![formula](https://render.githubusercontent.com/render/math?math=O(Elog_2E)) 196 | 197 | Kruskal 알고리즘 구현 ▶️ [KruskalTest.java](./code/KruskalTest.java) 198 | -------------------------------------------------------------------------------- /contents/algorithm/greedy.md: -------------------------------------------------------------------------------- 1 | # Greedy Algorithm 2 | 3 | ## Greedy 알고리즘 이란? 4 | 5 | **그리디 알고리즘**(욕심쟁이 알고리즘, Greedy Algorithm)이란 "매 선택에서 **지금 이 순간 당장 최적인 답**을 선택하여 적합한 결과를 도출하자" 라는 모토를 가지는 알고리즘 설계 기법이다. 6 | 7 | 8 | 9 | ## Greedy 알고리즘 예제 10 | 11 | 5개의 도시가 존재하고 1번도시에서 5번도시까지 가는데 도시들을 한번 씩만 거쳐 가는 경우 각 길에는 비용이 존재한다고 하면 최저의 비용으로 5번도시로 가능 비용을 구하려고 한다. 12 | 13 | 이러한 경우 그리디 알고리즘을 사용하면 1번 도시 부터 연결되어 있는 도시 중 가장 비용이 적은 길을 선택하여 다음 도시로 이동 한다. 이러한 과정을 반복하여 5번도시 까지 이동한다. 14 | 15 | 여기서 선택된 길들의 비용이 1 - 1 - 1 - 100 이라고 할 때, 이것이 과연 최적의 답이냐고 물어본다면 그럴수도 있고 아닐 수도 있고이다. 즉 각 순간순간의 최적의 답을 구한다고 무조건적으로 전체에 최적의 답이라는 것은 보장할 수 없다는 것이다. 예를들면 순간순간의 최적의 답을 구한 1 - 1 - 1- 100 이 아닌 3번째 길을 다른 길을 선택했다면 1 - 1 - 10 - 10 이라는 더욱 최적의 답이 나올 수 있는 것이다. 즉 3번째 길을 순간의 최적이 아닌 다른 길을 선택했더니 더욱 최적의 결과가 나온 것이다. 16 | 17 | 18 | 19 | ## Greedy 알고리즘 사용 20 | 21 | 위와 같이 순간의 최적을 구한다고 종합적인 최적을 구할 수 있다고 보장 할 수 없다면 해당 알고리즘은 언제 어떻게 사용 해야 할까? 다음과 같은 경우에 사용 할 수 있다. 22 | 23 | - 탐욕 선택 속성(greedy choice property) 24 | - 최적 부분 구조(optimal substructure) 25 | 26 | 탐욕 선택 속성이란 한번의 선택이 다음 선택에 아무런 영향을 끼치지 않는 것을 말한다. 27 | 28 | 최적 부분 구조란 매순간의 최적해의 집합이 전체 문제에 대한 최적해여야 하는 것을 말한다. 29 | 30 | 31 | 32 | ## Greedy 알고리즘 사용 예시 33 | 34 | - AI에 있어서 결정 트리 학습법(Decision Tree Learning) 35 | - 활동 선택 문제(Activity selection problem) 36 | - 거스름돈 문제 37 | - 최소 신장 트리 (Minimum spanning tree) 38 | - 제약조건이 많은 대부분의 문제 39 | - 다익스트라 알고리즘 40 | - 허프만 코드 41 | - 크루스칼 알고리즘 42 | 43 | 44 | 45 | ## Greedy 알고리즘으로 최적해를 구할 수 없는 문제 46 | 47 | - 외판원 순회 문제 (TSP, Traveling Salesperson Problem) 48 | - 배낭 문제 (Knapsack Problem) 49 | 50 | 51 | 52 | 53 | 54 | **Reference** [나무위키(그리디 알고리즘)](https://namu.wiki/w/%EA%B7%B8%EB%A6%AC%EB%94%94%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98) 55 | 56 | -------------------------------------------------------------------------------- /contents/algorithm/img/chess_board_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/img/chess_board_1.png -------------------------------------------------------------------------------- /contents/algorithm/materials/유니온파인드.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/materials/유니온파인드.pdf -------------------------------------------------------------------------------- /contents/algorithm/materials/이세명_algorithm_sorting.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/materials/이세명_algorithm_sorting.pdf -------------------------------------------------------------------------------- /contents/algorithm/materials/최단경로알고리즘.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/materials/최단경로알고리즘.pdf -------------------------------------------------------------------------------- /contents/algorithm/materials/최장감소수열_두포인터.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/materials/최장감소수열_두포인터.pdf -------------------------------------------------------------------------------- /contents/algorithm/materials/최장증가수열.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/algorithm/materials/최장증가수열.pdf -------------------------------------------------------------------------------- /contents/algorithm/sort.md: -------------------------------------------------------------------------------- 1 | # 정렬 알고리즘의 종류와 개념 2 | 3 | > 작성자 : [장주섭](https://github.com/wntjq68), [이세명](https://github.com/3people) 4 | 5 |
6 | Table of Contents 7 | 8 | - [Insertion Sort (삽입정렬)](#insertion-sort-삽입정렬) 9 | - [Selection Sort (선택정렬)](#selection-sort-선택정렬) 10 | - [Bubble Sort (거품정렬)](#bubble-sort-거품정렬) 11 | - [Merge Sort (합병정렬)](#merge-sort-합병정렬) 12 | - [Quick Sort (퀵정렬)](#quick-sort-퀵정렬) 13 | - [Quick Sort vs Merge Sort](#quick-sort-vs-merge-sort) 14 | - [Heap, Radix, Counting Sort](#heap-radix-counting-sort) 15 | 16 |
17 | 18 | --- 19 | 20 | ## Insertion Sort (삽입정렬) 21 | 22 | 삽입정렬이란, 배열의 모든 요소를 배열의 시작부터 끝까지 현재 배열의 요소들과 비교해 가면서 적절한 위치에 삽입하는 정렬 알고리즘이다. 23 | 24 | ### Example 25 | 26 | > 28 13 23 25 19 : 초기 배열 27 | > 28 | > 28 **13** 23 25 19 : 2번째 자리 13 부터 시작 29 | > 30 | > 13 28 **23** 25 19 : 13은 적절한 자리를 찾아가고 다음으로 3번째 자리 23을 본다. 31 | > 32 | > 13 23 28 **25** 19 : 23도 적절한 자리를 찾아가고 다음으로 4번째 자리 25를 본다. 33 | > 34 | > 13 23 25 28 **19** : 25도 적절한 자리를 찾아가고 다음으로 5번쨰 자리 19를 본다. 35 | > 36 | > 13 19 23 25 28 : 정렬 완료! 37 | 38 | ### 시간복잡도 39 | 40 | ![formula]() 41 | 42 | ### Source Code 43 | 44 | ```java 45 | void insertionSort(int[] arr) 46 | { 47 | 48 | for(int index = 1 ; index < arr.length ; index++){ 49 | 50 | int temp = arr[index]; 51 | int aux = index - 1; 52 | 53 | while( (aux >= 0) && ( arr[aux] > temp ) ) { 54 | 55 | arr[aux+1] = arr[aux]; 56 | aux--; 57 | } 58 | arr[aux + 1] = temp; 59 | } 60 | } 61 | //출처 위키피디아 62 | ``` 63 | 64 | --- 65 | 66 | ## Selection Sort (선택정렬) 67 | 68 | 선택정렬이란, 주어진 배열 중에 최솟값을 찾아 정렬 되지 않은 배열의 맨앞의 값과 자리를 바꾸어 나가는 정렬 알고리즘이다. 69 | 70 | 배열의 맨앞부터 차례로 정렬이 된다. 71 | 72 | ### Example 73 | 74 | > **28 13 23 25 19** : 초기배열 75 | > 76 | > 13 **28 23 25 19** : 최솟값 13을 맨 앞의 수 28과 자리 바꾸기 77 | > 78 | > 13 19 **23 25 28** : 다음 최솟값 19를 맨 앞의 수 28과 자리 바꾸기 79 | > 80 | > 13 19 23 **25 28** : 다음 최솟값은 23이니깐 자리 그대로! 81 | > 82 | > 13 19 23 25 **28** : 다음 최솟값은 25이니깐 자리 그대로! 83 | > 84 | > 13 19 23 25 28 : 정렬 완료! 85 | 86 | ### 시간복잡도 87 | 88 | ![formula]() 89 | 90 | ### Source Code 91 | 92 | ```java 93 | void selectionSort(int[] list) { 94 | int indexMin, temp; 95 | 96 | for (int i = 0; i < list.length - 1; i++) { 97 | indexMin = i; 98 | for (int j = i + 1; j < list.length; j++) { 99 | if (list[j] < list[indexMin]) { 100 | indexMin = j; 101 | } 102 | } 103 | temp = list[indexMin]; 104 | list[indexMin] = list[i]; 105 | list[i] = temp; 106 | } 107 | } 108 | //출처 위키피디아 109 | ``` 110 | 111 | --- 112 | 113 | ## Bubble Sort (거품정렬) 114 | 115 | 거품정렬이란, 인접한 두 원소를 비교해 가면서 자리를 바꾸는 방식의 정렬 알고리즘이다. 116 | 117 | 배열의 맨뒤부터 차례로 정렬이 된다. 118 | 119 | ### Example 120 | 121 | > **(28 13) 23 25 19** : 28 > 13 Change! 122 | > 123 | > **13 (28 23) 25 19** : 28 > 23 Change! 124 | > 125 | > **13 23 (28 25) 19** : 28 > 25 Change! 126 | > 127 | > **13 23 25 (28 19)** : 28 > 19 Change! 128 | > 129 | > **(13 23) 25 19** 28 : 28 최댓값으로 고정, 13 < 23 Pass! 130 | > 131 | > **13 (23 25) 19** 28 : 23 < 25 Pass! 132 | > 133 | > **13 23 (25 19)** 28 : 25 > 19 Change! 134 | > 135 | > **(13 23) 19** 25 28 : 25 다음 최댓값으로 고정, 13 < 23 Pass! 136 | > 137 | > **13 (23 19)** 25 28 : 23 > 19 Change! 138 | > 139 | > **(13 19)** 23 25 28 : 23 다음 최댓값으로 고정, 13 < 19 Pass! 140 | > 141 | > 13 19 23 25 28 : 19 다음 최댓값으로 고정, 정렬 완료! 142 | 143 | ### 시간복잡도 144 | 145 | ![formula]() 146 | 147 | ### Source Code 148 | 149 | ```java 150 | void bubbleSort(int[] arr) { 151 | int temp = 0; 152 | for(int i = 0; i < arr.length; i++) { 153 | for(int j= 1 ; j < arr.length-i; j++) { 154 | if(arr[j] 178 | 179 | ### 시간복잡도 180 | 181 | - Avaerage Case : ![formula]() 182 | - Worst Case : ![formula]() 183 | 184 | ### Source Code 185 | 186 | ```java 187 | void mergeSort(int[] arr, int start, int end) { 188 | if (start < end) { 189 | int mid = (start + end) / 2; 190 | mergeSort(arr, start, mid); 191 | mergeSort(arr, mid + 1, end); 192 | merge(arr, start, mid, end); 193 | } 194 | } 195 | 196 | void merge(int[] arr, int start, int mid, int end) { 197 | int i = start; // 왼쪽 배열의 시작 198 | int j = mid + 1; // 오른쪽 배열의 시작 199 | int k = 0; // 병합된 배열의 시작 200 | 201 | int[] temp = new int[end - start + 1]; 202 | while (i <= mid && j <= end) { 203 | if (arr[i] < arr[j]) { 204 | temp[k++] = arr[i++]; 205 | } else { 206 | temp[k++] = arr[j++]; 207 | } 208 | } 209 | // 남은 값 복사 210 | if (i > mid) { // 왼쪽 배열 값 다 사용함 , 오른쪽 다 복사 211 | for (int idx = j; idx <= end; idx++, k++) { 212 | temp[k] = arr[idx]; 213 | } 214 | } else { // 오른쪽 배열 값 다 사용함, 왼쪽 다 복사 215 | for (int idx = i; idx <= mid; idx++, k++) { 216 | temp[k] = arr[idx]; 217 | } 218 | } 219 | // 임시 배열 -> 원래 배열 220 | for (int num : temp) { 221 | arr[start++] = num; 222 | } 223 | } 224 | ``` 225 | 226 | ### 장점 227 | 228 | - 안정적인 정렬 방법이다. 입력데이터의 분포에 상관없이 ![formula]() 를 유지 할 수 있다. 229 | 230 | ### 단점 231 | 232 | - 배열을 사용하면 임시 배열을 사용해야 한다. 233 | - 배열의 크기가 커지면 데이터의 이동 횟수가 많아져 시간이 커질 수 있다. 234 | 235 | --- 236 | 237 | ## Quick Sort (퀵정렬) 238 | 239 | 합병정렬은 문제를 분리하고 각각을 해결한 후 다시 합치는 Divide & Concuer 방식을 사용한다. 240 | 241 | 1. 주어진 배열에서 pivot을 선정한다.(밑에 소스코드에선 맨 앞의 값으로 함) 242 | 2. pivot을 기준으로 작은 값들이 모인 배열과 큰 값들이 모인 배열로 비균등하게 배열을 두 부분으로 나눈다. 243 | 3. 해당 pivot을 기준으로 배열을 나누고 나면 pivot은 고정 된다. (재귀 호출마다 pivot의 위치는 매번 고정 되므로 이 알고리즘이 반드시 끝나는 것을 보장한다.) 244 | 4. 분할된 두 배열에서 재귀적으로 이 과정을 반복한다. 245 | 246 | ### Example 247 | 248 | > 3 7 6 5 1 4 2 : 초기배열 249 | > 250 | > **pivot = 3** 251 | > 252 | > 3 **7** 6 5 1 4 **2** : low = 1, high = 6, swap! 253 | > 254 | > 3 2 **6** 5 1 **4** 7 : low = 2, high, = 5 255 | > 256 | > 3 2 **6** 5 **1** 4 7 : low = 2, high = 4, swap! 257 | > 258 | > 3 2 1 **5** 6 4 7 : low = 3, high = 3 259 | > 260 | > 3 2 **1** **5** 6 4 7 : low = 3, high = 2 (서로 지나침) => high 와 pivot swap! 261 | > 262 | > 1 2 **3** 5 6 4 7 263 | > 264 | > **pivot = 1** 265 | > 266 | > ... 267 | > 268 | > **pivot = 5** 269 | > 270 | > ... 271 | 272 | ### 시간복잡도 273 | 274 | - Average Case : ![formula]() 275 | - Worst Case : ![formula]() 276 | 277 | ### Source Code 278 | 279 | ```java 280 | void quickSort(int[] arr, int start, int end) { 281 | if (start < end) { // 배열의 크기가 충분히 작아 질 때 까지 나눔 282 | int p = partition(arr, start, end); // 파티션을 적용 했을 때 피봇의 인덱스를 구함 283 | 284 | quickSort(arr, start, p - 1); // 처음 부터 피봇 전, 285 | quickSort(arr, p + 1, end); // 피봇 후 부터 마지막 까지 다시 퀵소트를 함 286 | } 287 | } 288 | 289 | int partition(int[] arr, int start, int end) { 290 | int low = start + 1; // pivot을 맨 왼쪽 값으로 할것이기 때문에 그 다음 값부터 확인 291 | int high = end; 292 | int pivot = arr[start]; // 가장 왼쪽 값을 pivot으로 설정 293 | 294 | while (low <= high) { // 양쪽에서 탐색하면서 둘이 겹쳐져 지나칠때 까지 한다. 295 | while (low <= end && arr[low] < pivot) { // 앞에서 부터 비교중 pivot 보다 크면 stop 296 | low++; 297 | } 298 | while (high >= start && arr[high] > pivot) { // 뒤에서 부터 비교중 pivot 보다 작으면 stop 299 | high--; 300 | } 301 | if (low < high) { // low , high 가 겹쳐져 지나친게 아니면 둘을 바꿔줌 302 | swap(arr, low, high); 303 | } 304 | } 305 | swap(arr, start, high); // 마지막으로 pivot과 high index의 값을 바꾸면 high index 가 pivot의 index가 됨 306 | return high; // pivot 위치 반환 307 | } 308 | 309 | void swap(int[] arr, int i, int j) { 310 | int temp = arr[j]; 311 | arr[j] = arr[i]; 312 | arr[i] = temp; 313 | } 314 | 315 | ``` 316 | 317 | ### 장점 318 | 319 | - 속도가 매우 빠르다 320 | - 추가적인 메모리 공간을 필요로 하지 않는다 321 | 322 | ### 단점 323 | 324 | - 불균형 분할이 많아질 경우 최악의 경우 시간이 오래 걸릴 수 있다. 325 | - 불균형 분할을 방지하기 위해 세 값의 중위법을 사용해서 피벗을 선택하는 경우가 많다. 326 | 327 | --- 328 | 329 | ## Quick Sort vs Merge Sort 330 | 331 | 두 정렬 알고리즘 다 average case ![formula]() 이고 심지어 Quick Sort는 worst case ![formula]()이다. 하지만 보통 일반적으로 퀵소트가 빠른것으로 알고 있다. 332 | 333 | 일단 Quick Sort 의 worst case 는 (맨앞의 수를 pivot으로 가정) 아이러니 하게 정렬이 잘 되어 있는 배열에서 나온다. 334 | 335 | 이렇게 Quick Sort의 worst case 와 평균 적인 Merge Sort 를 비교해 보아도 Quick Sort가 더 빠르게 나온다. 그 이유는 실제 시간 정렬 되는 중 Merge Sort는 분할 과정에서 추가적인 배열을 생성해야 한다는 문제가 있다. 이러한 과정에서 계속적으로 Delay 가 생기다 보니 결과적으로 Quick Sort가 더욱 빠르게 정렬이 되는 것이다. 336 | 337 | --- 338 | 339 | ## Heap, Radix, Counting Sort 340 | 341 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 342 | 343 | - 작성자 이세명 | [Heap, Radix, Counting Sort](./materials/이세명_algorithm_sorting.pdf) 344 | -------------------------------------------------------------------------------- /contents/algorithm/string.md: -------------------------------------------------------------------------------- 1 | # 문자열 처리 알고리즘 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 |
6 | Table of Contents 7 | 8 | - [KMP 알고리즘 (Knuth-Morris-Pratt Algorithm)](#KMP-알고리즘-knuth-morris-pratt-algorithm) 9 | 10 |
11 | 12 | --- 13 | 14 | # 문자열 패턴 매칭 15 | 16 | > 문자열 T 안에 문자열 패턴 P가 존재하는지 찾아보자. 17 | 18 | 문자열 패턴 매칭에 완전탐색 알고리즘을 적용하면 문자열 T를 차례로 순회하면서 패턴 P 내의 문자들을 일일이 비교하게 된다. 이 때 최악의 경우 시간복잡도는 O(TP)를 가지므로, 매우 비효율적이다. 19 | 20 | ## KMP 알고리즘 (Knuth-Morris-Pratt Algorithm) 21 | 22 | 완전탐색 알고리즘에서 '활용하지 못하는 정보'는 패턴이 안 맞다고 결론내기 전 '어디까지의 문자열이 일치했는지'에 대한 정보이다. KMP 알고리즘은 이 정보를 활용한다. 따라서 불일치가 발생하기 전의 문자열에 대하여 다시 비교하지 않고 매칭을 수행한다. **O(T+P)** 의 시간복잡도를 가지는 효율적인 알고리즘이다. 23 | 24 | ### 알고리즘 설계 및 구현 25 | 26 | 1. 패턴 P에 대하여 `fail[k]`를 구한다. (`fail[k]` : k 인덱스에서 접두사와 접미사가 일치하는 최대 길이) 27 | 2. 문자열 T는 인덱스 i로, 패턴 P는 인덱스 j로 차례로 순회하며 각 문자들을 비교한다. 28 | 3. j 인덱스에서 문자열 불일치가 발생하면 j 값을 `fail[j-1]` 값으로 설정하고 다시 비교를 수행해나간다. (j가 0이 되면 멈춤) 29 | 30 | #### 실패함수 fail 만들기 31 | 32 | fail 함수는 패턴 매칭이 실패했을 때 패턴 포인터가 돌아갈 곳에 대한 정보를 관리한다. (전처리) 33 | 34 | 1. 0 인덱스는 돌아갈 곳이 없으므로 `fail[0] = 0;` 35 | 2. 패턴 P에 대하여 접미사는 인덱스 i로, 접두사는 인덱스 j로 차례로 순회하며 각 문자들을 비교한다. 36 | 3-1. i 인덱스와 j 인덱스의 문자열이 일치하면 `fail[i] = ++j;` 37 | 3-2. j 인덱스에서 문자열 불일치가 발생하면 j 값을 `fail[j-1]` 값으로 설정하고 다시 비교를 수행해나간다. (j가 0이 되면 멈춤) 38 | 39 | KMP 알고리즘 구현 ▶️ [KMPTest.java](./code/KMPTest.java) 40 | -------------------------------------------------------------------------------- /contents/algorithm/two-pointer.md: -------------------------------------------------------------------------------- 1 | # 두 포인터 (two-pointer) 2 | 3 | > 작성자 : [권혁진](https://github.com/KimKwon), [윤가영](https://github.com/yoongoing) 4 | 5 |
6 | Table of Contents 7 | 8 | - [최장 증가 부분수열 알고리즘](#최장-증가-부분수열-알고리즘) 9 | - [최장 감소 부분수열 알고리즘](#최장-감소-부분수열-알고리즘) 10 | - [투 포인터 알고리즘](#투-포인터-알고리즘) 11 | 12 |
13 | 14 | --- 15 | 16 | ## 최장 증가 부분수열 알고리즘 17 | 18 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 19 | 20 | - 작성자 권혁진 | [최장증가부분수열 LIS](./materials/최장증가수열.pdf) 21 | 22 | LIS 완전탐색 알고리즘 구현 ▶️ [lis_brute.cpp](./code/lis_brute.cpp) 23 | 24 | LIS 동적계획법 알고리즘 구현 ▶️ [lis_dp.cpp](./code/lis_dp.cpp) 25 | 26 | LIS 이분탐색 알고리즘 구현 ▶️ [lis_bs.cpp](./code/lis_bs.cpp) 27 | 28 | --- 29 | 30 | ## 최장 감소 부분수열 알고리즘 31 | 32 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 33 | 34 | - 작성자 윤가영 | [최장감소부분수열 LDS & 두포인터](./materials/최장감소수열_두포인터.pdf) 35 | 36 | LDS 이분탐색 알고리즘 구현 ▶️ [LDS_bs.cpp](./code/LDS_bs.cpp) 37 | 38 | --- 39 | 40 | ## 투 포인터 알고리즘 41 | 42 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 43 | 44 | - 작성자 윤가영 | [최장감소부분수열 LDS & 두포인터](./materials/최장감소수열_두포인터.pdf) 45 | 46 | 투 포인터 알고리즘 구현 ▶️ [two_pointer.cpp](./code/two_pointer.cpp) 47 | 48 | ### 특징 49 | 50 | - 배열이 정렬되어있다면 O(n)의 시간복잡도를 가지며, 그렇지 않을 경우 O(logn)의 복잡도를 가진다. 51 | -------------------------------------------------------------------------------- /contents/data-structure/README.md: -------------------------------------------------------------------------------- 1 | # Data Structure (자료구조) 2 | 3 | ## 기본 자료 구조 [▶︎ 🗒](basic.md) 4 | 5 | - [Array](basic.md#array-배열) 6 | - [Linked List](basic.md#linked-list-연결-리스트) 7 | - [Stack](basic.md#stack-스택) 8 | - [Queue](basic.md#queue-큐) 9 | - [Tree](basic.md#tree-트리) 10 | - [Binary Tree](basic.md#binary-tree-이진-트리) 11 | - [Graph](basic.md#graph-그래프) 12 | 13 | ## 응용 자료 구조 [▶︎ 🗒](advanced.md) 14 | 15 | - [Deque](advanced.md#deque-덱) 16 | - Heap & Priority Queue 17 | - [Indexed Tree (Segment Tree)](advanced.md#세그먼트-트리-indexed-tree--segment-tree) 18 | - [Trie](advanced.md#trie-트라이) 19 | 20 | --- 21 | 22 | ## 질의응답 23 | 24 |
25 | 자료구조가 무엇인지 말씀해주세요. 26 |

27 | 28 | 자료구조는 컴퓨터 과학에서 `효율적인 접근 및 수정`을 가능케 하는 자료의 조직, 관리, 저장을 의미한다. 29 | 더 정확히 말해, 자료 구조는 데이터 값의 모임, 또 데이터 간의 관계, 그리고 데이터에 적용할 수 있는 함수나 명령을 의미한다. 30 | 31 |

32 |
33 | 34 |
35 | 선형 구조와 비선형 구조의 차이가 무엇인가요? 36 |

37 | 38 | 자료구조는 저장되는 데이터의 형태에 따라 구분된다. 선형 구조는 데이터가 일렬로 나열되어있고, 비선형 구조는 데이터가 특정한 형태를 띄고 있다. 39 | 40 |

41 |
42 | 43 |
44 | 배열과 연결 리스트의 차이점에 대해 설명해주세요. 45 |

46 | 47 | 배열은 동일한 자료형의 데이터를 일렬로 나열한 자료구조로서, 데이터 접근이 용이하나 데이터의 삽입과 삭제가 비효율적이다. 48 | 49 | 연결리스트는 각 노드가 데이터와 포인터를 가지고 일렬로 연결된 자료구조로서, 데이터의 접근이 O(n)으로 느리지만 데이터의 삽입과 삭제가 O(1)로 용이하다. 단, 데이터 삽입/삭제 연산 이전에 데이터에 접근하는 것이 선행되므로 배열보다 비효율적일 수 있다. 50 | 51 |

52 |
53 | 54 |
55 | A-B-C-D 순서로 연결된 연결 리스트가 있습니다. C 노드 다음에 F 노드를 삽입할 때의 과정을 설명해주세요. 56 |

57 | 58 | 1. F의 next node를 C의 next node인 D로 설정한다. 59 | `A-B-C-D` 60 | `F-D` 61 | 62 | 2. C의 next node를 F로 설정한다. 63 | `A-B-C-F-D` 64 | 65 |

66 |
67 | 68 |
69 | 스택으로 큐를 구현할 수 있을까요? 70 |

71 | 72 | 네. 2개의 스택을 이용하여 구현할 수 있습니다. Enqueue 연산은 첫번째 스택에 원소를 추가하면 됩니다. Dequeue 연산은 두번째 스택을 이용합니다. 우선 두번째 스택이 비어있다면 첫번째 스택이 빌 때까지 첫번째 스택의 원소를 pop하고 두번째 스택에 push하는 것을 반복합니다. 그리고 두번째 스택이 비어있지 않다면 두번째 스택의 원소를 pop하면 됩니다. 73 | 74 |

75 |
76 | 77 |
78 | 큐로 스택을 구현할 수 있을까요? 79 |

80 | 81 | 네. 2개의 큐를 이용하여 구현할 수 있습니다. `push` 연산은 첫번째 큐에 원소를 추가하기 전에 첫번째 큐가 빌때까지 두번째 큐로 값을 옮겨줍니다. 그 후 첫번째 큐에 원소를 추가하고 두번째 큐에서 다시 첫번째 큐로 빌때까지 원소들을 전부 다시 옮겨줍니다. 쉽게 말하자면 원소를 추가할 때마다 원소들의 위치를 스택에 맞게 변경시키는 것입니다. `pop` 연산은 첫번째 큐에서 dequeue만 하면 됩니다. 82 | 83 |

84 |
85 | 86 |
87 | 큐와 덱의 차이점은 무엇일까요? 88 |

89 | 90 | `큐` 는 front에서만 output이 발생하고 rear에서만 input이 발생하는 입출력의 방향이 제한되어 있는 자료구조이다. 반면 `덱` 은 양방향에서 입출력이 가능하다. 91 | 92 |

93 |
94 | 95 |
96 | 큐보다 덱을 사용했을 때 더 효율적인 경우가 있을까요? 97 |

98 | 99 | 스케줄링 알고리즘을 수행할 때 스케줄링이 복잡해질수록 덱이 더 효율적으로 동작한다. 즉, 우선순위를 관리하는 데 있어 스택과 큐에 비해 이점을 갖는다. 100 | 예를 들어 오래된 프로세스에 우선순위를 주고 싶다면 앞에 있는 프로세스를 빼내야하는데 이는 스택에서 불가능하고 최근에 들어온 프로세스에 우선순위를 두고 싶다면 큐에서 불가능하다. 반면 덱은 두 경우 모두에서 사용 가능하다. 101 | 102 |

103 |
104 | 105 |
106 | 트리라는 자료구조가 무엇인지 간략하게 한 줄로 설명해보세요. 107 |

108 | 109 | 자료들 사이의 계층적 관계를 나타내는데 사용하는 자료구조로 부모-자식관계로 표현합니다. 110 | 111 |

112 |
113 | 114 |
115 | 트리의 용어중 '깊이' 라는 용어의 정의는 무엇인가요? 116 |

117 | 118 | 루트 노드에서 해당노드까지 도달하는데 사용하는 간선의 개수며, 루트노드의 깊이는 0입니다. 119 | 120 |

121 |
122 | 123 |
124 | 포화 이진트리와 완전 이진트리의 차이점은 무엇인가요? 125 |

126 | 127 | 1. 포화 이진 트리(Perfect Binary Tree) : 정 이진트리(Full Binary Tree)에서 모든 단말 노드의 깊이가 같은 이진트리 128 | 2. 완전 이진 트리(Complete Binary Tree) : 마지막 레벨은 노드가 왼쪽에 몰려있고, 마지막 레벨을 제외하면 포화이진트리(Perfect Binary Tree) 구조를 띄고 있음 129 | 130 |

131 |
132 | 133 |
134 | 트리의 순회에 대해 알고있는 것 한 가지 말해주세요. 135 |

136 | 137 | 1. 전위 순회(Pre-order) : __현재 노드 방문__ -> 왼쪽 자식 탐색 -> 오른쪽 자식 탐색 138 | 2. 중위 순회(In-order) : 왼쪽 자식 탐색 -> __현재 노드 방문__ -> 오른쪽 자식 탐색 139 | 3. 후위 순회(Post-order) : 왼쪽 자식 탐색 -> 오른쪽 자식 탐색 -> __현재노드 방문__ 140 | 141 |

142 |
143 | 144 |
145 | 구간합 문제를 누적합으로 풀이한다면, 단점은 무엇이며 그에 비해 인덱스 트리가 갖는 장점은 무엇인지 시간복잡도를 들어 설명해주세요. 146 |

147 | 148 | 누적합으로 풀 경우 누적합을 구하는데 O(N), 이를 M번 수행하면 O(MN)이 걸린다. 하지만 인덱스 트리를 사용할 경우 누적합을 구하는데 O(logN)이 걸리므로, 이를 M번 수행하면 O(MlogN)이 걸리기에 구간합을 여러차례 구하는 중간에 배열의 값이 바뀌는 경우 인덱스 트리가 적합하다. 149 | 150 |

151 |
152 | 153 |
154 | 인덱스 트리에서 삽입이 일어날때의 시간복잡도는 몇인가요? 155 |

156 | 157 | 수행시간은 O(logN)이다. 158 | 159 |

160 |
161 | 162 |
163 | 힙이란 무엇일까요? 164 |

165 | 166 | 힙은 최댓값 및 최솟값을 찾아내는 연산을 빠르게 하기 위해 고안된 완전이진트리를 기본으로 한 자료구조로서 다음과 같은 힙 속성을 만족한다. 167 | A가 B의 부모노드 이면, A의 키값과 B의 키값 사이에는 대소관계가 성립한다. 168 | 최대 힙의 경우 `A > B`를 만족하고, 169 | 최소 힙의 경우 `A < B`를 만족한다. 170 | 171 | 이렇게 힙은 부모와 자식노드 간의 대소관계를 만족하는 `느슨한 정렬 상태`를 가진 자료구조이다. 172 | 173 |

174 |
175 | 176 |
177 | 그림의 힙 구조에서 삭제연산이 일어났을 때 힙의 변화를 서술하세요. 178 |

179 | 180 | 스크린샷 2021-06-01 오전 11 47 16 181 | 182 | 1. 루트 노드 값을 삭제한다. (44 삭제) 183 | 2. 가장 마지막 리프노드를 루트 노드로 이동한다. (14가 루트 노드로 이동) 184 | 3. Heapify 진행 185 | > Heapify란 루트노드부터 시작하여 힙의 구조를 만족할 때까지 부모/자식 노드 간 Swap연산을 하며 밑으로 내려가는 연산을 의미한다. 186 | 187 | a. 현재 노드의 자식노드가 현재 노드보다 클 경우 SWAP한다. (14<->42) (14<->33) 188 | 189 | ㅋㅋ 190 | 191 |

192 |
193 | -------------------------------------------------------------------------------- /contents/data-structure/advanced.md: -------------------------------------------------------------------------------- 1 | # 응용 자료 구조 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim), [정희재](https://github.com/Hee-Jae) 4 | 5 |
6 | Table of Contents 7 | 8 | - [Deque (덱)](#deque-덱) 9 | - [세그먼트 트리 (Indexed Tree / Segment Tree)](#세그먼트-트리-indexed-tree--segment-tree) 10 | - [Trie (트라이)](#trie-트라이) 11 | 12 |
13 | 14 | --- 15 | 16 | ## Deque (덱) 17 | 18 | - 선형 자료구조 19 | - Double-ended Queue 20 | - 양방향에서 삽입, 삭제 연산이 모두 이루어지는 큐를 말한다. 21 | - Stack(LIFO), Queue(FIFO)처럼 활용이 가능하기 때문에 대신해서 사용할 수 있다. 22 | 23 | ### Deque 용어 24 | 25 | ### Deque 주요 명령어 26 | 27 | ### Deque 구현 28 | 29 | ### Deque 시간 복잡도 & 공간 복잡도 30 | 31 | ### Deque 활용 32 | 33 | --- 34 | 35 | ## 세그먼트 트리 (Indexed Tree / Segment Tree) 36 | 37 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 38 | 39 | - 작성자 정희재 | [Segment Tree](./materials/세그먼트트리.pdf) 40 | 41 | --- 42 | 43 | ## Trie (트라이) 44 | 45 | Trie는 **문자열을 빠르게 검색할 수 있는 자료 구조**로, **단어 사전**과 같은 개념이라 볼 수 있다. 46 | 47 | - 비선형 자료구조 (Tree의 응용) 48 | - Prefix Tree, Digital Search Tree, Retrieval Tree 라고도 부름 49 | - 문자열을 저장하고 효율적으로 탐색하기 위한 트리 형태의 자료구조 50 | - K진 트리 구조 51 | - 단어 사전을 트라이로 생성, 그 후 찾을 단어를 트라이를 사용해 검색 52 | - 트라이의 root 노드는 항상 빈 문자열 53 | 54 | ![Trie](https://user-images.githubusercontent.com/22045163/91132291-1c4f9480-e6e8-11ea-831b-e063f1a145e0.jpg) 55 | 56 | > 위 그림을 살펴보면, 트리의 깊이에 따라 단어를 1글자, 2글자, 3글자씩 저장한 것을 볼 수 있다. (root 노드는 빈 문자열) 57 | > 58 | > - 'tea' 찾기 : 트리를 따라 가서 t, e, a 를 찾는다. 59 | > - 'tee' 찾기 : 트리를 따라 갔을 때 t, e 다음 e가 없기 때문에 없는 글자이다. 60 | > - 'te' 찾기 : t, e까지는 있지만 e가 단어의 끝이 아니므로 없는 글자이다. (즉 트라이에는 단어의 끝을 알리는 flag가 필요하다.) 61 | 62 | ### Trie 구현 63 | 64 | 예제 코드는 알파벳 소문자만 저장하는 트라이라고 생각하고 구현해보겠다. 65 | 66 | #### Trie Node 설계 67 | 68 | 트라이 노드에 필요한 정보는 **자식 노드에 대한 정보** `children`, **현재 노드가 단어의 끝인지 여부** `isEnd` 이 두 가지이다. 구현의 편의를 위해 `getChild`, `hasChild` 메서드도 추가해준다. 69 | 70 | ```java 71 | class TrieNode { 72 | TrieNode[] children = new TrieNode[26]; // 알파벳 소문자만 73 | boolean isEnd; 74 | 75 | TrieNode getChild(char c) { 76 | return children[c - 'a']; 77 | } 78 | 79 | boolean hasChild(char c) { 80 | return children[c - 'a'] != null; 81 | } 82 | } 83 | ``` 84 | 85 | #### Trie 생성하기 (단어 사전 만들기) 86 | 87 | 단어 사전의 단어를 트라이에 하나씩 삽입한다고 생각해보자. 88 | 89 | 1. 루트 노드부터 시작하여 단어의 첫 글자부터 트라이를 탐색한다. 90 | 2. 현재 노드의 자식 노드 중 현재 탐색 중인 문자가 **있다면**, 그 자식 노드로 이동한다. 91 | 3. 현재 노드의 자식 노드 중 현재 탐색 중인 문자가 **없다면**, 새로운 자식 노드를 추가한다. 92 | 4. 단어의 마지막 글자까지 왔다면, isEnd를 true로 해주면 단어에 대한 정보가 트라이에 저장된다. 93 | 94 | #### Trie를 이용하여 검색하기 95 | 96 | 1. 루트 노드부터 시작하여 단어의 첫 글자부터 트라이를 탐색한다. 97 | 2. 현재 노드의 자식 노드 중 현재 탐색 중인 문자가 **있다면**, 그 자식 노드로 이동한다. 98 | 3. 현재 노드의 자식 노드 중 현재 탐색 중인 문자가 **없다면**, `return false` 99 | 4. 단어의 마지막 글자까지 왔는데, isEnd가 false라면 `return false` 100 | 5. 단어의 마지막 글자까지 왔고, isEnd가 true라면 `return true` 101 | 102 | 트라이 생성, 검색에 관한 Trie 클래스를 구현해보면 다음과 같다. 103 | 104 | ```java 105 | class Trie { 106 | TrieNode root = new TrieNode(); // 루트 노드 생성 107 | 108 | void insert(String word) { 109 | TrieNode current = root; 110 | for (int i = 0; i < word.length(); i++) { // 단어의 첫 글자 ~ 끝 글자까지 탐색 111 | char c = word.charAt(i); 112 | if (!current.hasChild(c)) { // 해당 문자에 대한 자식 노드 있는지 검색 후 그 자식 노드로 이동 113 | current.children[c - 'a'] = new TrieNode(); 114 | } 115 | current = current.getChild(c); 116 | } 117 | current.isEnd = true; // 끝 글자임을 알리는 플래그 적용 118 | } 119 | 120 | boolean checkWord(String word) { 121 | TrieNode current = root; 122 | for (int i = 0; i < word.length(); i++) { // 단어의 첫 글자 ~ 끝 글자까지 탐색 123 | char c = word.charAt(i); 124 | if (current.hasChild(c)) { // 해당 문자에 대한 자식 노드 있다면 그 자식 노드로 이동 125 | current = current.getChild(c); 126 | } else { // 해당 문자에 대한 자식 노드 없으면 return false 127 | return false; 128 | } 129 | } 130 | return current.isEnd; // 끝까지 왔는데 isEnd라면 true, 아니면 false 131 | } 132 | } 133 | ``` 134 | 135 | 예제 코드 바로 가기 ▶️ [TrieExample.java](./code/Trie/TrieExample.java) 136 | 137 | ### Trie 시간 복잡도 138 | 139 | 제일 긴 단어의 길이를 M, 총 단어들의 수를 N이라고 할 때, 140 | 141 | - **트라이 생성** 시 시간 복잡도 : **O(N\*M)** 142 | 단어 하나를 삽입하는데 가장 긴 단어의 길이 M 만큼 걸리므로 O(M)이고, 이를 N개 넣으므로 O(N\*M)이다. 143 | - **단어 검색** 시 시간 복잡도 : **O(M)** 144 | 가장 긴 문자열의 길이만큼 걸리므로 O(M)이다. 145 | 146 | 단어를 하나씩 비교하며 탐색하는 것보다 **시간적으로 훨씬 효율적**이지만, 147 | 각 노드에서 그 자식 노드에 대한 정보를 배열로 가지고 있고, 그 노드들의 개수를 생각해봤을 때 **공간 복잡도 측면에서는 비효율적**이다. 148 | 149 | ### Trie 활용 150 | 151 | - 문자열 탐색 152 | - 검색어 자동 완성 153 | - 사전 찾기 154 | -------------------------------------------------------------------------------- /contents/data-structure/basic.md: -------------------------------------------------------------------------------- 1 | # 기본 자료 구조 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 | 자료구조(Data Structure)란 **자료에 효율적으로 접근하고 수정할 수 있도록 데이터를 구성하고 저장하는 방법**을 이야기한다. 6 | 자료구조는 저장되는 데이터의 형태에 따라 **선형 자료구조**와 **비선형 자료구조**로 구분되며, 7 | 선형 자료구조는 데이터가 일렬로 나열되어 있고 비선형 자료구조는 데이터가 특정한 형태를 띄고 있다. 8 | 선형 자료구조의 종류에는 array, linked list, stack, queue 등이 있으며 비선형 자료구조로는 tree, graph 등이 있다. 9 | 10 |
11 | Table of Contents 12 | 13 | - [Array (배열)](#array-배열) 14 | - [Linked List (연결 리스트)](#linked-list-연결-리스트) 15 | - [Stack (스택)](#stack-스택) 16 | - [Queue (큐)](#queue-큐) 17 | - [Tree (트리)](#tree-트리) 18 | - [Binary Tree (이진 트리)](#binary-tree-이진-트리) 19 | - [Graph (그래프)](#graph-그래프) 20 | 21 |
22 | 23 | --- 24 | 25 | ## Array (배열) 26 | 27 | 동일한 자료형의 데이터를 일렬로 나열한 자료구조이다. 28 | 29 | - 선형 자료구조 30 | - 데이터 접근이 용이하다. (인덱스로 접근 - Random Access가 가능) 31 | - 데이터 삽입/삭제가 어렵다. (Shift 해줘야 함) 32 | - 구조가 간단하여 프로그램 작성이 쉽다. 33 | 34 | ### Array 구현 35 | 36 | #### Java 37 | 38 | ```java 39 | /* 선언 (Declaring Arrays) */ 40 | int[] arrayOfInt; 41 | String[] arrayOfString; 42 | 43 | /* 생성 (Creating Arrays) */ 44 | arrayOfInt = new int[100]; 45 | arrayOfString = new String[10]; 46 | 47 | /* 초기화 (Initializing Arrays) */ 48 | for (int i = 0; i < arrayOfInt.length; i++) { 49 | arrayOfInt[i] = i; 50 | } 51 | arrayOfString = new String[]{"hello", "world"}; 52 | String[] name = {"Stacy", "Tracy", "Dorothy"}; 53 | ``` 54 | 55 | ### Array 시간 복잡도 & 공간 복잡도 56 | 57 | #### 시간 복잡도 58 | 59 | - 데이터 조회 : O(1) 60 | - 데이터 삽입/삭제하기 : O(n) 61 | 62 | --- 63 | 64 | ## Linked List (연결 리스트) 65 | 66 | 각 노드가 **데이터**와 **포인터**를 가지고 일렬로 연결되어 있는 방식이다. 67 | 68 | - 선형 자료구조 69 | - 데이터의 접근이 느리다. (링크를 타고 가서 찾아야 한다.) 70 | - 데이터의 삽입/삭제 연산이 용이하다. 71 | - 포인터를 위한 추가 공간이 필요하다. 72 | 73 | ### Linked List 구현 74 | 75 | - [Singly Linked List](./code/LinkedList/SinglyLinkedList.java) 76 | - [Doubly Linked List](./code/LinkedList/DoublyLinkedList.java) 77 | - 위 코드 실행 : [LinkedListExample.java](./code/LinkedList/LinkedListExample.java) 78 | 79 | ### Linked List 시간 복잡도 80 | 81 | - 데이터 조회 : O(n) 82 | - **맨 앞/뒤에** 데이터 삽입/삭제하기 : O(1) (SinglyLinkedList의 경우 맨 뒤의 데이터 삭제 연산은 O(n)) 83 | - **중간의 원하는 위치에** 데이터 삽입/삭제하기 : O(n) _(원하는 원소까지 데이터를 조회하는 과정이 있으므로 O(n) + O(1))_ 84 | 85 | > #### Array와 Linked List의 차이 86 | > - 데이터 접근 속도 87 | > - Array는 인덱스를 통한 Random Access를 지원하므로 시간 복잡도 O(1)로 빠르게 찾을 수 있다. 88 | > - LinkedList는 순차 접근 방식을 사용하므로 시간 복잡도 O(N)이 걸린다. 89 | > - 데이터의 삽입/삭제 속도 90 | > - Array는 데이터를 중간이나 맨 앞에 삽입/삭제하는 경우 shift가 필요하므로 데이터가 많을수록 비효율적이다. 91 | > - LinkedList는 중간 삽입/삭제는 똑같이 O(N)의 시간 복잡도를 갖지만, 맨 앞 또는 뒤에 삽입할 경우 O(1)의 시간복잡도를 갖는다. 92 | > - 다만 LinkedList는 데이터 삽입/삭제마다 메모리 할당/해제가 일어나므로 시간복잡도는 빠를지라도 시스템 콜(System Call)에 있어서 Array보다 더 시간이 걸린다. 93 | > - 메모리 할당 94 | > - Array는 정적 메모리 할당이 이루어진다. (Compile time) 95 | > - LinkedList는 동적 메모리 할당이 이루어진다. (Runtime) 96 | > - Array의 경우 데이터 삽입 시 모든 공간이 다 차버렸다면 새로운 메모리 공간이 필요하지만 LinkedList는 동적으로 할당받을 수 있다. 97 | > 98 | > 데이터 삽입/삭제가 빈번하다면 LinkedList를 사용하는 것이 좋고, 데이터 접근 속도가 중요하다면 Array를 사용하는 것이 좋다. 99 | 100 | 101 | --- 102 | 103 | ## Stack (스택) 104 | 105 | - 선형 자료구조 106 | - 삽입, 삭제 연산이 한 방향에서 이루어진다. 107 | - **LIFO(Last In First Out)** : 나중에 들어간 원소가 먼저 나오는 구조이다. 108 | 109 | ### Stack 용어 110 | 111 | - `Top` : 스택에 데이터가 삽입될 위치 112 | 113 | ### Stack 주요 연산 114 | 115 | - `push` : 스택의 top에 원소 삽입 116 | - `pop` : 스택의 top에 있는 원소 삭제 및 반환 117 | - `peek` : 스택의 top에 있는 원소 반환 118 | 119 | ### Stack 구현 120 | 121 | - [Array를 통해 구현한 Stack](./code/Stack/ArrayStack.java) 122 | - [Singly Linked List를 통해 구현한 Stack](./code/Stack/LinkedStack.java) 123 | - 위 코드 실행 : [StackExample.java](./code/Stack/StackExample.java) 124 | 125 | ### Stack 시간 복잡도 & 공간 복잡도 126 | 127 | - 데이터 삽입/삭제 : O(1) 128 | - top 데이터 조회 : O(1) 129 | - 특정 데이터 조회 : O(n) 130 | 131 | ### Stack 활용 132 | 133 | - 시스템 스택(System Stack) / 실행시간 스택(Runtime stack) : 프로그램의 함수 호출과 복귀에 따른 실행 순서 관리 134 | - 인터럽트 루틴 처리 135 | - 웹 브라우저 방문 기록 관리 (뒤로가기) 136 | - 실행 취소 (undo) 137 | - 수식의 후위 표기법(Postfix Notation) 138 | - 수식의 괄호식 검사 139 | - 계산기 검사 140 | - 깊이 우선 탐색(DFS, Depth-First Search) 141 | 142 | > **프로그램의 함수 호출과 복귀에 따른 실행 순서 관리는 다음과 같은 과정을 가진다.** 143 | > 144 | > 1. 함수 호출이 발생하면 스택 프레임(stack frame)에 지역변수, 매개변수, 수행 후 복귀할 주소 등의 정보를 저장하여 시스템 스택에 삽입한다. 145 | > 2. 함수의 실행이 끝나면 시스템 스택의 top에 있는 stack frame 원소를 pop하고, frame에 저장되어 있던 복귀 주소를 확인하고 복귀한다. 146 | > 3. 함수 호출 - 복귀에 따라 이 과정을 반복하고, 전체 프로그램 수행이 종료되면 시스템 스택은 공백 스택이 된다. 147 | > 148 | > 함수 호출은 가장 마지막에 호출된 함수가 가장 먼저 실행을 완료하고 복귀하는 후입선출 구조이기 때문에, 스택을 이용해 관리한다. 149 | 150 | --- 151 | 152 | ## Queue (큐) 153 | 154 | - 선형 자료구조 155 | - 한 방향에서는 삽입 연산이, 반대 방향에서는 삭제 연산이 이루어진다. 156 | - **FIFO(First In First Out)** : 먼저 들어간 원소가 먼저 나오는 구조이다. 157 | 158 | ### Queue 용어 159 | 160 | - `Front` / `Head` : 큐에서 데이터가 삭제될 위치 161 | - `Rear` / `Tail` : 큐에서 마지막 데이터가 삽입된 위치 162 | 163 | ### Queue 주요 연산 164 | 165 | - `enQueue` : 큐의 rear에 원소 삽입 166 | - `deQueue` : 큐의 front에 있는 원소 삭제 및 반환 167 | 168 | ### Queue 구현 169 | 170 | - [Array를 통해 구현한 Queue](./code/Queue/ArrayQueue.java) 171 | - [Singly Linked List를 통해 구현한 Queue](./code/Queue/LinkedQueue.java) 172 | - 위 코드 실행 : [QueueExample.java](./code/Queue/QueueExample.java) 173 | 174 | > Java에서 API로 Queue를 사용할 때, java.util.Queue는 인터페이스이며, 그 구현체로 java.util.LinkedList를 사용한다. 175 | > 따라서 `Queue queue = new LinkedList()`로 선언해야 한다. 176 | 177 | ### Queue 시간 복잡도 & 공간 복잡도 178 | 179 | - 데이터 삽입/삭제 : O(1) 180 | - front 데이터 조회 : O(1) 181 | - 특정 데이터 조회 : O(n) 182 | 183 | ### Queue 활용 184 | 185 | - 프로세스 레디 큐 186 | - 스케쥴링 187 | - 캐시(Cache) 구현 188 | - 네트워크 패킷 전송시 필요한 버퍼 대기 큐 189 | - javascript의 Event Loop 관리 (비동기 처리) 190 | - 키보드 버퍼 191 | - 프린터의 출력 처리 192 | - 너비 우선 탐색(BFS, Breadth-First Search) 193 | 194 | --- 195 | 196 | ## Tree (트리) 197 | 198 | 자료들 사이의 계층적 관계를 나타내는데 사용하는 자료구조로 부모-자식 관계로 표현된다. 199 | 200 | - 비선형 자료구조 201 | - 트리는 다음의 조건을 만족한다. 202 | - 루트 노드(root node)가 존재한다. _(→ 트리는 반드시 1개 이상의 노드를 가진다.)_ 203 | - 트리의 부분 트리(sub tree) 또한 트리 구조를 따른다. 204 | 205 | ### Tree 용어 206 | 207 | - **루트 노드 (Root Node)** : 트리의 최상위 노드. unique 208 | - **부모 노드 (Parent Node)** : 부모-자식 관계에서 상위 계층의 노드 209 | - **자식 노드 (Child Node)** : 부모-자식 관계에서 하위 계층의 노드 210 | - **형제 노드** : 부모가 동일한 노드 211 | - **조상 노드** : 해당 노드의 부모 노드 ~ 루트 노드까지 가는 경로에 존재하는 모든 노드들 212 | - **후손 노드** : 해당 노드를 루트로 하는 부분 트리(sub tree)에 있는 모든 노드들 213 | - **내부 노드 (internal/nonterminal node)** : 자식이 있는 노드 214 | - **외부 노드 (단말 노드, 잎새 노드, leaf/external/terminal node)** : 자식이 없는 노드 215 | - **깊이 (Depth)** : 루트 노드에서 해당 노드까지 도달하는데 사용하는 간선의 개수 216 | - 루트 노드의 깊이는 0 217 | - **레벨 (Level)** : 노드의 깊이(depth) + 1 218 | - **높이 (Height)** : 루트 노드에서 해당 노드까지 도달하는데 지나간 정점의 개수 219 | - 트리의 높이 : 해당 트리 내 모든 노드의 높이 중 최댓값 220 | - **노드의 차수 (Degree)** : 노드의 자식 수 221 | - 트리의 차수 : 해당 트리 내 모든 노드의 차수 중 최댓값 222 | 223 | ### Tree 구현 224 | 225 | - [List를 사용해 구현한 Tree (Typescript로 작성됨)](./code/Tree/Tree.ts) 226 | 227 | ### Tree 시간 복잡도 & 공간 복잡도 228 | 229 | - 노드 삽입: O(1) 230 | - 노드 삭제: O(1) 231 | - 노드 검색: O(N) 232 | 233 | > 노드 삭제의 경우, 언어와 구현에 따라 시간복잡도가 달라질 수 있음. 234 | > 자바스크립트의 경우 가비지 콜렉션에 의해 참조를 삭제하는 방식으로 O(1)에 구현할 수 있음. 235 | 236 | ### Tree 활용 237 | 238 | - HTML DOM 트리 239 | - 파일 시스템 240 | - DBMS 241 | - 검색 엔진 242 | - 트라이 알고리즘 243 | 244 | --- 245 | 246 | ## Binary Tree (이진 트리) 247 | 248 | 트리의 차수가 2 이하인 트리이다. 249 | 250 | - 비선형 자료구조 251 | - 자식이 최대 2개이기 때문에 자식을 왼쪽 자식과 오른쪽 자식으로 구분한다. 252 | - 높이가 `N`인 이진 트리의 최대 노드 개수는 ![formula](https://render.githubusercontent.com/render/math?math=2^{N}-1)개 이다. 253 | 254 | ### Binary Tree의 종류 255 | 256 | - **정 이진 트리 (Fully Binary Tree)** : 모든 노드의 차수가 0 또는 2인 이진트리 257 | - **포화 이진 트리 (Perfect Binary Tree)** : 정 이진 트리에서 모든 외부 노드(leaf node)의 깊이가 같은 이진 트리 258 | - 높이가 `H`인 포화 이진 트리의 노드 개수는 ![formula](https://render.githubusercontent.com/render/math?math=2^{H}-1)개 이다. 259 | - 반대로 노드의 개수가 `N`개인 포화 이진 트리의 높이는 ![formula]()개 이다. 260 | - 깊이가 `D`인 포화 이진 트리의 외부 노드(leaf node) 개수는 ![formula](https://render.githubusercontent.com/render/math?math=2^{D})개 이다. 261 | - **완전 이진 트리 (Complete Binary Tree)** : 마지막 레벨은 노드가 왼쪽에 몰려있고, 마지막 레벨을 제외하면 포화 이진 트리 구조를 띄고 있는 이진 트리 262 | - **사향 이진 트리 (Skewed Binary Tree)** : linked list처럼 한 줄로 연결되어 있는 형태의 이진 트리 263 | 264 | ### Binary Tree 구현 265 | 266 | ### Binary Tree 시간 복잡도 & 공간 복잡도 267 | 268 | ### Binary Tree 활용 269 | 270 | --- 271 | 272 | ## Graph (그래프) 273 | 274 | ## 그래프 정의 275 | 276 | 현실세계의 사물이나 개념 간의 **연결 관계**를 수학적 모델로 단순화하여 표현한 것 277 | 278 | - V : 정점 (Vertex / Node) 279 | - E : 간선 (Edge / Link / Arc) 280 | - 그래프 G = (V, E) 281 | 282 | ## 그래프 용어 283 | 284 | 1. **정점, 노드 (Vertex, Node)** 285 | 2. **간선 (Edge)** 286 | - 무향 간선 (Undirected Edge) : 방향이 존재하지 않는 간선, 양방향 287 | - 유향 간선 (Directed Edge) : 방향이 존재하는 간선 288 | 3. **인접 (Adjacent)** : (정점 관점) 두 정점 A, B 사이에 간선이 존재한다면 A, B는 인접한다. 289 | 4. **부속 (Incident)** : (간선 관점) 두 정점 A, B 사이에 간선 e가 존재한다면 간선 e는 정점 A, B에 부속한다. 290 | 5. **차수 (Degree)** : 한 정점에 연결된 간선의 수 291 | - (방향 그래프) in-degree : 정점에 들어오는 간선의 수, out-degree : 나가는 간선의 수 292 | 6. **자기 간선과 다중 간선** 293 | - 자기 간선 (Self-loop) : 자신으로 다시 돌아오는 간선 294 | - 다중 간선 (Multiple / Parallel edges) : 두 개 이상의 간선이 똑같은 두 정점에 부속할 때 295 | 7. **경로 (Path)** : 정점 + 간선이 교대로 구성된 sequence 296 | - 단순 경로 (Simple Path) : 같은 정점을 두 번 이상 가지 않는 경로 297 | 8. **회로 (Cycle)** : A 정점에서 출발했을 때 다시 A 정점으로 돌아오는 경로 298 | - 단순 회로 (Simple Cycle) : 같은 정점을 두 번 이상 가지 않는 싸이클 299 | 9. **연결됨 (Connected)** : 정점 A에서 정점 B로의 경로가 존재할 때 A와 B는 연결되어 있다. 300 | 301 | ## 그래프 종류 302 | 303 | 1. **무향 그래프 (Undirected Graph)** : 무방향 간선으로 이루어진 그래프 304 | 2. **유향 그래프 (Directed Graph)** : 방향 간선으로 이루어진 그래프 305 | 3. **가중치 그래프 (Weighted Graph)** : 가중치(비용)를 갖는 간선들로 이루어진 그래프 306 | 4. **정규 그래프 (Regular Graph)** : 모든 정점이 동일한 차수를 가지는 그래프 307 | 5. **완전 그래프 (Complete Graph)** : 모든 정점이 서로 인접해있는 그래프, 완전 그래프는 정규 그래프 308 | 6. **연결 그래프 (Connected Graph)** : 모든 정점이 연결되어 있어서 모든 정점끼리 경로가 존재하는 그래프 309 | 7. **부분 그래프** : 어떤 그래프의 부분 부분 310 | 8. **트리 그래프** : 싸이클을 가지지 않는 연결 그래프, 모든 정점에 대해서 경로가 정확히 1개 존재한다. 311 | 312 | ## 그래프 표현 313 | 314 | 그래프의 표현 방식에는 **간선 리스트, 인접 행렬, 인접 리스트** 3가지 방식이 있다. 315 | 316 | > 정점 개수 : V개, 간선 개수 : E개 317 | 318 | ### 간선 리스트 (Edge List) 319 | 320 | - E x 2 (or E x 3) 이차원 배열 A에 정보를 저장한다. 321 | - 두 정점 x, y 를 연결하는 간선 k에 대해서 A[k][0] = x, A[k][1] = y 322 | - 가중치 그래프의 경우 A[k][2] 에 가중치 정보를 저장한다. 323 | 324 | ![간선리스트](./img/그래프.001.jpeg) 325 | 326 | 327 | ### 인접 행렬 (Adjacency Matrix) 328 | 329 | - V x V 이차원 배열 A에 정보를 저장한다. 330 | - Vi, Vj를 연결하는 간선이 존재한다면 A[i][j] = 1, 존재하지 않는다면 A[i][j] = 0 331 | - 가중치 그래프의 경우 1 대신 가중치 정보를 저장한다. 332 | 333 | > 메모리 복잡도가 V2 이기 때문에 V의 값이 클 경우 쓰지 않는 것이 좋다. 334 | > 100 이하의 값일 때 사용하는 것이 좋다. 335 | 336 | ![인접행렬](./img/그래프.002.jpeg) 337 | 338 | ### 인접 리스트 (Adjacent List) 339 | 340 | - V 개의 Linked List로 그래프 정보를 저장한다. 341 | - 가중치 그래프의 경우 (정점 정보, 가중치 정보)를 함께 저장한다. (C++ : pair, Java : class) 342 | 343 | ![인접리스트](./img/그래프.003.jpeg) 344 | 345 | ### 그래프 표현 방식 비교 346 | 347 | > 정점 개수 : V개, 간선 개수 : E개 348 | 349 | | | 간선 리스트 | 인접 행렬 | 인접 리스트 | 350 | | :---: | :------: | :-------------: | :------------: | 351 | | 공간 | E | V2 | V + E | 352 | | 정점 Va 의 부속 간선 | E | V | Va 차수 | 353 | | 정점 Va, Vb 의 인접 여부 | E | 1 | min(Va 차수, Vb 차수) | 354 | | 정점 삽입 | 1 | V2 | 1 | 355 | | 간선 삽입 | 1 | 1 | 1 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /contents/data-structure/code/LinkedList/DoublyLinkedList.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | public class DoublyLinkedList implements ILinkedList { 4 | private Node header; 5 | private Node trailer; 6 | private int size; 7 | 8 | public DoublyLinkedList() { 9 | header = new Node<>(null, null, null); 10 | trailer = new Node<>(null, header, null); 11 | header.setNext(trailer); 12 | size = 0; 13 | } 14 | 15 | @Override 16 | public int size() { 17 | return size; 18 | } 19 | 20 | @Override 21 | public boolean isEmpty() { 22 | return size == 0; 23 | } 24 | 25 | @Override 26 | public E first() { 27 | if (isEmpty()) return null; 28 | return header.getNext().getElement(); 29 | } 30 | 31 | @Override 32 | public E last() { 33 | if (isEmpty()) return null; 34 | return trailer.getPrev().getElement(); 35 | } 36 | 37 | @Override 38 | public void addFirst(E e) { 39 | addBetween(e, header, header.getNext()); 40 | } 41 | 42 | @Override 43 | public void addLast(E e) { 44 | addBetween(e, trailer.getPrev(), trailer); 45 | } 46 | 47 | @Override 48 | public E removeFirst() { 49 | if (isEmpty()) return null; 50 | return remove(header.getNext()); 51 | } 52 | 53 | @Override 54 | public E removeLast() { 55 | if (isEmpty()) return null; 56 | return remove(trailer.getPrev()); 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | if (isEmpty()) return "[]"; 62 | 63 | StringBuilder sb = new StringBuilder("["); 64 | DoublyLinkedList.Node current = header.getNext(); 65 | while (current.getNext() != trailer) { 66 | sb.append(current.getElement()).append(", "); 67 | current = current.getNext(); 68 | } 69 | sb.append(current.getElement()).append("]"); 70 | return sb.toString(); 71 | } 72 | 73 | private void addBetween(E e, Node predecessor, Node successor) { 74 | Node newest = new Node<>(e, predecessor, successor); 75 | predecessor.setNext(newest); 76 | successor.setPrev(newest); 77 | size ++; 78 | } 79 | 80 | private E remove(Node node) { 81 | Node predecessor = node.getPrev(); 82 | Node successor = node.getNext(); 83 | predecessor.setNext(successor); 84 | successor.setPrev(predecessor); 85 | size --; 86 | return node.getElement(); 87 | } 88 | 89 | private static class Node { 90 | private E element; 91 | private Node prev; 92 | private Node next; 93 | 94 | public Node() { 95 | this(null, null, null); 96 | } 97 | 98 | public Node(E element, Node prev, Node next) { 99 | this.element = element; 100 | this.prev = prev; 101 | this.next = next; 102 | } 103 | 104 | public E getElement() { 105 | return element; 106 | } 107 | 108 | public void setElement(E element) { 109 | this.element = element; 110 | } 111 | 112 | public Node getPrev() { 113 | return prev; 114 | } 115 | 116 | public void setPrev(Node prev) { 117 | this.prev = prev; 118 | } 119 | 120 | public Node getNext() { 121 | return next; 122 | } 123 | 124 | public void setNext(Node next) { 125 | this.next = next; 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /contents/data-structure/code/LinkedList/ILinkedList.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | public interface ILinkedList { 4 | 5 | public int size(); 6 | public boolean isEmpty(); 7 | 8 | public E first(); 9 | public E last(); 10 | 11 | public void addFirst(E e); 12 | public void addLast(E e); 13 | 14 | public E removeFirst(); 15 | public E removeLast(); 16 | } 17 | -------------------------------------------------------------------------------- /contents/data-structure/code/LinkedList/LinkedListExample.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | public class LinkedListExample { 4 | public static void main(String[] args) { 5 | System.out.println("==================Singly Linked List=================="); 6 | ILinkedList sll = new SinglyLinkedList<>(); 7 | sll.addFirst(3); 8 | sll.addLast(10); 9 | sll.addFirst(2); 10 | sll.addLast(12); 11 | System.out.println(sll); 12 | System.out.println("size : " + sll.size()); 13 | System.out.println("first : " + sll.first()); 14 | System.out.println("last : " + sll.last()); 15 | System.out.println("removeFirst : " + sll.removeFirst()); 16 | System.out.println(sll); 17 | System.out.println("removeLast : " + sll.removeLast()); 18 | System.out.println(sll); 19 | System.out.println("removeFirst : " + sll.removeFirst()); 20 | System.out.println(sll); 21 | System.out.println("removeLast : " + sll.removeLast()); 22 | System.out.println(sll); 23 | System.out.println("size : " + sll.size()); 24 | 25 | System.out.println("==================Doubly Linked List=================="); 26 | ILinkedList dll = new DoublyLinkedList<>(); 27 | dll.addFirst(3); 28 | dll.addLast(10); 29 | dll.addFirst(2); 30 | dll.addLast(12); 31 | System.out.println(dll); 32 | System.out.println("size : " + dll.size()); 33 | System.out.println("first : " + dll.first()); 34 | System.out.println("last : " + dll.last()); 35 | System.out.println("removeFirst : " + dll.removeFirst()); 36 | System.out.println(dll); 37 | System.out.println("removeLast : " + dll.removeLast()); 38 | System.out.println(dll); 39 | System.out.println("removeFirst : " + dll.removeFirst()); 40 | System.out.println(dll); 41 | System.out.println("removeLast : " + dll.removeLast()); 42 | System.out.println(dll); 43 | System.out.println("size : " + dll.size()); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /contents/data-structure/code/LinkedList/SinglyLinkedList.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | public class SinglyLinkedList implements ILinkedList { 4 | private Node head; 5 | private Node tail; 6 | private int size; 7 | 8 | public SinglyLinkedList() { 9 | head = tail = null; 10 | size = 0; 11 | } 12 | 13 | @Override 14 | public int size() { 15 | return size; 16 | } 17 | 18 | @Override 19 | public boolean isEmpty() { 20 | return size == 0; 21 | } 22 | 23 | @Override 24 | public E first() { 25 | if (isEmpty()) return null; 26 | return head.getElement(); 27 | } 28 | 29 | @Override 30 | public E last() { 31 | if (isEmpty()) return null; 32 | return tail.getElement(); 33 | } 34 | 35 | @Override 36 | public void addFirst(E e) { 37 | head = new Node<>(e, head); 38 | if (isEmpty()) tail = head; 39 | size ++; 40 | } 41 | 42 | @Override 43 | public void addLast(E e) { 44 | Node newest = new Node<>(e, null); 45 | if (isEmpty()) head = newest; 46 | else tail.setNext(newest); 47 | tail = newest; 48 | size ++; 49 | } 50 | 51 | @Override 52 | public E removeFirst() { 53 | if (isEmpty()) return null; 54 | E target = head.getElement(); 55 | head = head.getNext(); 56 | size --; 57 | if (isEmpty()) tail = null; 58 | return target; 59 | } 60 | 61 | @Override 62 | public E removeLast() { 63 | if (isEmpty()) return null; 64 | E target; 65 | if (head == tail) { 66 | target = head.getElement(); 67 | head = tail = null; 68 | } else { 69 | Node current = head; 70 | while (current.getNext() != tail) { 71 | current = current.getNext(); 72 | } 73 | target = tail.getElement(); 74 | tail = current; 75 | tail.setNext(null); 76 | } 77 | size --; 78 | return target; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | if (isEmpty()) return "[]"; 84 | 85 | StringBuilder sb = new StringBuilder("["); 86 | Node current = head; 87 | while (current.getNext() != null) { 88 | sb.append(current.getElement()).append(", "); 89 | current = current.getNext(); 90 | } 91 | sb.append(current.getElement()).append("]"); 92 | return sb.toString(); 93 | } 94 | 95 | private static class Node { 96 | private E element; 97 | private Node next; 98 | 99 | public Node() { 100 | this(null, null); 101 | } 102 | 103 | public Node(E element, Node next) { 104 | this.element = element; 105 | this.next = next; 106 | } 107 | 108 | public E getElement() { 109 | return element; 110 | } 111 | 112 | public void setElement(E element) { 113 | this.element = element; 114 | } 115 | 116 | public Node getNext() { 117 | return next; 118 | } 119 | 120 | public void setNext(Node next) { 121 | this.next = next; 122 | } 123 | } 124 | } 125 | 126 | -------------------------------------------------------------------------------- /contents/data-structure/code/Queue/ArrayQueue.java: -------------------------------------------------------------------------------- 1 | package Queue; 2 | 3 | public class ArrayQueue implements IQueue { 4 | private static final int CAPACITY = 1000; 5 | private E[] data; 6 | private int front = 0; 7 | private int size = 0; 8 | 9 | public ArrayQueue() { 10 | this(CAPACITY); 11 | } 12 | 13 | public ArrayQueue(int capacity) { 14 | data = (E[]) new Object[capacity]; 15 | } 16 | 17 | @Override 18 | public int size() { 19 | return size; 20 | } 21 | 22 | @Override 23 | public boolean isEmpty() { 24 | return size == 0; 25 | } 26 | 27 | @Override 28 | public void enqueue(E e) { 29 | if (size == data.length) throw new IllegalStateException("Queue is full"); 30 | int avail = (front + size) % data.length; 31 | data[avail] = e; 32 | size ++; 33 | } 34 | 35 | @Override 36 | public E dequeue() { 37 | if (isEmpty()) return null; 38 | E target = data[front]; 39 | data[front] = null; // dereference to help garbage collection 40 | front = (front + 1) % data.length; 41 | size --; 42 | return target; 43 | } 44 | 45 | @Override 46 | public E peek() { 47 | if (isEmpty()) return null; 48 | return data[front]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contents/data-structure/code/Queue/IQueue.java: -------------------------------------------------------------------------------- 1 | package Queue; 2 | 3 | public interface IQueue { 4 | public int size(); 5 | public boolean isEmpty(); 6 | 7 | public void enqueue(E e); 8 | public E dequeue(); 9 | public E peek(); 10 | } 11 | -------------------------------------------------------------------------------- /contents/data-structure/code/Queue/LinkedQueue.java: -------------------------------------------------------------------------------- 1 | package Queue; 2 | 3 | import LinkedList.SinglyLinkedList; 4 | 5 | public class LinkedQueue implements IQueue { 6 | private SinglyLinkedList list = new SinglyLinkedList<>(); 7 | 8 | public LinkedQueue() {} 9 | 10 | @Override 11 | public int size() { 12 | return list.size(); 13 | } 14 | 15 | @Override 16 | public boolean isEmpty() { 17 | return list.isEmpty(); 18 | } 19 | 20 | @Override 21 | public void enqueue(E e) { 22 | list.addLast(e); 23 | } 24 | 25 | @Override 26 | public E dequeue() { 27 | return list.removeFirst(); 28 | } 29 | 30 | @Override 31 | public E peek() { 32 | return list.first(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contents/data-structure/code/Queue/QueueExample.java: -------------------------------------------------------------------------------- 1 | package Queue; 2 | 3 | public class QueueExample { 4 | 5 | public static void main(String[] args) { 6 | { 7 | ArrayQueue queue = new ArrayQueue<>(3); 8 | 9 | queue.enqueue(5); 10 | queue.enqueue(3); 11 | 12 | System.out.println(queue.size()); // 2 13 | System.out.println(queue.dequeue()); // 5 14 | System.out.println(queue.isEmpty()); // false 15 | System.out.println(queue.dequeue()); // 3 16 | System.out.println(queue.isEmpty()); // true 17 | System.out.println(queue.dequeue()); // null 18 | 19 | queue.enqueue(7); 20 | queue.enqueue(9); 21 | 22 | System.out.println(queue.peek()); // 7 23 | 24 | queue.enqueue(4); 25 | 26 | System.out.println(queue.dequeue()); // 7 27 | } 28 | { 29 | LinkedQueue queue = new LinkedQueue<>(); 30 | 31 | queue.enqueue(5); 32 | queue.enqueue(3); 33 | 34 | System.out.println(queue.size()); // 2 35 | System.out.println(queue.dequeue()); // 5 36 | System.out.println(queue.isEmpty()); // false 37 | System.out.println(queue.dequeue()); // 3 38 | System.out.println(queue.isEmpty()); // true 39 | System.out.println(queue.dequeue()); // null 40 | 41 | queue.enqueue(7); 42 | queue.enqueue(9); 43 | 44 | System.out.println(queue.peek()); // 7 45 | 46 | queue.enqueue(4); 47 | 48 | System.out.println(queue.dequeue()); // 7 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contents/data-structure/code/Stack/ArrayStack.java: -------------------------------------------------------------------------------- 1 | package Stack; 2 | 3 | public class ArrayStack implements IStack { 4 | private static final int CAPACITY = 1000; 5 | private E[] data; 6 | private int top = -1; 7 | 8 | public ArrayStack() { 9 | this(CAPACITY); 10 | } 11 | 12 | public ArrayStack(int capacity) { 13 | data = (E[]) new Object[capacity]; 14 | } 15 | 16 | @Override 17 | public int size() { 18 | return top + 1; 19 | } 20 | 21 | @Override 22 | public boolean isEmpty() { 23 | return top == -1; 24 | } 25 | 26 | @Override 27 | public void push(E e) throws IllegalStateException { 28 | if (size() == data.length) throw new IllegalStateException("Stack is full"); 29 | data[++top] = e; 30 | } 31 | 32 | @Override 33 | public E pop() { 34 | if (isEmpty()) return null; 35 | E target = data[top]; 36 | data[top] = null; // dereference to help garbage collection 37 | top--; 38 | return target; 39 | } 40 | 41 | @Override 42 | public E peek() { 43 | if (isEmpty()) return null; 44 | return data[top]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contents/data-structure/code/Stack/IStack.java: -------------------------------------------------------------------------------- 1 | package Stack; 2 | 3 | public interface IStack { 4 | public int size(); 5 | public boolean isEmpty(); 6 | 7 | public void push(E e); 8 | public E pop(); 9 | public E peek(); 10 | } 11 | -------------------------------------------------------------------------------- /contents/data-structure/code/Stack/LinkedStack.java: -------------------------------------------------------------------------------- 1 | package Stack; 2 | 3 | import LinkedList.SinglyLinkedList; 4 | 5 | public class LinkedStack implements IStack { 6 | private SinglyLinkedList list = new SinglyLinkedList<>(); 7 | 8 | public LinkedStack() {} 9 | 10 | @Override 11 | public int size() { 12 | return list.size(); 13 | } 14 | 15 | @Override 16 | public boolean isEmpty() { 17 | return list.isEmpty(); 18 | } 19 | 20 | @Override 21 | public void push(E e) { 22 | list.addFirst(e); 23 | } 24 | 25 | @Override 26 | public E pop() { 27 | return list.removeFirst(); 28 | } 29 | 30 | @Override 31 | public E peek() { 32 | return list.first(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contents/data-structure/code/Stack/StackExample.java: -------------------------------------------------------------------------------- 1 | package Stack; 2 | 3 | public class StackExample { 4 | 5 | public static void main(String[] args) { 6 | { 7 | ArrayStack stack = new ArrayStack<>(); 8 | 9 | stack.push(5); 10 | stack.push(3); 11 | 12 | System.out.println(stack.size()); // 2 13 | System.out.println(stack.pop()); // 3 14 | System.out.println(stack.isEmpty()); // false 15 | System.out.println(stack.pop()); // 5 16 | System.out.println(stack.isEmpty()); // true 17 | System.out.println(stack.pop()); // null 18 | 19 | stack.push(7); 20 | stack.push(9); 21 | 22 | System.out.println(stack.peek()); // 9 23 | 24 | stack.push(4); 25 | 26 | System.out.println(stack.size()); // 3 27 | System.out.println(stack.pop()); // 4 28 | 29 | stack.push(6); 30 | stack.push(8); 31 | 32 | System.out.println(stack.pop()); // 8 33 | } 34 | { 35 | LinkedStack stack = new LinkedStack<>(); 36 | 37 | stack.push(5); 38 | stack.push(3); 39 | 40 | System.out.println(stack.size()); // 2 41 | System.out.println(stack.pop()); // 3 42 | System.out.println(stack.isEmpty()); // false 43 | System.out.println(stack.pop()); // 5 44 | System.out.println(stack.isEmpty()); // true 45 | System.out.println(stack.pop()); // null 46 | 47 | stack.push(7); 48 | stack.push(9); 49 | 50 | System.out.println(stack.peek()); // 9 51 | 52 | stack.push(4); 53 | 54 | System.out.println(stack.size()); // 3 55 | System.out.println(stack.pop()); // 4 56 | 57 | stack.push(6); 58 | stack.push(8); 59 | 60 | System.out.println(stack.pop()); // 8 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contents/data-structure/code/Tree/Tree.ts: -------------------------------------------------------------------------------- 1 | class TreeNode { 2 | parent: TreeNode | null; 3 | 4 | children: TreeNode[]; 5 | 6 | data: DataType; 7 | 8 | level: number; 9 | 10 | constructor(key: DataType, level: number, parent: TreeNode | null) { 11 | this.parent = parent; 12 | this.data = key; 13 | this.children = []; 14 | this.level = level; 15 | } 16 | 17 | addChlid(key: DataType): void { 18 | this.children.push(new TreeNode(key, this.level + 1, this)); 19 | } 20 | 21 | printChildNodes(): void { 22 | this.children.forEach((child) => console.log(child.data)); 23 | } 24 | 25 | getNode(key: DataType): TreeNode | null { 26 | if (key === this.data) return this; 27 | if (this.children.length === 0) return null; 28 | 29 | for (const child of this.children) { 30 | const res = child.getNode(key); 31 | if (res) return res; 32 | } 33 | 34 | return null; 35 | } 36 | 37 | removeNode(key: DataType): void { 38 | const target = this.getNode(key); 39 | const targetedChildren = target.parent.children; 40 | target.parent.children = targetedChildren.filter( 41 | (child) => child.data !== key 42 | ); 43 | } 44 | } 45 | 46 | const root = new TreeNode("root", 1, null); 47 | root.addChlid("Fruit"); // key 값이 Fruit인 노드 생성 후 root의 자식으로 추가 48 | root.addChlid("Celebrity"); // key 값이 Celebrity인 노드 생성 후 root의 자식으로 추가 49 | root.addChlid("Game"); // key 값이 Game인 노드 생성 후 root의 자식으로 추가 50 | root.addChlid("Netflix"); // key값이 Netflix인 노드 생성 후 root의 자식으로 추가 51 | 52 | root.getNode("Netflix").addChlid("Kingdom"); // Netflix 자식으로 Kingdom 추가 53 | root.getNode("Kingdom").addChlid("Zombie"); // Kingdom 자식으로 Zombie 추가 54 | 55 | root.getNode("Celebrity").addChlid("aespa"); // Celebrity 자식으로 aespa 추가 56 | root.getNode("Celebrity").addChlid("psy"); // Celebrity 자식으로 psy 추가 57 | root.getNode("aespa").addChlid("Winter"); // aespa 자식으로 Winter 추가 58 | 59 | root.getNode("Celebrity").printChildNodes(); // asepa psy 60 | root.removeNode("aespa"); // aespa 노드를 삭제 61 | root.getNode("Celebrity").printChildNodes(); // psy 62 | console.log(root.getNode("aespa")); // null 63 | -------------------------------------------------------------------------------- /contents/data-structure/code/Trie/TrieExample.java: -------------------------------------------------------------------------------- 1 | package Trie; 2 | 3 | public class TrieExample { 4 | 5 | public static void main(String[] args) { 6 | Trie t = new Trie(); 7 | 8 | /* insert words */ 9 | t.insert("a"); 10 | t.insert("inn"); 11 | t.insert("to"); 12 | t.insert("tea"); 13 | t.insert("ted"); 14 | t.insert("ten"); 15 | 16 | /* search words */ 17 | System.out.println(t.checkWord("inn")); 18 | System.out.println(t.checkWord("tea")); 19 | System.out.println(t.checkWord("tee")); 20 | System.out.println(t.checkWord("te")); 21 | } 22 | } 23 | 24 | class Trie { 25 | TrieNode root = new TrieNode(); 26 | 27 | void insert(String word) { 28 | TrieNode current = root; 29 | for (int i = 0; i < word.length(); i++) { 30 | char c = word.charAt(i); 31 | if (!current.hasChild(c)) { 32 | current.children[c - 'a'] = new TrieNode(); 33 | } 34 | current = current.getChild(c); 35 | } 36 | current.isEnd = true; 37 | } 38 | 39 | boolean checkWord(String word) { 40 | TrieNode current = root; 41 | for (int i = 0; i < word.length(); i++) { 42 | char c = word.charAt(i); 43 | if (current.hasChild(c)) { 44 | current = current.getChild(c); 45 | } else { 46 | return false; 47 | } 48 | } 49 | return current.isEnd; 50 | } 51 | } 52 | 53 | class TrieNode { 54 | TrieNode[] children = new TrieNode[26]; 55 | boolean isEnd; 56 | 57 | TrieNode getChild(char c) { 58 | return children[c - 'a']; 59 | } 60 | 61 | boolean hasChild(char c) { 62 | return children[c - 'a'] != null; 63 | } 64 | } -------------------------------------------------------------------------------- /contents/data-structure/img/그래프.001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/data-structure/img/그래프.001.jpeg -------------------------------------------------------------------------------- /contents/data-structure/img/그래프.002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/data-structure/img/그래프.002.jpeg -------------------------------------------------------------------------------- /contents/data-structure/img/그래프.003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/data-structure/img/그래프.003.jpeg -------------------------------------------------------------------------------- /contents/data-structure/materials/세그먼트트리.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/data-structure/materials/세그먼트트리.pdf -------------------------------------------------------------------------------- /contents/database/materials/윤가영_database_&Index.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/database/materials/윤가영_database_&Index.pdf -------------------------------------------------------------------------------- /contents/database/materials/이세명_database_NoSQL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/database/materials/이세명_database_NoSQL.pdf -------------------------------------------------------------------------------- /contents/design-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Pattern (디자인 패턴) 2 | 3 | 디자인 패턴(Design Pattern)은 **소프트웨어 설계의 효율성을 높이는 최선의 해결책** 중 하나다. 소프트웨어 최적화에 대한 관심이 높아지면서 소프트웨어 아키텍트는 객체 생성과 코드 구조, 객체 간의 상호작용 등을 반드시 설계 단계부터 생각해야 한다. 디자인 패턴을 잘 활용하면 소프트웨어 **유지보수 비용이 줄어들고 코드 재사용성이 증가하며** 쉽게 확장할 수 있는 구조가 될 것이다. 재사용할 수 있는 모듈간 독립적인 프레임워크를 제공하는 일이 현대 소프트웨어 개발의 핵심이다. 4 | 5 | ### GoF 디자인 패턴 6 | - [싱글톤 (Singleton)](singleton.md) 7 | - [싱글톤 Java 구현 방법](singleton-java.md) 8 | - [팩토리 (Factory)](factory.md) 9 | - [옵저버 (Observer)](observer.md) 10 | - [템플릿 메소드 (Template Method)](template-method.md) 11 | 12 | ### 아키텍처 패턴 13 | - [MVC (Model-View-Controller)](mvc-python.md) 14 | 15 | #### [부록] 피해야 하는 개발 습관 16 | - [안티 패턴](anti-pattern.md) 17 | 18 | --- 19 | 20 | ## 질의응답 21 | 22 | > 아직 없습니다. 23 | -------------------------------------------------------------------------------- /contents/design-pattern/adapter.md: -------------------------------------------------------------------------------- 1 | # Adapter(어댑터) 디자인 패턴 2 | 3 | 어댑터 패턴이란 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동할 수 있도록 해주는 패턴이다. 4 | 5 |

6 | 어댑터패턴 7 |

8 | 9 | 10 | 11 | ## Adapter 디자인 패턴의 장점 12 | 13 | - 관계가 없는 인터페이스를 같이 사용 가능 14 | - 프로그램을 테스트 하는데 용이 15 | - 클래스 재활용성이 높아짐 16 | 17 | ## Adapter 디자인 패턴의 종류 18 | 19 | - 객체 어댑터 패턴 - 위임을 이용한 어댑터 패턴 20 | 21 | - 클래스 어댑터 패턴 - 상속을 이용한 어댑터 패턴 22 | 23 | 상속(Inheritance)과 위임(Delegation)에서 위임에 대해 익숙하지 않을 수 있는데 위임이란 다른 클래스의 객체를 멤버 변수로 갖는 형태의 클래스이다. 24 | 25 | ## Adapter 디자인 패턴 예제 26 | 27 | ### 시나리오 28 | 29 | 원래는 이미지만 보여줄 수 있는 뷰어가 있다. 여기서 요구사항에 의해 이미지가 아닌 동영상도 보여 주고 싶다. 30 | 31 | 1. Image 클래스는 Media 인터페이스를 상속한다. 32 | 2. Video 클래스는 AdvancedMedia 인터페이스를 상속한다. 33 | 3. 현재 개발된 코드를 수정 할 수 없는 상황이다. 34 | 4. 새로운 Viewer를 만들 수 없는 상황이다. 35 | 36 | 어떤 파일들을 추가하고 main()에 어떤 코드들이 추가 되면 좋을 지 생각해보자. 37 | 38 | 39 | 40 | ### 요구사항 반영 전 41 | 42 | Main.java 43 | 44 | ```java 45 | public class Main { 46 | public static void main(String[] args) { 47 | Media media = new Image("Monalisa"); 48 | Viewer viewer = new Viewer(); 49 | viewer.view(media); 50 | } 51 | } 52 | 53 | ``` 54 | 55 | Viewer.java 56 | 57 | ```java 58 | public class Viewer { // media의 show()를 호출하는 역할 59 | public void view(Media media){ 60 | media.show(); 61 | } 62 | } 63 | ``` 64 | 65 | Media.java( Media interface ) 66 | 67 | ```java 68 | public interface Media { 69 | public void show(); 70 | } 71 | ``` 72 | 73 | Image.java 74 | 75 | ```java 76 | public class Image implements Media { 77 | public String name; 78 | 79 | public Image(String name) { 80 | this.name = name; 81 | } 82 | 83 | @Override 84 | public void show() { 85 | System.out.println("Image :" + name); 86 | } 87 | } 88 | ``` 89 | 90 | ### 요구사항 반영 후 91 | 92 | 1. Video 클래스, AdvancedMedia 인터페이스에 대한 파일을 추가한다. 93 | 2. Image와 Video는 interface가 다르기 때문에 Video를 바로 Viewer에서 사용할 수 없기 때문에 Adapter를 추가해준다. 94 | 95 | 96 | 97 | AdvancedMedia.java( AdvancedMedia interface ) 98 | 99 | ```swift 100 | public interface AdvancedMedia { 101 | public void play(); 102 | } 103 | ``` 104 | 105 | Video.java 106 | 107 | ```java 108 | public class Video implements AdvancedMedia { 109 | public String name; 110 | 111 | public Video(String name) { 112 | this.name = name; 113 | } 114 | 115 | @Override 116 | public void play() { 117 | System.out.println("Video :" + name); 118 | } 119 | } 120 | ``` 121 | 122 | #### 객체 어댑터 패턴(위임) 123 | 124 | MediaAdapter.java 125 | 126 | ```java 127 | public class MediaAdapter implements Media { 128 | public AdvancedMedia media; // 위임 129 | public MediaAdapter(AdvancedMedia media) { 130 | this.media = media; 131 | } 132 | 133 | @Override 134 | public void show() { 135 | media.play(); 136 | } 137 | } 138 | ``` 139 | 140 | Main.java 141 | 142 | ```java 143 | public class Main { 144 | public static void main(String[] args) { 145 | 146 | Media media = new Image("Monalisa"); 147 | AdvancedMedia advancedMedia = new Video("IZONE M/V"); // 위임할 객체 생성 148 | 149 | MediaAdapter mediaAdapter = new MediaAdapter(advancedMedia); // MediaAdapter에 advancedMedia 위임 150 | 151 | Viewer viewer = new Viewer(); 152 | viewer.view(media); 153 | viewer.view(mediaAdapter); 154 | } 155 | } 156 | ``` 157 | 158 | 159 | 160 | #### 클래스 어댑터 패턴 161 | 162 | MediaAdapter.java 163 | 164 | ```java 165 | public class MediaAdapter extends Video implements Media { // 상속 166 | public MediaAdapter(String name) { 167 | super(name); // 상속한 Video 클래스 init 168 | } 169 | 170 | @Override 171 | public void show() { 172 | super.play(); // 상속한 Video 클래스의 play() 실행 173 | } 174 | } 175 | ``` 176 | 177 | Main.java 178 | 179 | ```java 180 | public class Main { 181 | public static void main(String[] args) { 182 | 183 | Media media = new Image("Monalisa"); 184 | MediaAdapter mediaAdapter = new MediaAdapter("IZONE M/V"); 185 | 186 | Viewer viewer = new Viewer(); 187 | viewer.view(media); 188 | viewer.view(mediaAdapter); 189 | } 190 | } 191 | ``` 192 | 193 | #### 결과 194 | 195 | 어떤 Adapter를 적용해도 결과는 같게 나온다. 196 | 197 | ![image](https://user-images.githubusercontent.com/22047374/127747979-77aadb92-4037-4b5b-9539-63db3ae7effb.png) 198 | 199 | ### 마무리 200 | 201 | 이렇게 어댑터 패턴을 사용하면 기존 소스코드를 수정하지 않고 새로운 또는 맞추고자 하는 인터페이스에 맞춰서 동작가능하게 한다. 그렇기 때문에 소스코드가 좀 더 간결해지고 유지보수 또한 원활해진다. 202 | 203 | -------------------------------------------------------------------------------- /contents/design-pattern/anti-pattern.md: -------------------------------------------------------------------------------- 1 | # 안티 패턴 2 | 3 | > 작성자 : [정희재](https://github.com/Hee-Jae) 4 | 5 | 본 자료는 '파이썬 디자인 패턴 2/e (Chetan Giridhar)' 책을 토대로 작성되었습니다. 따라서 모든 예시 코드에는 저자를 표시하는 `__author__ = 'Chetan'` 이 포함됩니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [안티 패턴 개요](#안티-패턴-개요) 11 | - [소프트웨어 개발 안티 패턴](#소프트웨어-개발-안티-패턴) 12 | - [소프트웨어 개발 안티 패턴의 예](#소프트웨어-개발-안티-패턴의-예) 13 | - [소프트웨어 설계 안티 패턴](#소프트웨어-설계-안티-패턴) 14 | - [소프트웨어 설계 안티 패턴의 예](#소프트웨어-설계-안티-패턴의-예) 15 | 16 |
17 | 18 | --- 19 | 20 | ## 안티 패턴 개요 21 | 소프트웨어 디자인 원칙은 개발자가 설계 단계에서 지켜야 할 몇가지 원칙과 가이드라인을 제시한다. `로버트 마틴`에 의하면 잘못된 설계에는 4가지 특성이 있다. 22 | 1. **유연하지 않다** -> 재사용이 어렵다. 23 | 2. **융통성이 없다** -> 간단한 수정도 여러 부분을 손봐야 한다. 24 | 3. **취약하다** -> 수정할수록 시스템이 취약해진다. 25 | 4. **점성이 높다** -> 설계상의 결함에 대한 수정이 어려워 겉돌게 한다. 26 | 27 | 28 | 29 | ### 안티 패턴이 생기는 원인 30 | 1. 개발자의 소프트웨어 개발 방식이 미숙하다. 31 | 2. 개발자가 상황에 맞지 않는 디자인 패턴을 사용한다. 32 | 33 | ### 안티 패턴을 방지했을 때 유용한 점 34 | 1. 소프트웨어 개발 단계에서 자주 발생하는 문제를 해결할 수 있다. 35 | 2. 문제를 찾아내는 툴을 개발해 원인을 분석할 수 있다. 36 | 3. 애플리케이션과 설계를 개선할 수 있는 다양한 방법을 찾을 수 있다. 37 | 38 | --- 39 | 40 | ## 소프트웨어 개발 안티 패턴 41 | 보통 애플리케이션이나 프로젝트 개발을 시작할 때 코드 구조부터 설계한다. 제품 설계와 디자인, 사용 패턴 등을 생각해 결정한다. 42 | 개발이 진행될수록 최초의 코드 구조에서 벗어나게 되는 원인은 다음과 같다. 43 | 1. 개발이 진행되면서 개발자의 사고력이 높아진다. 44 | 2. 고객의 피드백에 따라 사용 패턴이 바뀐다. 45 | 3. 기능 추가 및 확장성을 고려해 초기에 구성한 자료 구조가 수정된다. 46 | 47 | 위와 같은 이유로 **리팩토링**을 하게 된다. **리팩토링**은 부정적인 의미로 받아들여 질 수 있지만 실제로는 자료 구조를 다시 살펴보고 확장성과 끊임없는 고객의 요구를 다시 생각해 볼 수 있는 소프트웨어 개발에 꼭 필요한 단계다. 48 | 49 | --- 50 | 51 | ## 소프트웨어 개발 안티 패턴의 예 52 | 53 | ### 스파게티 코드 54 | 가장 흔하게 접하는 안티 패턴이다. 코드가 뒤죽박죽 복잡하게 서로 엮여 있는 경우다. 소프트웨어를 충동적으로 개발하다 보면 자주 발생한다. 이런 **스파게티** 코드는 유지보수와 최적화가 어렵다. 55 | 56 | #### 스파게티 코드의 원인 57 | 1. 객체지향 프로그래밍 및 분석에 대한 이해 부족 58 | 2. 깊게 생각하지 않은 제품 설계 및 디자인 59 | 3. 대충 빨리 고치려는 성격 60 | 61 |
62 | 63 | ### 황금 망치 64 | 특정 솔루션이 다른 프로젝트에서 성공적이었다는 이유로 여러 곳에서 쓰이는 경우가 많다. 하지만 각 솔루션은 적합한 환경과 목적에서 사용해야 한다. 그럼에도 불구하고 개발자는 적합하지 않은 상황에도 한 번 사용해본 솔루션을 반복적으로 사용하는 경향이 있다. 어떤 못도 박을 수 있는 **황금 망치**다. 65 | 66 | #### 황금 망치의 원인 67 | 1. 주어진 문제와 환경에 대해서 잘 알지 못하는 외부인이 관여하는 경우 68 | 2. 전혀 다른 목적의 프로젝트에서 성공적으로 적용된 솔루션을 재사용하는 경우 69 | 3. 이전에 한 번 교육받은 솔루션을 지속적으로 사용하는 경우 70 | 71 | #### 황금 망치의 결과 72 | 1. 특정 솔루션이 모든 프로젝트에서 사용된다. 73 | 2. 소프트웨어가 제품의 기능이 아닌 개발에 사용된 기술 위주로 설명된다. 74 | 3. 개발자들이 “저 솔루션을 사용했어야 하는데.”라는 탄식을 자주한다. 75 | 4. 사용자의 요구가 충족되지 않는다. 76 | 77 |
78 | 79 | ### 용암류 80 | 프로그램이 망가질까 봐 두려워서 건드리지 못하는 죽은 코드나 쓸 수 없는 코드를 일컫는다. **용암이 굳어서 단단한 돌**이 되듯이 이런 코드는 계속 프로그램 속에 남아 자리만 차지한다. 특정 목적에 맞춰 개발한 애플리케이션의 사용 용도가 변경되는 경우에 주로 생긴다. 81 | 82 | #### 용암류의 원인 83 | 1. 테스트 코드와 에러 코드가 지나치게 많은 경우 84 | 2. 리뷰 없이 단독적으로 코드를 작성하고 인수인계 없이 다른 팀에 넘기는 경우 85 | 3. 코드를 이해하는 사람이 아무도 없는 경우 86 | 87 | #### 용암류의 특징 88 | 1. 테스트 코드의 범위가 좁다. 89 | 2. 알 수 없는 주석된 코드가 많다. 90 | 3. 쓰이지 않는 인터페이스가 생기거나 기존 코드를 우회하는 방식으로 개발한다. 91 | 92 |
93 | 94 | ### 복사-붙여넣기 프로그래밍 95 | 가장 흔한 안티 패턴이다. 숙련된 개발자는 자주 발생하는 문제에 대한 해결책으로 자신의 코드를 온라인(StackOverflow, GitHub)에 올려둔다. 그리고 다른 개발자가 이 코드를 **그대로 복사해 자신의 코드에 붙여 넣는다.** 붙여 넣은 코드가 최적화 되었는지 또는 상황에 적합한지 생각하지 않아 결국 유지보수가 어려운 불규칙한 코드가 되어버린다. 96 | 97 | #### 복사-붙여넣기의 원인 98 | 1. 코딩 및 개발이 미숙한 초보 개발자인 경우 99 | 2. 빠르게 버그를 수정하고 넘어가야 하는 경우 100 | 3. 모듈 간의 표준화 또는 구조 단일화를 위한 코드 중복이 생길 때 101 | 4. 장기적인 사고의 부재 102 | 103 | #### 복사-붙여넣기의 결과 104 | 1. 유사한 문제가 애플리케이션의 여러 부분에서 발생한다. 105 | 2. 높은 유지보수 비용과 버그 발생률 106 | 3. 코드 중복으로 인해 모듈식 코드가 줄어든다. 107 | 4. 동일한 문제가 계속 발생한다. 108 | 109 | --- 110 | 111 | ## 소프트웨어 설계 안티 패턴 112 | 소프트웨어 설계는 전체 시스템 설계에서 가장 중요한 부분이다. **시스템 설계**는 디자인과 툴, 하드웨어에 중점을 두지만 **소프트웨어 설계**는 개발 및 테스트 팀과 PM등의 관계자들이 함께 모델링한다. 기초 설계는 제품의 성공을 좌지우지한다. 113 | 114 | --- 115 | 116 | ## 소프트웨어 설계 안티 패턴의 예 117 | 118 | ### 시간 낭비 119 | 여기서 **시간 낭비**란 설계의 재사용을 의미한다. 예를 들어 설계 단계에서 해결한 문제와 비슷한 또 다른 문제가 발생한다면 앞서 해결한 방식 및 과정을 재사용해야 한다. 같은 문제를 또 분석하고 고민할 필요가 전혀 없다. 120 | 121 | #### 시간낭비의 원인 122 | 1. 문서 또는 설계 단계의 문제점 및 해결책 공유 부재 123 | 2. 집단 내 소통 부족 124 | 3. 처음부터 새롭게 개발하는 방식이 습관인 집단 125 | 4. 개발 프로세스 및 규칙이 없는 경우 126 | 127 | #### 시간낭비의 결과 128 | 1. 간단한 문제에 대한 쓸데없는 논의 129 | 2. 시간과 리소스 증가로 인한 예산 낭비와 늦춰지는 출시일 130 | 3. 한 제품에만 적용되는 폐쇄적인 설계와 노력의 중복, 부실한 위기관리 131 | 132 |
133 | 134 | ### 제품/기술 종속 135 | 개발사는 **다른 벤더사가 제공하는 기술에 의존**하는 경향이 있다. 특정 기술이 시스템에 종속되어 떼어낼 수가 없는 구조가 되어버린다. 136 | 137 | #### 제품/기술 종속의 원인 138 | 1. 벤더사와의 유착 관계 또는 사용 요금 할인 139 | 2. 기술 및 성능이 아닌 마케팅과 세일즈 관점에서 선택한 제품 140 | 3. 수익적으로 성공한 다른 프로젝트에 쓰인 제품을 목적 및 요구사항이 다른 상황에 그대로 접목하는 경우 141 | 4. 기술자나 개발자가 이미 익숙한 제품을 선택 142 | 143 | #### 제품/기술 종속의 결과 144 | 1. 제품의 출시 및 유지보수 주기를 벤더사의 제품의 출시 시기에 맞춘다. 145 | 2. 고객의 요구보다는 제품의 기술을 중심으로 개발한다. 146 | 3. 출시일이 불확실하고 고객의 기대치에 미치지 못한다. 147 | 148 |
149 | 150 | ### 다수 디자인 151 | 조직의 프로세스에 따라 **여러 사람이 모여 함께 시스템을 설계**하는 경우가 있다. 이런 경우 너무 다양한 의견이 나오거나 지식 및 경험이 부족한 기술자의 의견이 더해져 결과물이 복잡해지고 수준에 못 미치게 될 수 있다. 152 | 153 | #### 다수 디자인의 원인 154 | 1. 많은 관계자들의 의견을 수렴하는 분위기의 조직인 경우 155 | 2. 설계를 맡은 총책임자가 없는 경우 156 | 3. 고객의 요구가 아닌 마케터나 기술자의 의견이 우선시될 때 157 | 158 | #### 다수 디자인의 결과 159 | 1. 설계가 끝난 뒤에도 개발자와 설계자 사이에 의견이 갈린다. 160 | 2. 문서화하기 힘들 정도로 설계가 복잡해진다. 161 | 3. 수정사항이 여러 사람을 거쳐야 하고 개발 시간이 지연된다. 162 | -------------------------------------------------------------------------------- /contents/design-pattern/code/FactoryJava/FactoryTest.java: -------------------------------------------------------------------------------- 1 | public class FactoryTest { 2 | 3 | public static void main(String[] args) { 4 | Product pencil = ProductFactory.getProduct("Pencil"); 5 | pencil.sell(); // sell Pencil !!! 6 | 7 | Product note = ProductFactory.getProduct("Note"); 8 | note.sell(); // sell Note !!! 9 | } 10 | } 11 | 12 | interface Product { 13 | public void sell(); 14 | } 15 | 16 | class Pencil implements Product { 17 | 18 | @Override 19 | public void sell() { 20 | System.out.println("sell Pencil !!!"); 21 | } 22 | } 23 | 24 | class Note implements Product { 25 | 26 | @Override 27 | public void sell() { 28 | System.out.println("sell Note !!!"); 29 | } 30 | } 31 | 32 | class ProductFactory { 33 | public static Product getProduct(String className) { 34 | Product p = null; 35 | switch (className) { 36 | case "Pencil": p = new Pencil(); break; 37 | case "Note": p = new Note(); break; 38 | } 39 | return p; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contents/design-pattern/code/MVCPython/mvc_sample.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | class Model(object): 3 | services = { 4 | 'email': {'number': 1000, 'price': 2,}, 5 | 'sms': {'number': 1000, 'price': 10,}, 6 | 'voice': {'number': 1000, 'price': 15,}, 7 | } 8 | 9 | class View(object): 10 | def list_services(self, services): 11 | for svc in services: 12 | print(svc, ' ') 13 | 14 | def list_pricing(self, services): 15 | for svc in services: 16 | print("For" , Model.services[svc]['number'], 17 | svc, "message you pay $", 18 | Model.services[svc]['price']) 19 | 20 | 21 | class Controller(object): 22 | def __init__(self): 23 | self.model = Model() 24 | self.view = View() 25 | 26 | def get_services(self): 27 | services = self.model.services.keys() 28 | return(self.view.list_services(services)) 29 | 30 | def get_pricing(self): 31 | services = self.model.services.keys() 32 | return(self.view.list_pricing(services)) 33 | 34 | class Client(object): 35 | controller = Controller() 36 | print("Services Provided:") 37 | controller.get_services() 38 | 39 | print("Pricing for Services:") 40 | controller.get_pricing() -------------------------------------------------------------------------------- /contents/design-pattern/code/MVCPython/mvc_uml.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class Model(object): 4 | def logic(self): 5 | data = 'Got it!' 6 | print("Model: Crunching data as per business logic") 7 | return data 8 | 9 | class View(object): 10 | def update(self, data): 11 | print("View: Updating the view with results: ", data) 12 | 13 | class Controller(object): 14 | def __init__(self): 15 | self.model = Model() 16 | self.view = View() 17 | 18 | def interface(self): 19 | print("Controller: Relayed the Cient asks") 20 | data = self.model.logic() 21 | self.view.update(data) 22 | 23 | class Client(object): 24 | print("Client: asks for certain information") 25 | controller = Controller() 26 | controller.interface() -------------------------------------------------------------------------------- /contents/design-pattern/code/ObserverJava/Main.java: -------------------------------------------------------------------------------- 1 | public class Main { 2 | 3 | public static void main(String[] args) { 4 | Netflix netflix = new Netflix(); 5 | NetflixUser user1 = new NetflixUser("user1"); 6 | NetflixUser user2 = new NetflixUser("user2"); 7 | NetflixUser user3 = new NetflixUser("user3"); 8 | 9 | netflix.addObserver(user1); 10 | 11 | System.out.println("-------------------------------------------"); 12 | 13 | 14 | netflix.updateMovie("Dark Knight"); 15 | 16 | System.out.println("-------------------------------------------"); 17 | 18 | netflix.addObserver(user2); 19 | netflix.addObserver(user3); 20 | 21 | System.out.println("-------------------------------------------"); 22 | 23 | netflix.updateMovie("Shutter Island"); 24 | 25 | System.out.println("-------------------------------------------"); 26 | 27 | netflix.deleteObserver(user3); 28 | 29 | System.out.println("-------------------------------------------"); 30 | 31 | netflix.updateAnimation("짱구는 못말려14"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contents/design-pattern/code/ObserverJava/Netflix.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | 4 | public class Netflix implements Observable { 5 | ArrayList userList; 6 | 7 | public Netflix() { 8 | this.userList = new ArrayList(); 9 | } 10 | 11 | public void updateMovie(String movie){ 12 | notifyMovie(movie); 13 | } 14 | 15 | public void updateAnimation(String animation){ 16 | notifyAnimation(animation); 17 | } 18 | 19 | @Override 20 | public synchronized void addObserver(NetflixUser user) { 21 | System.out.println(user.name + "가 Netflix를 구독하기 시작했습니다."); 22 | userList.add(user); 23 | } 24 | 25 | @Override 26 | public synchronized void deleteObserver(NetflixUser user) { 27 | System.out.println(user.name + "가 Netflix를 구독 취소했습니다."); 28 | userList.remove(user); 29 | } 30 | 31 | @Override 32 | public void notifyMovie(String content) { 33 | for (NetflixUser user : userList) { 34 | user.updateMovie(content); 35 | } 36 | } 37 | 38 | @Override 39 | public void notifyAnimation(String content) { 40 | for (NetflixUser user : userList) { 41 | user.updateAnimation(content); 42 | } 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /contents/design-pattern/code/ObserverJava/NetflixUser.java: -------------------------------------------------------------------------------- 1 | public class NetflixUser implements Observer { 2 | 3 | public String name; 4 | 5 | public NetflixUser(String name){ 6 | this.name = name; 7 | } 8 | 9 | @Override 10 | public void updateMovie(String content) { 11 | System.out.println(name+ " | 영화 : " + content +"가 업로드 되었습니다."); 12 | } 13 | 14 | @Override 15 | public void updateAnimation(String content) { 16 | System.out.println(name+ " | 애니메이션 : " + content +"가 업로드 되었습니다."); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contents/design-pattern/code/ObserverJava/Observable.java: -------------------------------------------------------------------------------- 1 | public interface Observable { 2 | void addObserver(NetflixUser user); 3 | 4 | void deleteObserver(NetflixUser user); 5 | 6 | void notifyMovie(String content); 7 | 8 | void notifyAnimation(String content); 9 | } 10 | -------------------------------------------------------------------------------- /contents/design-pattern/code/ObserverJava/Observer.java: -------------------------------------------------------------------------------- 1 | public interface Observer { 2 | void updateMovie(String Content); 3 | 4 | void updateAnimation(String content); 5 | } 6 | -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonJava/SingletonTest.java: -------------------------------------------------------------------------------- 1 | public class SingletonTest { 2 | 3 | public static void main(String[]args) { 4 | SingletonClass sclass = SingletonClass.getInstance(); 5 | SingletonClass sclass2 = SingletonClass.getInstance(); 6 | System.out.println(sclass); 7 | System.out.println(sclass2); 8 | // sclass, sclass2 를 출력해보면 그 주소값이 같은 것을 확인할 수 있다. 9 | 10 | NonSingletonClass nclass = new NonSingletonClass(); 11 | NonSingletonClass nclass2 = new NonSingletonClass(); 12 | System.out.println(nclass); 13 | System.out.println(nclass2); 14 | // nclass, nclass2 를 출력해보면 주소값이 다르다. 15 | } 16 | } 17 | 18 | class SingletonClass { 19 | private SingletonClass() {} 20 | 21 | static { sClass = new SingletonClass(); } 22 | private static SingletonClass sClass; 23 | 24 | public static SingletonClass getInstance() { 25 | return sClass; 26 | } 27 | } 28 | 29 | class NonSingletonClass { 30 | public NonSingletonClass() {} 31 | } -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/borg_singleton.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class Borg: 4 | __shared_state = {} 5 | def __init__(self): 6 | self.x = 1 7 | self.__dict__ = self.__shared_state 8 | pass 9 | 10 | b = Borg() 11 | b1 = Borg() 12 | b.x = 3 13 | 14 | print("Borg Object 'b': ", b) ## b and b1 are distinct objects 15 | print("Borg Object 'b1': ", b1) 16 | 17 | print("Object State 'b':", b.__dict__) ## b and b1 share same state 18 | print("Object State 'b1':", b1.__dict__) -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/classical_singleton.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class Singleton(object): 4 | def __new__(cls): 5 | if not hasattr(cls, 'instance'): 6 | cls.instance = super(Singleton, cls).__new__(cls) 7 | return cls.instance 8 | 9 | s = Singleton() 10 | print("Object created", s) 11 | 12 | s1 = Singleton() 13 | print("Object created", s1) -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/lazy_instantiation.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class Singleton: 4 | 5 | __instance = None 6 | def __init__(self): 7 | if not Singleton.__instance: 8 | print(" __init__ method called..") 9 | else: 10 | print("Instance already created:", self.getInstance()) 11 | 12 | @classmethod 13 | def getInstance(cls): 14 | if not cls.__instance: 15 | cls.__instance = Singleton() 16 | return cls.__instance 17 | 18 | s = Singleton() ## class initialized, but object not created 19 | print("Object created", s.getInstance()) ## Gets created here 20 | s1 = Singleton() ## instance already created 21 | -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/metaclass_ex.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class MyInt(type): 4 | 5 | def __call__(cls, *args, **kwds): 6 | print("***** Here's My int *****", args) 7 | print("Now do whatever you want with these objects...") 8 | return type.__call__(cls, *args, **kwds) 9 | 10 | class int(metaclass=MyInt): 11 | 12 | def __init__(self, x, y): 13 | self.x = x 14 | self.y = y 15 | pass 16 | 17 | i = int(4,5) 18 | i = int(7,8) -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/metaclass_singleton.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class MetaSingleton(type): 4 | 5 | _instances = {} 6 | def __call__(cls, *args, **kwargs): 7 | if cls not in cls._instances: 8 | cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) 9 | return cls._instances[cls] 10 | 11 | class Logger(metaclass=MetaSingleton): 12 | pass 13 | 14 | logger1 = Logger() 15 | logger2 = Logger() 16 | print(logger1) 17 | print(logger2) 18 | -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/real_world_scenario_1.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | import sqlite3 4 | class MetaSingleton(type): 5 | 6 | _instances = {} 7 | def __call__(cls, *args, **kwargs): 8 | if cls not in cls._instances: 9 | cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) 10 | return cls._instances[cls] 11 | 12 | 13 | class Database(metaclass=MetaSingleton): 14 | connection = None 15 | def connect(self): 16 | if self.connection is None: 17 | self.connection = sqlite3.connect("db.sqlite3") 18 | self.cursorobj = self.connection.cursor() 19 | return self.cursorobj 20 | 21 | db1 = Database().connect() 22 | db2 = Database().connect() 23 | 24 | print ("Database Objects DB1", db1) 25 | print ("Database Objects DB2", db2) 26 | 27 | -------------------------------------------------------------------------------- /contents/design-pattern/code/SingletonPython/real_world_scenario_2.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Chetan' 2 | 3 | class HealthCheck: 4 | 5 | _instance = None 6 | def __new__(cls, *args, **kwargs): 7 | if not HealthCheck._instance: 8 | HealthCheck._instance = super(HealthCheck, cls).__new__(cls, *args, **kwargs) 9 | return HealthCheck._instance 10 | 11 | def __init__(self): 12 | self._servers = [] 13 | 14 | def addServer(self): 15 | self._servers.append("Server 1") 16 | self._servers.append("Server 2") 17 | self._servers.append("Server 3") 18 | self._servers.append("Server 4") 19 | 20 | def changeServer(self): 21 | self._servers.pop() 22 | self._servers.append("Server 5") 23 | 24 | hc1 = HealthCheck() 25 | hc2 = HealthCheck() 26 | 27 | hc1.addServer() 28 | print("Schedule health check for servers (1)..") 29 | for i in range(4): 30 | print("Checking ", hc1._servers[i]) 31 | 32 | 33 | hc2.changeServer() 34 | print("Schedule health check for servers (2)..") 35 | for i in range(4): 36 | print("Checking ", hc2._servers[i]) 37 | -------------------------------------------------------------------------------- /contents/design-pattern/factory.md: -------------------------------------------------------------------------------- 1 | # 팩토리 (Factory) 디자인 패턴 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 | 팩토리 디자인 패턴이란 객체 생성 처리를 별도의 클래스(팩토리)에게 맡기는 것이다. 즉 객체를 생성할 때 `A a = new A();` 이런 식으로 하지 않고, `Factory.getInstance("A");` 처럼 팩토리에게 객체 생성을 부탁하는 방식이다. 팩토리 디자인 패턴을 적용하면 추후 객체 생성의 변경 사항이 생겼을 때 수정이 용이하다. 6 | 7 | ## 팩토리 디자인 패턴 구현 8 | 9 | Factory 클래스를 통해 Product 객체를 생성하는 코드를 구현해보자. 10 | 11 | ### Product Interface 12 | 13 | ```java 14 | interface Product { 15 | public void sell(); 16 | } 17 | ``` 18 | 19 | ### Product Interface를 구현하는 클래스 Pencil, Note 20 | 21 | ```java 22 | class Pencil implements Product { 23 | 24 | @Override 25 | public void sell() { 26 | System.out.println("sell Pencil !!!"); 27 | } 28 | } 29 | 30 | class Note implements Product { 31 | 32 | @Override 33 | public void sell() { 34 | System.out.println("sell Note !!!"); 35 | } 36 | } 37 | ``` 38 | 39 | ### Product 객체 생성 40 | 41 | #### 팩토리 디자인 패턴을 적용하지 않았을 때 42 | 43 | ```java 44 | Product pencil = new Pencil(); 45 | pencil.sell(); 46 | ``` 47 | 48 | #### 팩토리 디자인 패턴을 적용한다면 49 | 50 | 팩토리 디자인 패턴을 적용하기 위해서는 먼저 객체를 생성하는 Factory 클래스를 생성해야 한다. 51 | 52 | 이 때, Factory 클래스의 객체 생성 메서드는 static으로 선언하는 것이 좋다. 외부에서는 Factory 클래스 내부에 전혀 관심이 없고, 문자열을 통해 생성한 객체와 그 인터페이스에만 관심이 있기 때문이다. 53 | 54 | ```java 55 | class ProductFactory { 56 | public static Product getProduct(String className) { 57 | Product p = null; 58 | switch (className) { 59 | case "Pencil": p = new Pencil(); break; 60 | case "Note": p = new Note(); break; 61 | } 62 | return p; 63 | } 64 | } 65 | ``` 66 | 67 | Factory 클래스를 통해 객체를 생성한다. 68 | 69 | ```java 70 | Product pencil = ProductFactory.getProduct("Pencil"); 71 | pencil.sell(); 72 | 73 | /* 74 | // getProduct 메서드를 static으로 선언하지 않았을 때 75 | ProductFactory factory = new ProductFactory(); 76 | Product pencil = factory.getTransportation("A"); 77 | */ 78 | ``` 79 | 80 | Factory Java 전체 구현 ▶️ [FactoryTest.java](./code/FactoryJava/FactoryTest.java) 81 | 82 | ## Factory Design Pattern의 장점 83 | 84 | 1. 계층 간 의존도가 낮아진다. 85 | 86 | 만일 A 클래스 파일이 변경/삭제 되었을 때, 객체를 직접 생성(`new A()`)했을 때는 객체 생성 코드를 다 고쳐야 한다. 반면 팩토리 디자인 패턴을 쓰면, Factory 코드만 고치면 된다. 87 | 88 | 즉 클래스에 변경사항이 생겼을 때, 객체를 생성해 사용하는 main 메서드 쪽에서는 영향을 받지 않는다. 대신 interface는 반드시 알고 있어야 한다. Factory를 통해 return 되어 오는 실제 객체 녀석에게는 관심이 없고, interface에만 관심이 있으며 비즈니스 로직에 따라서 interface에 있는 메서드를 호출하면 된다. 89 | 90 | 2. 외부에 소통 구조가 생긴다. 91 | 92 | 문자열을 통해 객체 생성을 요청하기 때문에 외부에서 문자열을 읽어와 그 문자열로 객체 생성을 호출하는 등의 구현이 가능하다. 따라서 외부 소통 구조가 생기며 하드코딩 하는 것이 많이 줄어들 수 있다. 93 | -------------------------------------------------------------------------------- /contents/design-pattern/materials/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/design-pattern/materials/.gitkeep -------------------------------------------------------------------------------- /contents/design-pattern/mvc-python.md: -------------------------------------------------------------------------------- 1 | # MVC (Model-View-Controller) 디자인 패턴 2 | 3 | > 작성자 : [정희재](https://github.com/Hee-Jae) 4 | 5 | 본 자료는 '파이썬 디자인 패턴 2/e (Chetan Giridhar)' 책을 토대로 작성되었습니다. 따라서 모든 예시 코드에는 저자를 표시하는 `__author__ = 'Chetan'` 이 포함됩니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [컴파운드 패턴 개요](#컴파운드-패턴-개요) 11 | - [MVC 패턴](#mvc-패턴) 12 | - [MVC 구성요소 이해](#mvc-구성요소-이해) 13 | - [MVC 패턴 예시 1](#mvc-패턴-예시-1) 14 | - [MVC 패턴 예시 2](#mvc-패턴-예시-2) 15 | - [MVC 패턴의 장점](#mvc-패턴의-장점) 16 | - [자주 묻는 질문](#자주-묻는-질문) 17 | 18 |
19 | 20 | --- 21 | 22 | ## 컴파운드 패턴 개요 23 | 24 | 소프트웨어 개발에서는 하나의 디자인 패턴만을 사용하지 않고 여러 가지 패턴을 섞어 사용한다. 일반적으로 여러 패턴을 합쳐 목적을 달성한다. **GoF**에 따르면 **컴파운드 패턴**은 2개 이상의 패턴을 합쳐 문제를 해결하는 패턴이다. 하지만 **컴파운드 패턴**은 단순히 여러 패턴의 조합이 아닌 문제를 해결하는 독립적인 솔루션이다. 25 | 26 | --- 27 | 28 | ## MVC 패턴 29 | **MVC 패턴**은 유저 인터페이스를 구현할 수 있는 유지보수가 용이한 디자인 패턴이다. **MVC 패턴**은 애플리케이션을 모델과 뷰, 컨트롤러로 나누어 구성한다. 각 파트는 맞물려 있으며 요청의 처리와 표현을 분리한다. 30 | 31 | ### MVC 패턴의 원리 32 | **모델**은 데이터와 비즈니스 로직을 처리하고 **뷰**는 데이터의 시각적 표현을 담당하며 **컨트롤러**는 사용자의 요청에 따라 **모델**과 **뷰** 사이에서 요청을 처리한다. **뷰**와 **컨트롤러**는 **모델**에 의존하지만 그 반대는 아니다. 사용자가 데이터를 직접 요청하는 구조이기 때문이다. 이와 같은 **모델**의 독립성이 MVC 패턴의 중요한 부분이다. 33 | 34 | ### MVC 패턴의 전형적인 예시, 웹사이트 로직 35 | 1. 사용자는 뷰를 통해 요청을 보낸다. 뷰는 사용자에게 보여지는 웹사이트다. 뷰에 있는 버튼을 클릭하면 뷰는 컨트롤러에게 요청을 전달한다. 36 | 2. 컨트롤러는 뷰에 전달받은 인풋을 모델로 보낸다. 모델은 요청에 맞는 작업을 수행한다. 37 | 3. 컨트롤러는 사용자의 요청에 따라 버튼 교체 및 UI 추가 등을 뷰에 지시할 수 있다. 38 | 4. 모델은 뷰에 상태 변경을 알린다. 내부 로직 또는 버튼 클릭 등의 외부 트리거에 의한 상태 변경이다. 39 | 5. 뷰는 모델이 전달한 상태를 출력한다. 예를 들어 사용자가 웹사이트에 로그인하면 대시보드를 표시한다. 대시보드의 세부 내용은 모델이 뷰에 전달한다. 40 | 41 | ### MVC 패턴의 4가지 구성요소 42 | 1. 모델 : 데이터를 저장하고 조작하는 클래스 43 | 2. 뷰 : 유저 인터페이스와 데이터의 시각적 표현을 담당하는 클래스 44 | 3. 컨트롤러 : 모델과 뷰를 연결하는 클래스 45 | 4. 클라이언트 : 목적에 따라 정보를 요청하는 클래스 46 | 47 | ### 개발 관점에서 본 MVC 패턴의 메인 클래스 48 | 1. Model : 데이터의 생성과 수정, 소멸 등 데이터에 관한 모든 작업을 정의하고 데이터를 사용하는 메소드를 제공한다. 49 | 2. View : 유저 인터페이스를 담당한다. 애플리케이션에 필요한 웹이나 GUI를 생성하는 메소드를 포함한다. 전달받는 데이터를 시각적으로 표현하는 기능 외 개별적인 로직을 포함하지 않아야 한다. 50 | 3. Controller : 데이터를 받고 전달한다. 요청을 라우팅하는 메소드를 포함한다. 51 | 52 | ### MVC 패턴의 목적 53 | 1. 데이터 조작과 표현의 분리 54 | 2. 쉬운 유지보수와 구현 55 | 3. 유연한 데이터 저장과 표현 방식의 수정. 서로 독립적이므로 쉽게 수정할 수 있다. 56 | 57 | --- 58 | 59 | ## MVC 구성요소 이해 60 | 61 | ### 모델 - 애플리케이션의 뇌 62 | - 모델은 뷰와 컨트롤러와는 독립적인 애플리케이션의 일부이다. 뷰와 컨트롤러는 모델에 의존적이다. 63 | - 모델은 사용자가 요청한 데이터를 제공한다. 일반적으로 모델은 데이터를 저장하고 반환하는 데이터베이스 테이블이다. 모델은 상태 정보와 상태를 변경하는 메소드를 포함하지만 데이터가 사용자에게 어떤 형태로 보여지는지 알지 못한다. 64 | - 모델은 반드시 여러 작업 간 일관성을 유지해야 한다. 그렇지 않으면 사용자는 일관성 없는 오래된 데이터를 전달 받는다. 65 | - 모델은 완전히 독립적이므로 개발자는 뷰와 상관없이 모델 유지보수에 집중할 수 있다. 66 | 67 | ### 뷰 - 외모 68 | - 뷰는 사용자가 인터페이스에서 보게 되는 데이터의 시각적 표현이다. 뷰를 독립적으로 작성할 수 있으나 복잡한 로직을 포함하면 안된다. 모든 로직은 컨트롤러나 모델에 포함되어야 한다. 69 | - 특히 요즘처럼 데스크톱과 모바일 등의 여러 플랫폼과 다양한 화면 크기의 기기를 모두 지원하려면 뷰는 최대한 유연해야 한다. 70 | - 뷰는 데이터베이스와 직접 통신하지 않고 원하는 정보를 얻기 위해 모델에 의존해야 한다. 71 | 72 | ### 컨트롤러 - 접착제 73 | - 컨트롤러는 이름에서 짐작할 수 있듯이 사용자의 행동을 제어한다. 사용자가 인터페이스 내의 특정 요소를 클릭하면 행동(클릭 또는 터치)에 따라 컨트롤러는 모델을 호출해 데이터를 생성 또는 갱신, 삭제한다. 74 | - 컨트롤러는 뷰에 데이터를 전달하고 뷰는 해당 데이터를 렌더링해 사용자에게 보여준다. 75 | - 컨트롤러는 데이터베이스를 직접 호출하거나 데이터를 시각화하지 않는다. 컨트롤러는 모델과 뷰 사이에서 얇은 접착제 역할을 한다. 76 | 77 | --- 78 | 79 | ## MVC 패턴 예시 1 80 | ### 이메일, SMS, 음성 메시지를 제공하는 클라우드 서비스 81 | #### Python 82 | ```python 83 | __author__ = 'Chetan' 84 | class Model(object): 85 | services = { 86 | 'email': {'number': 1000, 'price': 2,}, 87 | 'sms': {'number': 1000, 'price': 10,}, 88 | 'voice': {'number': 1000, 'price': 15,}, 89 | } 90 | 91 | class View(object): 92 | def list_services(self, services): 93 | for svc in services: 94 | print(svc, ' ') 95 | 96 | def list_pricing(self, services): 97 | for svc in services: 98 | print("For" , Model.services[svc]['number'], 99 | svc, "message you pay $", 100 | Model.services[svc]['price']) 101 | 102 | class Controller(object): 103 | def __init__(self): 104 | self.model = Model() 105 | self.view = View() 106 | 107 | def get_services(self): 108 | services = self.model.services.keys() 109 | return(self.view.list_services(services)) 110 | 111 | def get_pricing(self): 112 | services = self.model.services.keys() 113 | return(self.view.list_pricing(services)) 114 | 115 | class Client(object): 116 | controller = Controller() 117 | print("Services Provided:") 118 | controller.get_services() 119 | 120 | print("Pricing for Services:") 121 | controller.get_pricing() 122 | ``` 123 | 124 | --- 125 | 126 | ## MVC 패턴 예시 2 127 | ### 모든 클래스를 구현한 코드 예시 128 | #### Python 129 | 130 | ```python 131 | class Model(object): 132 | def logic(self): 133 | data = 'Got it!' 134 | print("Model: Crunching data as per business logic") 135 | return data 136 | 137 | class View(object): 138 | def update(self, data): 139 | print("View: Updating the view with results: ", data) 140 | 141 | class Controller(object): 142 | def __init__(self): 143 | self.model = Model() 144 | self.view = View() 145 | 146 | def interface(self): 147 | print("Controller: Relayed the Client asks") 148 | data = self.model.logic() 149 | self.view.update(data) 150 | 151 | class Client(object): 152 | print("Client: asks for certain information") 153 | controller = Controller() 154 | controller.interface() 155 | ``` 156 | 157 | --- 158 | 159 | ## MVC 패턴의 장점 160 | 161 | 1. MVC를 사용하면 애플리케이션을 모델과 뷰, 컨트롤러 총 3개의 파트로 나눌 수 있다. 이 구조는 유지보수가 쉽고 요소 간의 독립성이 높아져 복잡성이 줄어든다. 162 | 2. 백엔드 로직을 거의 건드리지 않고 독립적으로 프론트 엔드를 수정할 수 있다. 163 | 3. 모델이나 비즈니스 로직도 마찬가지로 뷰와 상관없이 수정될 수 있다. 164 | 4. 컨트롤러 또한 뷰와 모델과는 독립적으로 수정될 수 있다. 165 | 5. 플랫폼 개발자와 UI 개발자 같이 특정 분야의 전문가들이 독립적으로 일할 수 있는 환경을 제공한다. 166 | 167 | --- 168 | 169 | ## 자주 묻는 질문 170 | 171 | 1. MVC도 하나의 패턴인데 왜 컴파운드 패턴이라고 불리는가? 172 | > 컴파운드 패턴은 더 큰 문제를 해결하기 위해 여러 패턴을 합친 것이다. MVC 패턴은 가장 많이 쓰이는 컴파운드 패턴이다. 안정적이며 많이 쓰이기 때문에 개별적인 패턴처럼 취급된다. 173 | >
174 | > MVC에는 어떤 패턴들이 포함되어 있는가? 175 | >

176 | >
- 옵저버(Observer) 패턴
177 | > Model 의 상태가 변경 되었을 때 Controller, 혹은 View 에게 이 사실을 알리는데 사용된다.

178 | > - 컴포지트(Composite) 패턴
179 | > View 를 구성하는 컴포넌트들은 계층 구조를 이룬다. (e.g. Java Swing 의 JFrame/JLabel 등, Android 의 View/ViewGroup, HTML 의 DOM)

180 | > - 스트래티지(Strategy) 패턴
181 | > Controller 의 핵심 기능을 인터페이스로 분리하여 View 가 이 인터페이스를 통해 Controller 를 구성(Composition) 한다. 그렇기 때문에 View 는 Controller 를 갈아 끼우며 기능을 변경할 수 있다.

182 | > - 또한, 필요에 따라 어댑터(Adapter) 패턴 을 함께 사용할 수도 있다.

183 | > 출처 : Sungho's Blog 184 | >

185 | >
186 | 187 | 2. MVC 웹사이트에서만 쓰이는가? 188 | > 그렇지 않다. 웹사이트가 MVC를 설명하기 가장 좋은 예다. MVC 패턴은 GUI기반 애플리케이션이나 프로그램 내 요소 간의 높은 독립성이 요구되는 경우 적합하다. 블로그나 영화 데이터베이스 애플리케이션, 비디오 스트리밍 애플리케이션 등이 MVC가 적합한 전형적인 예다. 하지만 MVC가 아무리 좋다 해도 랜딩 페이지나 마케팅 콘텐츠, 단일 페이지 애플리케이션 등에 쓰는 것은 적합하지 않다. 189 | 190 | 3. 여러 개의 뷰와 모델을 사용해도 되는가? 191 | > 물론이다. 여러 모델에서 데이터를 수집해 한 개의 뷰에 보여줘야 하는 경우가 자주 있다. 요즘 웹 어플리케이션에서는 일대일 매핑을 쓰는 경우가 흔치 않다. 192 | -------------------------------------------------------------------------------- /contents/design-pattern/observer.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Observer(옵저버) 디자인 패턴 4 | 5 | > 작성자 : [장주섭](https://github.com/wntjq68) 6 | 7 | ## Observer 패턴 개요 8 | 9 | 옵저버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다. 10 | 11 | 출처: [wikipedia: 옵저버 패턴](https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4) 12 | 13 | 이 말을 좀 더 구체적으로 풀어서 얘기하자면 Observer패턴을 사용하기 위한 객체 및 인터페이스로 **Observable**(주체)과 **Observer** 두 가지가 존재한다. 여기서 Observable객체는 상태가 변화 되는 주체이고 이러한 주체를 관찰하고 있는 것이 Observer이다. Observable은 하나 또는 여러 Observer를 등록하거나 해제할 수 있고 어떠한 상태 변화시 Observer들에 알릴 수 있다. 각각의 Observer들은 이러한 상태변화에 대해서 전달 받으면 각각의 알맞는 행동을 수행한다. 14 | 15 | 16 | 17 | ## Observer 패턴 장점 18 | 19 | 1. 한 객체의 변경사항을 참조하고 있는 다른 객체에서 실시간으로 변경사항을 전파 할 수 있다. 20 | 2. 객체 간의 의존성을 제거함으로써 느슨한 결합으로 시스템이 유연해진다. 21 | - 옵저버를 언제든 새로 추가 및 제거 가능하다. 22 | - 새로운 형식의 옵저버라도 Observable(주체)를 변경할 필요 없다. 23 | - Observable(주체)와 Observer는 독립적으로 재사용 가능하다. 24 | 25 | 26 | 27 | ## Observer 패턴 단점 28 | 29 | 1. observer를 필요한 시점 외에 제거하지 않으면 메모리 누수가 일어날 수 있다. 30 | 2. 너무 빈번하게 사용하게 되면 상태 관리가 힘들 수 있다. 31 | 32 | 33 | 34 | ## Netflix 구독 예제 (Java) 35 | 36 | Observer 패턴을 이용한 Netflix구독 예제이다. 37 | 38 | NetflixUser(Observer)는 Netflix(Observable)에 영화 또는 애니메이션이 업데이트 되는 것을 관찰 하고 있다. 만약 영화 또는 애니메이션이 업데이트 되면 Netflix(Observable)은 자신을 구독하고 있는 NetflixUser(Observer)에게 알려주고 NetflixUser(Observer)는 영화이냐 애니메이션이냐에 따라 각기 다른 로직을 수행하게 된다. 39 | 40 | ### Netflix Class (Observable) 41 | 42 | ```java 43 | public class Netflix implements Observable { 44 | 45 | // 구독하고 있는 user 리스트 46 | ArrayList userList; 47 | 48 | public Netflix() { 49 | this.userList = new ArrayList(); 50 | } 51 | 52 | // movie를 update해주고 구독 중인 구독자에게 알림 53 | public void updateMovie(String movie){ 54 | // movie update code 55 | notifyMovie(movie); 56 | } 57 | 58 | // animation을 update해주고 구독 중인 구독자에게 알림 59 | public void updateAnimation(String animation){ 60 | // animation update code 61 | notifyAnimation(animation); 62 | } 63 | 64 | // 구독자 추가 65 | @Override 66 | public synchronized void addObserver(NetflixUser user) { 67 | System.out.println(user.name + "가 Netflix를 구독하기 시작했습니다."); 68 | userList.add(user); 69 | } 70 | 71 | // 구독자 삭제 72 | @Override 73 | public synchronized void deleteObserver(NetflixUser user) { 74 | System.out.println(user.name + "가 Netflix를 구독 취소했습니다."); 75 | userList.remove(user); 76 | } 77 | 78 | // Movie가 update되었다고 구독자들에게 알림 79 | @Override 80 | public void notifyMovie(String content) { 81 | for (NetflixUser user : userList) { 82 | user.updateMovie(content); 83 | } 84 | } 85 | 86 | // Animation이 update되었다고 구독자들에게 알림 87 | @Override 88 | public void notifyAnimation(String content) { 89 | for (NetflixUser user : userList) { 90 | user.updateAnimation(content); 91 | } 92 | } 93 | } 94 | ``` 95 | 96 | 97 | 98 | ### NetflixUser Class (Observer) 99 | 100 | ```java 101 | public class NetflixUser implements Observer { 102 | // 구독자 이름 103 | public String name; 104 | 105 | public NetflixUser(String name){ 106 | this.name = name; 107 | } 108 | // movie update시 알림을 받음 109 | @Override 110 | public void updateMovie(String content) { 111 | System.out.println(name+ " | 영화 : " + content +"가 업로드 되었습니다."); 112 | } 113 | 114 | // animation update시 알림을 받음 115 | @Override 116 | public void updateAnimation(String content) { 117 | System.out.println(name+ " | 애니메이션 : " + content +"가 업로드 되었습니다."); 118 | } 119 | } 120 | ``` 121 | 122 | 123 | 124 | ### Main Class 125 | 126 | ```java 127 | public class Main { 128 | 129 | public static void main(String[] args) { 130 | Netflix netflix = new Netflix(); 131 | NetflixUser user1 = new NetflixUser("user1"); 132 | NetflixUser user2 = new NetflixUser("user2"); 133 | NetflixUser user3 = new NetflixUser("user3"); 134 | 135 | // user1 이 구독을 시작 함 136 | netflix.addObserver(user1); 137 | 138 | System.out.println("-------------------------------------------"); 139 | 140 | // netflix에 Dark Knight가 업데이트 됨 141 | netflix.updateMovie("Dark Knight"); 142 | 143 | System.out.println("-------------------------------------------"); 144 | 145 | // user2, user3 가 구독을 시작함 146 | netflix.addObserver(user2); 147 | netflix.addObserver(user3); 148 | 149 | System.out.println("-------------------------------------------"); 150 | 151 | // netflix에 Shutter Island가 업데이트 됨 152 | netflix.updateMovie("Shutter Island"); 153 | 154 | System.out.println("-------------------------------------------"); 155 | 156 | // user3가 구독을 취소함 157 | netflix.deleteObserver(user3); 158 | 159 | System.out.println("-------------------------------------------"); 160 | 161 | // netflix에 짱구는 못말려14가 업데이트 됨 162 | netflix.updateAnimation("짱구는 못말려14"); 163 | } 164 | } 165 | ``` 166 | 167 | 168 | 169 | ### Result 170 | 171 |

172 | 173 |

174 | 175 | -------------------------------------------------------------------------------- /contents/design-pattern/singleton-java.md: -------------------------------------------------------------------------------- 1 | # 싱글톤 (Singleton) Java 구현 방법 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 | 싱글톤 디자인 패턴에 대한 자세한 설명은 [싱글톤 (Singleton) 문서](singleton.md)를 참고하세요. 여기서는 싱글톤 디자인 패턴의 Java 구현 방법을 정리합니다. 6 | 7 | ## private 생성자 8 | 9 | Java의 싱글톤 구현은 생성자에 대한 이해로부터 시작한다. 생성자를 public으로 둔다면 누구나 접근하여 객체 생성이 가능하기 때문에 오직 하나의 객체 생성에 대한 제약을 걸 수 없다. 따라서 **생성자를 private**으로 두고, **생성된 객체는 static 변수로 관리**, **객체 접근에 대한 부분은 별도의 public한 method**를 둬서 싱글톤 디자인 패턴을 구현한다. 10 | 11 | ```java 12 | class ClassName { 13 | private ClassName() {} 14 | } 15 | ``` 16 | 17 | ## 객체 생성의 3가지 방법 18 | 19 | 생성자를 private으로 뒀을 때 객체를 생성하는 방법은 다음의 3가지 경우가 있을 수 있다. 20 | 21 | ### getInstance 22 | 23 | 코드가 직관적이나 if 조건 체크가 번거롭다는 단점이 있다. 24 | 25 | ```java 26 | class ClassName { 27 | private ClassName() {} 28 | 29 | static ClassName className; 30 | 31 | public static ClassName getInstance() { 32 | if (className == null) className = new ClassName(); 33 | return className; // 객체 접근 34 | } 35 | } 36 | ``` 37 | 38 | ### static block 39 | 40 | 가장 간결한 방법이다. 41 | 42 | ```java 43 | class ClassName { 44 | private ClassName() {} 45 | 46 | private static ClassName className; 47 | static { className = new ClassName(); } 48 | } 49 | ``` 50 | 51 | ### member 변수 선언 시 생성 52 | 53 | 이 방법은 되도록 지양하는 방법이다. 54 | 55 | ```java 56 | class ClassName { 57 | private ClassName() {} 58 | 59 | private static ClassName className = new ClassName(); 60 | } 61 | ``` 62 | 63 | ## 객체 접근 64 | 65 | 자기 자신을 return하는 public static method를 구현한다. static 변수에는 static 끼리만 접근이 가능하기 때문에 static으로 선언해야 한다. 66 | 67 | ```java 68 | class ClassName { 69 | private ClassName() {} 70 | 71 | private static ClassName className; 72 | static { className = new ClassName(); } 73 | 74 | public static ClassName getInstance() { 75 | return className; 76 | } 77 | } 78 | ``` 79 | 80 | Singleton Java 전체 구현 ▶️ [SingletonTest.java](./code/SingletonJava/SingletonTest.java) 81 | -------------------------------------------------------------------------------- /contents/design-pattern/singleton.md: -------------------------------------------------------------------------------- 1 | # 싱글톤 (Singleton) 디자인 패턴 2 | 3 | > 작성자 : [정희재](https://github.com/Hee-Jae) 4 | 5 | 본 자료는 '파이썬 디자인 패턴 2/e (Chetan Giridhar)' 책을 토대로 작성되었습니다. 따라서 모든 예시 코드에는 저자를 표시하는 `__author__ = 'Chetan'` 이 포함됩니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [싱글톤 (Singleton) 디자인 패턴이란](#싱글톤-singleton-디자인-패턴이란) 11 | - [게으른 초기화](#게으른-초기화) 12 | - [모듈 싱글톤](#모듈-싱글톤) 13 | - [모노스테이트 싱글톤 패턴](#모노스테이트-싱글톤-패턴) 14 | - [싱글톤과 메타클래스](#싱글톤과-메타클래스) 15 | - [싱글톤 패턴 사용 사례 1](#싱글톤-패턴-사용-사례-1) 16 | - [싱글톤 패턴 사용 사례 2](#싱글톤-패턴-사용-사례-2) 17 | - [싱글톤 패턴의 단점](#싱글톤-패턴의-단점) 18 | 19 |
20 | 21 | --- 22 | 23 | ## 싱글톤 (Singleton) 디자인 패턴이란 24 | 25 | 싱글톤 디자인 패턴은 글로벌하게 접근 가능한 단 한 개의 객체만을 허용하는 패턴이다. 26 | 27 | ### 싱글톤 디자인 패턴의 목적 28 | - 클래스에 대한 단일 객체 생성 29 | - 전역 객체 제공 30 | - 공유된 리소스에 대한 동시 접근 제어 31 | 32 | ### 싱글톤 패턴 구현 33 | 34 | #### Python 35 | ```python 36 | __author__ = 'Chetan' 37 | 38 | class Singleton(object): 39 | def __new__(cls): 40 | if not hasattr(cls, 'instance'): 41 | cls.instance = super(Singleton, cls).__new__(cls) 42 | return cls.instance 43 | 44 | s = Singleton() 45 | print("Object created", s) 46 | 47 | s1 = Singleton() 48 | print("Object created", s1) 49 | 50 | ''' <실행 결과> 51 | Object created <__main__.Singleton object at 0x1005c12b0> 52 | Object created <__main__.Singleton object at 0x1005c12b0> 53 | ''' 54 | ``` 55 | - 한 개의 instance 클래스 인스턴스를 생성한다. 56 | - 이미 생성된 인스턴스가 있다면 재사용한다. 57 | 58 | ## 게으른 초기화 59 | 60 | 인스턴스가 꼭 필요한 상황일 때 초기화를 한다. 사용할 수 있는 리소스가 제한적일 때 사용하는 방식이다. 61 | 62 | ```python 63 | __author__ = 'Chetan' 64 | 65 | class Singleton: 66 | 67 | __instance = None 68 | 69 | def __init__(self): 70 | if not Singleton.__instance: 71 | print(" __init__ method called..") 72 | else: 73 | print("Instance already created:", self.getInstance()) 74 | 75 | @classmethod 76 | def getInstance(cls): 77 | if not cls.__instance: 78 | cls.__instance = Singleton() 79 | return cls.__instance 80 | 81 | s = Singleton() 82 | ## 클래스는 초기화 되었지만 아직 객체는 생성되지 않았다. 83 | print("Object created", Singleton.getInstance()) 84 | ## 객체를 생성했다. 85 | s1 = Singleton() 86 | ## 객체는 이미 생성되었다. 87 | 88 | ''' <실행 결과> 89 | __init__ method called.. 90 | __init__ method called.. 91 | Object created <__main__.Singleton object at 0x10ca025c0> 92 | Instance already created: <__main__.Singleton object at 0x10ca025c0> 93 | ''' 94 | ``` 95 | 96 | - `getInstance()`로 얻어오는 주소가 일치하는 것을 볼 수 있다. 97 | 98 | ## 모듈 싱글톤 99 | 100 | 파이썬에서 모든 모듈은 기본적으로 싱글톤이다. 한 개의 객체만 유지하고 반환하는 싱글톤 방식이다. 파이썬의 모듈 임포트 방식은 다음과 같다. 101 | 102 | - 파이썬 모듈이 임포트됐는지 확인한다. 103 | - 임포트 됐다면 해당 객체를 반환한다. 104 | - 임포트되지 않았다면 임포트하고 인스턴스화 한다. 105 | - 모듈은 임포트와 동시에 초기화된다. 하지만 같은 모듈을 다시 임포트하면 초기화하지 않는다. 106 | 107 | ```python 108 | import sys 109 | import sys 110 | ``` 111 | 이처럼 같은 모듈을 중복해서 임포트해도 sys객체는 하나만 생성된다. 112 | 113 | ## 모노스테이트 싱글톤 패턴 114 | 115 | 싱글톤 패턴과 달리 두 개 이상의 객체가 생성될 수 있지만 모든 객체가 상태를 공유한다. 116 | 117 | ### 모노스테이트 싱글톤 예시 118 | ```python 119 | __author__ = 'Chetan' 120 | 121 | class Borg: 122 | __shared_state = {} 123 | def __init__(self): 124 | self.x = 1 125 | self.__dict__ = self.__shared_state 126 | pass 127 | 128 | b = Borg() 129 | b1 = Borg() 130 | b.x = 3 131 | 132 | print("Borg Object 'b': ", b) 133 | print("Borg Object 'b1': ", b1) 134 | ## b와 b1은 서로 다른 객체다. 135 | print("Object State 'b':", b.__dict__) 136 | print("Object State 'b1':", b1.__dict__) 137 | ## 그러나 서로 state를 공유한다. 138 | 139 | ''' <실행 결과> 140 | Borg Object 'b': <__main__.Borg object at 0x10d6d95c0> 141 | Borg Object 'b1': <__main__.Borg object at 0x10d6d95f8> 142 | Object State 'b': {'x': 3} 143 | Object State 'b1': {'x': 3} 144 | ''' 145 | ``` 146 | 147 | ## 싱글톤과 메타클래스 148 | 149 | 메타클래스는 클래스의 클래스다. 즉 클래스는 자신의 메타클래스의 인스턴스다. 클래스는 메타클래스가 정의한다. 150 | 151 | 152 | **class A** 구문으로 클래스를 생성하면 파이썬은 **A = type(name, bases, dict)** 를 실행한다. 이미 정의된 메타클래스가 있다면 파이썬은 **A = MetaKls(name, bases, dict)** 를 실행해 클래스를 생성한다. 153 | 154 | - `name` : 클래스명 155 | - `base` : 베이스 클래스 156 | - `dict` : 속성 값 157 | 158 | ### 메타클래스 구현코드 159 | 160 | ```python 161 | __author__ = 'Chetan' 162 | 163 | class MyInt(type): 164 | 165 | def __call__(cls, *args, **kwds): 166 | print("***** Here's My int *****", args) 167 | print("Now do whatever you want with these objects...") 168 | return type.__call__(cls, *args, **kwds) 169 | 170 | class int(metaclass=MyInt): 171 | 172 | def __init__(self, x, y): 173 | self.x = x 174 | self.y = y 175 | pass 176 | 177 | i = int(4,5) 178 | i = int(7,8) 179 | 180 | ''' <실행 결과> 181 | ***** Here's My int ***** (4, 5) 182 | Now do whatever you want with these objects... 183 | ***** Here's My int ***** (7, 8) 184 | Now do whatever you want with these objects... 185 | ''' 186 | 187 | ``` 188 | 189 | - `__call__` 메소드는 이미 존재하는 클래스의 객체를 생성할 때 호출되는 파이썬의 특수 메소드다. 190 | - 객체 생성을 메타클래스가 제어한다. 191 | - 메타클래스가 클래스와 객체 생성을 제어하기 때문에 싱글톤을 생성하는 용도로 사용할 수 있다. 192 | 193 | ### 메타클래스를 사용한 싱글톤 패턴 구현 194 | 195 | ```python 196 | __author__ = 'Chetan' 197 | 198 | class MetaSingleton(type): 199 | 200 | _instances = {} 201 | def __call__(cls, *args, **kwargs): 202 | if cls not in cls._instances: 203 | cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) 204 | return cls._instances[cls] 205 | 206 | class Logger(metaclass=MetaSingleton): 207 | pass 208 | 209 | logger1 = Logger() 210 | logger2 = Logger() 211 | print(logger1) 212 | print(logger2) 213 | 214 | ''' <실행 결과> 215 | <__main__.Logger object at 0x1059635c0> 216 | <__main__.Logger object at 0x1059635c0> 217 | ''' 218 | ``` 219 | 220 | ## 싱글톤 패턴 사용 사례 1 221 | 222 | 한 개의 DB를 공유하는 클라우드 서비스의 예. 223 | - 데이터베이스의 일관성을 보존해야 한다. 연산 간의 충돌이 없어야 한다. 224 | - 다수의 DB 연산을 처리하려면 메모리와 CPU를 효율적으로 사용해야 한다. 225 | 226 | ```python 227 | __author__ = 'Chetan' 228 | 229 | import sqlite3 230 | class MetaSingleton(type): 231 | 232 | _instances = {} 233 | def __call__(cls, *args, **kwargs): 234 | if cls not in cls._instances: 235 | cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs) 236 | return cls._instances[cls] 237 | 238 | class Database(metaclass=MetaSingleton): 239 | connection = None 240 | def connect(self): 241 | if self.connection is None: 242 | self.connection = sqlite3.connect("db.sqlite3") 243 | self.cursorobj = self.connection.cursor() 244 | return self.cursorobj 245 | 246 | db1 = Database().connect() 247 | db2 = Database().connect() 248 | 249 | print ("Database Objects DB1", db1) 250 | print ("Database Objects DB2", db2) 251 | 252 | ''' <실행 결과> 253 | Database Objects DB1 254 | Database Objects DB2 255 | ''' 256 | ``` 257 | 258 | - `Database` 클래스는 `MetaSingleton` 메타클래스의 도움으로 싱글톤 역할을 한다. 단 한 개의 `Database` 클래스 객체만 생성된다. 259 | - 웹 앱이 DB 연산을 요청할 때마다 `Database` 클래스를 생성하지만 내부적으로 한 개의 객체만 생성된다. 따라서 데이터베이스의 동기화가 보장된다. 리소스를 적게 사용해 메모리와 CPU사용량을 최적화할 수 있다. 260 | 261 | ## 싱글톤 패턴 사용 사례 2 262 | 263 | 인프라 상태를 확인하는 서비스의 예. 264 | - `HealthCheck` 클래스를 싱글톤으로 구현한다. 265 | - 상태를 확인해야 하는 서버의 목록을 만들고 목록에서 제거된 서버의 상태는 확인하지 않아야 한다. 266 | 267 | ``` python 268 | __author__ = 'Chetan' 269 | 270 | class HealthCheck: 271 | 272 | _instance = None 273 | def __new__(cls, *args, **kwargs): 274 | if not HealthCheck._instance: 275 | HealthCheck._instance = super(HealthCheck, cls).__new__(cls, *args, **kwargs) 276 | return HealthCheck._instance 277 | 278 | def __init__(self): 279 | self._servers = [] 280 | 281 | def addServer(self): 282 | self._servers.append("Server 1") 283 | self._servers.append("Server 2") 284 | self._servers.append("Server 3") 285 | self._servers.append("Server 4") 286 | 287 | def changeServer(self): 288 | self._servers.pop() 289 | self._servers.append("Server 5") 290 | 291 | hc1 = HealthCheck() 292 | hc2 = HealthCheck() 293 | 294 | hc1.addServer() 295 | print("Schedule health check for servers (1)..") 296 | for i in range(4): 297 | print("Checking ", hc1._servers[i]) 298 | 299 | 300 | hc2.changeServer() 301 | print("Schedule health check for servers (2)..") 302 | for i in range(4): 303 | print("Checking ", hc2._servers[i]) 304 | 305 | ''' <실행 결과> 306 | Schedule health check for servers (1).. 307 | Checking Server 1 308 | Checking Server 2 309 | Checking Server 3 310 | Checking Server 4 311 | Schedule health check for servers (2).. 312 | Checking Server 1 313 | Checking Server 2 314 | Checking Server 3 315 | Checking Server 5 316 | ''' 317 | ``` 318 | 319 | - `addServer()` 메소드는 서버를 목록에 추가한다. 320 | - `changeServer()` 메소드는 서버를 목록에서 제거하고 새로운 서버를 추가한다. 321 | - `h1`과 `h2`는 싱글톤으로 생성된 객체이므로 동일한 객체다. 322 | 323 | ## 싱글톤 패턴의 단점 324 | 325 | - 전역 변수의 값이 실수로 변경된 것을 모르고 애플리케이션에서 사용될 수 있다. 326 | - 같은 객체에 대한 여러 참조자가 생길 수 있다. 싱글톤은 한 개의 객체만을 만들기 때문에 같은 객체에 대한 여러 개의 참조자가 생긴다. 327 | - 전역 변수에 종속적인 모든 클래스 간 상호관계가 복잡해진다. 전역 변수 수정이 의도치 않게 다른 클래스에도 영향을 줄 수 있다. 328 | -------------------------------------------------------------------------------- /contents/design-pattern/template-method.md: -------------------------------------------------------------------------------- 1 | # 템플릿 메소드 패턴 2 | 3 | > 작성자 : [정희재](https://github.com/Hee-Jae) 4 | 5 | 본 자료는 '파이썬 디자인 패턴 2/e (Chetan Giridhar)' 책을 토대로 작성되었습니다. 따라서 모든 예시 코드에는 저자를 표시하는 `__author__ = 'Chetan'` 이 포함됩니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [템플릿 메소드 패턴 개요](#템플릿-메소드-패턴-개요) 11 | - [템플릿 메소드 패턴의 구성요소](#템플릿-메소드-패턴의-구성요소) 12 | - [템플릿 메소드 패턴 구현 코드](#템플릿-메소드-패턴-구현-코드) 13 | - [템플릿 메소드 패턴 예시 (iOS 컴파일러)](#템플릿-메소드-패턴-예시-ios-컴파일러) 14 | - [템플릿 메소드 패턴 – 후크](#템플릿-메소드-패턴-후크) 15 | - [할리우드 원칙과 템플릿 메소드](#할리우드-원칙과-템플릿-메소드) 16 | - [템플릿 메소드 패턴의 장단점](#템플릿-메소드-패턴의-장단점) 17 | - [자주 묻는 질문](#자주-묻는-질문) 18 | 19 |
20 | 21 | --- 22 | 23 | ## 템플릿 메소드 패턴 개요 24 | 25 | 행동 패턴에서는 각 객체의 역할이 중요하다. 객체는 상호 작용을 통해 더 큰 기능을 구현할 수 있다. **템플릿 메소드 패턴**은 행동 패턴의 한 종류로 애플리케이션의 뼈대나 핵심 알고리즘을 `Template Method`라는 곳에 정의한다. 템플릿 메소드 패턴은 알고리즘의 일부 단계를 서브클래스화해 알고리즘의 부분적 수정 및 재정의를 쉽게 한다. 즉 서브클래스를 자유롭게 재정의할 수 있다. 26 | 27 | 28 | ### 템플릿 메소드 패턴은 다음과 같은 상황에 적합하다. 29 | 1. 여러 알고리즘 또는 클래스의 구조나 로직이 비슷할 때 30 | 2. 알고리즘을 단계별로 서브클래스화해 코드의 중복을 줄일 수 있는 경우 31 | 3. 서브클래스를 오버라이드해 여러 알고리즘을 구현할 수 있는 경우 32 | 33 | 34 | ### 템플릿 매소드 패턴의 목적 35 | 1. 알고리즘의 뼈대를 원시 연산으로 구현 36 | 2. 알고리즘의 구조를 수정하지 않고 일부 서브클래스를 재정의 37 | 3. 코드의 재사용과 중복 최소화 38 | 4. 공통 인터페이스 및 구현 활용 39 | 40 | --- 41 | ### 템플릿 메소드 패턴의 구성요소 42 | #### 1. AbstractClass 43 | - **알고리즘의 단계를 정의하는 인터페이스** 44 | - 알고리즘의 각 단계를 정의하는 추상 메소드로 구성되어 있다. 45 | - `ConcreteClass`에서 오버라이드한다. 46 | #### 2. ConcreteClass 47 | - **단계별 서브클래스** 48 | - 여러 추상 메소드로 구성된 알고리즘의 서브클래스를 구현한다. 49 | #### 3. template_method() 50 | - **단계별 메소드를 호출하는 알고리즘 정의** 51 | - 알고리즘의 뼈대를 정의한다. 52 | - 전체 알고리즘을 정의하는 여러 추상 메소드를 호출한다. 53 | 54 | ### 템플릿 메소드 패턴 구현 코드 55 | ```python 56 | __author__ = 'Chetan' 57 | 58 | from abc import ABCMeta, abstractmethod 59 | 60 | # 알고리즘의 인터페이스 정의 61 | class AbstractClass(metaclass=ABCMeta): 62 | def __init__(self): 63 | pass 64 | 65 | @abstractmethod 66 | def operation1(self): 67 | pass 68 | 69 | @abstractmethod 70 | def operation2(self): 71 | pass 72 | 73 | # 단계별 메소드를 호출하여 알고리즘 뼈대 정의 74 | def template_method(self): 75 | print("Defining the Algorithm. Operation1 follows Operation2") 76 | self.operation2() 77 | self.operation1() 78 | 79 | # 인터페이스를 오버라이드하여 서브클래스 구현 80 | class ConcreteClass(AbstractClass): 81 | 82 | def operation1(self): 83 | print("My Concrete Operation1") 84 | 85 | def operation2(self): 86 | print("Operation 2 remains same") 87 | 88 | # 사용 예시 89 | class Client: 90 | def main(self): 91 | self.concreate = ConcreteClass() 92 | self.concreate.template_method() 93 | 94 | client = Client() 95 | client.main() 96 | ``` 97 | 98 | ### 템플릿 메소드 패턴 예시 (iOS 컴파일러) 99 | ```python 100 | __author__ = 'Chetan' 101 | 102 | from abc import ABCMeta, abstractmethod 103 | 104 | # 컴파일러 알고리즘 인터페이스 105 | class Compiler(metaclass=ABCMeta): 106 | 107 | # 소스 수집 108 | @abstractmethod 109 | def collectSource(self): 110 | pass 111 | 112 | # 객체로 컴파일 113 | @abstractmethod 114 | def compileToObject(self): 115 | pass 116 | 117 | # 실행 118 | @abstractmethod 119 | def run(self): 120 | pass 121 | 122 | # 단계별 메소드를 호출하여 컴파일 과정 설계 123 | def compileAndRun(self): 124 | self.collectSource() 125 | self.compileToObject() 126 | self.run() 127 | 128 | # 인터페이스를 오버라이드하여 알고리즘 구체화 129 | class iOSCompiler(Compiler): 130 | def collectSource(self): 131 | print("Collecting Swift Source Code") 132 | 133 | def compileToObject(self): 134 | print("Compiling Swift code to LLVM bitcode") 135 | 136 | def run(self): 137 | print("Program runing on runtime environment") 138 | 139 | # 사용 예시 140 | iOS = iOSCompiler() 141 | iOS.compileAndRun() 142 | ``` 143 | 144 | --- 145 | ### 템플릿 메소드 패턴 – 후크 146 | **후크**는 추상 클래스(`AbstactClass`)에 정의되는 메소드다. 일반적으로 기본 구현을 포함하여 정의된다. **후크**는 서브클래스가 알고리즘 중간 단계를 제어할 수 있는 기능을 제공한다. 예를 들어 앞서 다룬 [iOS 컴파일러](#템플릿-메소드-패턴-예시-ios-컴파일러) 예제에서 `run()` 메소드 실행 전에 유저에게 실행 여부를 확인하는 간단한 **후크**를 추가할 수 있다. 다만 서브클래스는 **후크**를 꼭 사용하지 않아도 된다. 서브클래스는 추상 메소드를 반드시 구현하여 구체화 해야하지만 **후크**는 선택적으로 구현할 수 있다. 147 | 148 | ### 할리우드 원칙과 템플릿 메소드 149 | 할리우드 원칙은 `“먼저 연락하지 마세요. 저희가 연락 드리겠습니다.”` 에 기반하는 디자인 철학이다. 할리우드 영화 제작사에서 배우가 필요할 때 제작사가 배우에게 연락하는 것에서 비롯된 원칙이다. **상위 요소가 언제 어떤 하위 요소가 필요한지 결정한다.** 즉 상위 요소가 하위 요소에 대한 접근 및 필요성을 직접 결정하는 개념이다. **템플릿 메소드 패턴에서는 상위 추상 클래스가 알고리즘에 필요한 단계를 정리한다.** 알고리즘에 따라 각 단계에 맞는 하위 클래스가 호출된다. 150 | 151 | 152 | 153 | --- 154 | ### 템플릿 메소드 패턴의 장단점 155 | 156 | #### 장점 157 | 1. 코드 중복이 없다. 158 | 2. 컴포지션이 아닌 상속을 사용하므로 코드를 재활용할 수 있다. 일부의 함수만 오버라이드하면 된다. 159 | 3. 알고리즘의 각 단계를 서브클래스에서 구현할 수 있는 유연성을 제공한다. 160 | 161 | #### 단점 162 | 1. 코드 디버깅 및 이해가 어려울 수 있다. 추상 메소드에 구현되지 않은 메소드를 구현하거나 추상 메소드를 아예 구현하지 않는 실수를 저지를 수 있다. 에러 핸들링과 문서화가 필수적이다. 163 | 2. 어떤 계층의 클래스(상위 및 하위 클래스)를 수정한다면 전체 구조 및 구현에 영향을 줄 수 있어 유지 보수가 어렵다. 164 | 165 | --- 166 | ### 자주 묻는 질문 167 | **1. 하위 클래스가 상위 클래스를 호출하는 것을 막아야 하는가?** 168 | 아니다. 하위 클래스는 상속을 통해 상위 클래스를 호출할 수 있어야 한다. 하지만 프로그래머는 상하위 클래스가 서로 의존하는 순환 종속성이 없도록 주의해야 한다. 169 | 170 | **2. 전략 패턴과 템플릿 패턴의 차이점은 무엇인가?** 171 | 두 패턴 모두 알고리즘을 캡슐화한다. 하지만 템플릿 패턴은 상속을 사용하는 반면에 전략 패턴은 컴포지션을 사용한다. 템플릿 메소드 패턴은 서브클래스를 사용해 컴파일 단계에서 알고리즘을 선택하지만 전략 패턴은 런타임에 선택한다. 172 | 173 | 174 | -------------------------------------------------------------------------------- /contents/language/README.md: -------------------------------------------------------------------------------- 1 | # Language 2 | 3 | - [코루틴](./coroutine.md) 4 | 5 | ## Java 6 | 7 | - [자바 언어의 구조와 기본 문법](./java/1.md) 8 | - [객체지향 핵심 원리](./java/2.md) 9 | - [부록 - 생각해보기](./java/appendix.md) 10 | 11 | ## C++ 12 | 13 | - [C++ STL](./c++/STL.md) 14 | - [Modern C++](./c++/moderncpp.md) 15 | - [멀티스레드 프로그래밍](./c++/multithread-programming.md) 16 | -------------------------------------------------------------------------------- /contents/language/c++/STL.md: -------------------------------------------------------------------------------- 1 | # STL 2 | 3 | > 작성자 : [박재용](https://github.com/ggjae) 4 | 5 | - C++의 기본! STL이 무엇인지 이해하기 6 | - STL을 직접 사용해보기 7 | 8 |
9 | Table of Contents 10 | 11 | - [What is STL](#STL) 12 | - [What is Container](#STL-컨테이너) 13 | - [What is Iterator](#STL-반복자) 14 | - [What is Algorithm](#STL-알고리즘) 15 | - [What is Functor](#STL-함수자) 16 | 17 |
18 | 19 | --- 20 | 21 | ## STL 22 | 23 | STL은 Standard Template Library로 C++를 위한 표준 라이브러리로서 컨테이너, 반복자, 알고리즘 그리고 함수자라고 불리는 네가지의 구성요소를 제공한다. (C++ : Black Book) 24 | C++에서 vector, queue 등 다양한 라이브러리들을 #include 하여 사용한 적이 있는가? 이것들이 STL의 일부이다. 25 | 26 | ### 우리가 왜 STL을 배워야 하는가? 27 | 28 | 초창기 STL은 열악한 성능과 버그에 의해서 개발자들의 호응을 끌어낼 수 없었지만 최근에는 STL 없이 C++을 논할 수 없을만큼 중요해졌다. STL을 사용함으로서 효율적인 프로그래밍이 가능해졌다. 29 | 30 | ## STL 컨테이너 31 | 32 | 일반적으로 컨테이너는 다른 객체들을 보관하는 하나의 커다란 보관소라고 볼 수 있다. STL 컨테이너의 경우 클래스 템플릿의 형태로 구현되어 있기 때문에 임의의 타입 원소들을 위한 컨테이너를 만들 수 있다. 표준 라이브러리에서는 여러가지 형태의 컨테이너를 제공한다. 33 | 시퀀스 컨테이너 - Sequence Container 34 | > ex) `vector, deque, array, priority_queue, list` 35 | 36 | 37 | 연관 컨테이너 - Associative Container 38 | > ex) `set, map, multiset, multimap, unordered_set, unordered_map` 39 | 40 | ## STL 반복자 41 | > iterator 42 | 43 | 포인터와 비슷한 개념으로 컨테이너의 원소를 가리키고, 가리키는 원소에 접근하여 순회가 가능한 반복자. 구체적으로 설명하자면 컨테이너에서 보유하는 내부 데이터를 순회할 수 있는 객체로 컨테이너의 특정 위치를 나타낸다. 44 | C++에서는 아직 자료를 포인터 단위로 관리하기에 해당 자료의 위치에서 데이터에 접근하고 사용하기 위해서 그 위치에 저장된 자료구조를 읽는데 **반복자**가 필요하다. 45 | > ex1) `begin()` - 컨테이너의 첫번째 위치를 가리키는 반복자를 반환 46 | 47 | > ex2) `end()` - 컨테이너의 마지막 위치를 가리키는 반복자를 반환 48 | 49 | 1. *연산자 : 지금 현재 위치의 원소를 반환 50 | 2. ++연산자 : 컨테이너의 다음 원소를 가리키는 위치로 반복자가 이동 51 | 3. --연산자 : 컨테이너의 이전 원소를 가리키는 위치로 반복자가 이동 52 | 4. = 연산자 : 반복자가 참조하는 원소의 위치를 다른 반복자에 할당 53 | 5. ==, != 연산자 : 비교 연산자 54 | 55 | ## STL 알고리즘 56 | 57 | 정렬, 삭제, 검색, 연산 등을 해결하는 일반화된 방법을 제공하는 함수 Template 58 | 59 | 1. 계수 알고리즘 60 | count(), count_if() 61 | 주어진 값과 일치하거나, 조건에 맞는 요소들의 개수를 count 62 | 63 | 2. 탐색 알고리즘 64 | search(), search_n(), find(), find_if(), find_end(), binary_search() 65 | 주어진 값과 일치하거나, 조건에 맞는 요소를 반환 66 | 67 | 3. 비교 알고리즘 68 | equal(), mismatch(), lexicographical_compare() 69 | 두개의 요소가 같은지 비교 70 | 71 | 4. 초기화 알고리즘 72 | fill(), generate() 73 | 지정된 범위의 요소를 지정한 값으로 할당 및 함수의 반환 값 할당 74 | 75 | 5. 변경 알고리즘 76 | for_each(), transform() 77 | 지정된 범위의 모든 요소에 대한 연산과 함수 적용 78 | 79 | 6. 복사 알고리즘 80 | copy() 81 | 하나의 구간을 다른 구간으로 복사 82 | 83 | 7. 삭제 알고리즘 84 | remove(), unique() 85 | 지정된 값을 가지는 요소를 삭제하거나, 중복된 요소를 삭제 86 | 87 | 8. 대치 알고리즘 88 | replace() 89 | 지정된 구간에서 요소가 지정된 값과 일치하면 대치값으로 바꿈 90 | 91 | 9. 정렬 알고리즘 92 | sort() 93 | 지정된 정렬 기준에 따라서 구간의 요소들을 정렬 94 | 95 | 10. 분할 알고리즘 96 | partition() 97 | 지정된 구간의 요소들을 조건에 따라서 두개의 집합으로 나눔 98 | 99 | **이러한 것들이 STL에 기본으로 내장된 알고리즘이라니 정말 C++ STL이 놀랍지 않은가?** 100 | 101 | ## STL 함수자 102 | 103 | STL 함수자란 함수 객체와 같은 말로, operator() 연산자를 오버로딩한 클래스 객체이다. 104 | 우리가 C++을 사용할 때 105 | `sort(v.begin(), v.end(), less());` 의 세번째 인자 less, greater를 사용했던 기억이 있는가? 106 | 이것이 바로 함수처럼 동작하는 객체인 함수자이다. 107 | 108 | 우리와 같은 C++ 개발자는 함수자를 이용한 함수를 많이 사용하지 않는다. 함수 포인터를 인자로 사용했을 경우의 단점은 반환값과 인자가 동일한 함수에 대해서만 동작하기 때문에 범용성이 떨어지기 때문이다. 109 | 110 | ### 비교 함수자 111 | 비교 연산 기능을 수행하는 함수자 112 | less(), greater(), equal_to(), greater_equal() 등이 존재한다. 113 | 114 | ### 산술 함수자 115 | 산술 연산 기능을 수행하는 함수자 116 | plus(), minus(), multiplies(), divides() 등이 존재한다. 117 | 118 | ### 논리 함수자 119 | 논리 연산 기능을 수행하는 함수자 120 | logical_and(), logical_or(), logical_not() 등이 존재한다. 121 | 122 | ### plus 함수자를 사용하는 예시 123 | ```cpp 124 | #include 125 | #include 126 | #include 127 | #include 128 | using namespace std; 129 | 130 | void main( ) 131 | { 132 | vector v1; 133 | vector v2; 134 | 135 | 136 | v1.push_back(10); 137 | 138 | v1.push_back(20); 139 | v1.push_back(30); 140 | 141 | v2.push_back(1); 142 | v2.push_back(2); 143 | v2.push_back(3); 144 | 145 | for(int i = 0 ; i < v1.size() ; i++) 146 | cout << v1[i] << " "; // 10, 20, 30 147 | cout << endl; 148 | 149 | for(int i = 0 ; i < v2.size() ; i++) 150 | cout << v2[i] << " "; // 1, 2, 3 151 | cout << endl; 152 | 153 | transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), plus()); 154 | 155 | for(int i = 0 ; i < v1.size() ; i++) 156 | cout << v1[i] << " "; // 11, 22, 33 157 | cout << endl; 158 | } 159 | ``` 160 | 161 | ## 함수 어댑터 (function adaptor) 162 | 163 | 어댑터란 컨테이너의 인터페이스 또는 함수자의 기능을 변경하는 컴포넌트를 말한다. 164 | 기존의 컨테이너를 이용해서 스택을 꾸미고 싶을 때에는 스택 어댑터를, 함수자의 의미를 변경할 때는 바인더나 부정자를 사용한다. 165 | 166 | * 바인더(binder) 167 | 두개의 인자를 필요로 하는 이항 함수 객체의 인자 하나를 고정시켜 인자를 하나만 요구하는 단항 함수 객체로 변환 168 | 169 | * 부정자(negator) 170 | 함수 인자로 사용되는 조건자의 의미를 반대로 변환 171 | 172 | * 함수 포인터 어댑터(adaptors for pointers to functions) 173 | 단항이나 이항 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도움 174 | 175 | * 멤버 함수 포인터 어댑터(adaptors for pointers to member functions) 176 | 단항이나 이항 멤버 함수 포인터를 라이브러리가 제공하는 함수 어댑터와 사용할 수 있도록 도움 177 | 178 | ### 바인더 어댑터 179 | 180 | * bind1st 181 | 함수자에 전달되는 두개의 인자 중 첫번째 인자를 고정값으로 사용할 수 있도록 도와주는 바인더 182 | 183 | * bind2nd 184 | 함수자에 전달되는 두개의 인자 중 두번째 인자를 고정값으로 사용할 수 있도록 도와주는 바인더 185 | 186 | 187 | less 함수자에 bind1st 어댑터를 씌운 예시 188 | ```cpp 189 | #include 190 | #include 191 | using namespace std; 192 | 193 | int main(void){ 194 | // 첫 인자를 10으로 고정 195 | binder1st> binder = bind1st (less(), 10); 196 | 197 | cout << binder(5) << ':' << less()(10, 5) << endl; 198 | cout << binder(10) << ':' << less()(10, 10) << endl; 199 | cout << binder(20) << ':' << less()(10, 20) << endl; 200 | 201 | cout << "== ==" << endl; 202 | 203 | // 임시 객체 사용 204 | cout << bind1st(less(), 10) (5) << ':' << less()(10, 5) << endl; 205 | cout << bind1st(less(), 10) (10) << ':' << less()(10, 10) << endl; 206 | cout << bind1st(less(), 10) (20) << ':' << less()(10, 20) << endl; 207 | 208 | } 209 | ``` 210 | 실행 결과 211 | ```cpp 212 | 0:0 213 | 0:0 214 | 1:1 215 | == == 216 | 0:0 217 | 0:0 218 | 1:1 219 | ``` 220 | 221 | ### 부정자 어댑터 222 | 223 | [ref](http://www.cplusplus.com/reference/functional/not1/) 224 | 225 | * not1 226 | 인자로 전달된 단항 조건자의 의미를 반대로 변환 227 | 228 | > 5의 배수인지 확인하는 isMulti5 함수를 만들었다고 가정했을 때 not1(isMulti5()); 을 사용하면 5의 배수가 아닌것만 확인하게 된다. 229 | 230 | * not2 231 | 인자로 전달된 이항 조건자의 의미를 반대로 변환 232 | 233 | > ex) `sort(v.begin(), v.end(), compare());`을 오름차순이라고 가정했을 때 `sort(v.begin(), v.end(), not2(compare())); 은 내림차순이 된다. 234 | 235 | ### 함수 포인터 어댑터 236 | 함수 포인터는 객체가 아니므로 어댑터를 적용할 수 없는데 아래의 함수 포인터 어댑터를 사용함으로써 함수 포인터를 객체로 포장하게 되고 이 후 바인더, 부정자 등의 어댑터를 적용할 수 있다. 237 | 238 | * ptr_fun 239 | 단항 함수를 함수 어댑터처럼 사용할 수 있도록 지원 240 | 241 | ```cpp 242 | bool IsMultiFunc(int a,int b) 243 | { 244 | return (a % b == 0); 245 | } 246 | void main(){ 247 | int ari[]={1,2,3,4,5,6,7,8,9,10}; 248 | vector vi(&ari[0],&ari[10]); 249 | vector::iterator it; 250 | for (it=vi.begin();;it++) { 251 | it=find_if(it, vi.end(), bind2nd(ptr_fun(IsMultiFunc),3)); 252 | if (it==vi.end()) break; 253 | cout << *it << "이(가) 있다" << endl; 254 | } 255 | } 256 | ``` 257 | -------------------------------------------------------------------------------- /contents/language/c++/moderncpp.md: -------------------------------------------------------------------------------- 1 | # Modern C++ 2 | 3 | > 작성자 : [박재용](https://github.com/ggjae) 4 | 5 | 본 자료는 작성자인 '박재용'의 개인 Repository의 글과 Effective Modern C++ 책을 재구성하여 작성되었습니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [What is Modern C++?](#Modern-C++이란?) 11 | - [Auto Type](#Auto) 12 | - [Smart Pointer](#Smart-Pointer) 13 | - [람다 표현식](#lambda) 14 | - [이동 시맨틱](#Move-Semantics) 15 | - [다양한 라이브러리](#다양한-Library) 16 | 17 |
18 | 19 | --- 20 | 21 | ## Modern C++이란? 22 | 23 | C++98(C++03) 버전 이후 최신 유행에 맞게 새로운 C++ 표준 문서가 업데이트되었고 구 버전의 C++98 기능보다 성능을 향상시킬뿐만 아니라 가독성, 코드의 간결함도 장점으로 떠오르는 새로운 C++ 기능 집합 표준화이다. 24 | 25 | ## Auto 26 | 27 | Auto를 사용하여 변수를 선언하게 되면 특정 자료형을 지정하지 않을 수 있다. auto는 코드가 짧아지고 가독성에서 큰 장점을 가지고 있다. 28 | 중요한 점 : **전역변수나 함수의 매개변수로는 사용하지 못함** 29 | ```cpp 30 | auto a = 100; 31 | auto b = 100l; 32 | auto c; // ERROR 33 | 34 | vector v; 35 | auto be = v.begin(); // 이처럼 iterator형을 대신할 수 있다 36 | ``` 37 | 38 | 39 | ## Iterator와 Auto를 결합하기 40 | 41 | 반복문에서 STL의 iterator를 유용하게 사용할 수 있다. auto 키워드와 함께 사용한다면 반복문이 얼마나 간결해지는지 살펴보자. 42 | 43 | ```cpp 44 | for(vector::iterator i = v.begin(); i != v.end(); ++i){ 45 | // sth; 46 | } 47 | 48 | for(auto i = v.begin(); i != v.end(); ++i){ 49 | // sth; 50 | } 51 | 52 | for(auto& i : v){ 53 | // sth; 54 | } 55 | ``` 56 | 57 | 위의 세 for문의 동작은 같고, 코드의 길이가 확연하게 줄어드는 것을 확인할 수 있었다. 마지막 반복문의 v는 배열이나 vector가 될 수 있고 **참조변수**로 사용함으로서 복사가 발생하지 않아 메모리의 측면에서 성능이 좋다고 할 수 있다. 58 | 59 | ## 후행 반환 형식 60 | 61 | 후행 반환 형식이란 함수의 반환 형식을 기존의 리턴타입의 위치가 아니라 매개변수 목록 다음에 '->'을 사용하며 반환값을 auto로 지정한 경우에는 C++11에서는 후행 반환 형식이 필수였지만, C++14부터는 반환 타입을 추론해 준다. 하지만 개발자가 의도한 바와 다르게 반환 형식이 결정될 수 있는 문제를 신경써야 한다. 62 | 63 | ```cpp 64 | auto function(int a, int b) -> decltype(a+b){ 65 | return a+b; 66 | } 67 | ``` 68 | 69 | ## Smart Pointer 70 | 71 | 일반 포인터처럼 생겼지만 스마트 포인터란 무엇일까? 72 | 일반 포인터보다 똑똑하다는 것은 프로그램에서 가장 빈번히 발생하는 버그에 관련되어 있다. 버그를 줄일 수 있다는 점에서 **Smart**한 것이다. 기존 C++에서는 동적으로 할당받은 메모리를 반드시 delete를 이용하여 메모리 해제가 필수임에 따라 Modern C++에서는 Memory Leak, Segmentation fault로부터 안전성을 보장하기 위하여 Modern C++에서는 스마트 포인터를 제공한다. 73 | 74 | 가장 보편적으로 사용되는 예시로는 auto_ptr이 스마트 포인터의 예시로 가장 많이 사용되었지만 소멸동작 방식에서 객체에 대한 delete를 수행하기보다는 객체의 해제만 수행하는 치명적인 단점을 가지고 있어 C++17부터 제거 되었다. 이 문제점에 의해 unique_ptr와 shared_ptr을 주로 사용하게 되었다. 75 | 76 | ```cpp 77 | // 기존 C/C++ pointer 78 | Widget *getWidget(); 79 | 80 | void work() 81 | { 82 | Widget *wptr = getWidget(); 83 | // 여기서 Exception 발생 혹은 Return하게 될 경우, *wptr 메모리 해제는 어쩌죠? 84 | delete wptr; // 직접 명시적으로 메모리를 해제해야 함. 85 | 86 | } 87 | // 모던 C++의 unique_ptr(스마트 포인터의 일종) 88 | unique_ptr getWidget(); 89 | void work() 90 | { 91 | unique_ptr upw = getWidget(); 92 | // Exception이 발생하거나 중간에 Return 하더라도 별도로 신경 쓸 필요가 없습니다! 93 | } // 메모리도 알아서 자동으로 해제됩니다. 94 | ``` 95 | 코드 ref) https://www.hanbit.co.kr/media/channel/view.html?cms_code=CMS8140000918 96 | 97 | ## lambda 98 | 99 | 람다표현식(lambda expression)은 이름 없는 함수로도 불리는데 python, C#, js등에서 이미 편리하게 사용되었던 기능들이다. 제일 큰 장점으로는 함수의 인라인화가 가능하다는 것에 있다. 100 | 101 | 람다표현식이란 어떤것인지 코드를 보고 이해해보자. (in Effective Modern C++ Book) 102 | 103 | ```cpp 104 | std::find_if(container.begin(), container.end(), 105 | [](int val) { return 0 < val && val < 10; }); 106 | ``` 107 | 108 | 109 | ### 문법 110 | 111 | ```cpp 112 | [captures](parameters) -> return type { body } 113 | 114 | /* 115 | * captures: comma(,)로 구분된 캡처들이 들어갑니다. 캡쳐 부가 설명을 아래에 작성했습니다. 116 | * [] : 아무 외부변수도 사용하지 않음 117 | * [=] : 모든 외부변수의 값을 전달받아서 캡쳐한다. value 만 사용하고, 외부변수의 값을 바꾸지는 못함. 118 | * [&] : 모든 외부변수를 참조로 전달받아서 캡쳐한다. value도 사용하고, 외부변수의 값도 바꿀 수 있다. 119 | * [A] : 외부변수 A를 값으로 전달받아서 캡쳐한다. 단 A의 value는 사용할 수 있으나 값을 바꿀 수 없다. 120 | * [&A] : 외부변수 A를 참조로 전달받아서 캡쳐한다. value도 사용하고, 값도 바꿀 수 있다. 121 | * parameters: 함수의 인자들이 들어갑니다. 122 | * return type: 함수의 반환형입니다. 123 | * body: 함수의 몸통입니다. 124 | */ 125 | ``` 126 | 127 | ### 다양한 사용 예시 128 | 기본 사용법 129 | ```cpp 130 | int main(){ 131 | auto a = 5; 132 | [&](){a = 3;cout< func = [&](){a = 3;cout< func = [&func] (int num) -> int 167 | { 168 | if(num <= 1)return 1; 169 | else return num*func(num-1); 170 | }; 171 | auto a = func(5); 172 | cout<(num1) // 값으로 캐스팅 lvalue 222 | static_cast(num1) // rvalue 참조로 캐스팅 xvalue 223 | } 224 | ``` 225 | 226 | ## Move Semantics의 예시 227 | 228 | str1와 str2를 swap해야 하는 과정을 거칠 때 우리는 임시 변수(string tmp)를 만들어 임시 변수를 이용하여 값을 교체하는 과정을 거쳐왔다. 하지만 이 과정에서 복사가 세번 일어나게 된다. 우리는 굳이 문자열 내용을 복사할 필요 없이 string_content의 주소값만 서로 바꿔주면 되는것을 알고 있다. 좌측값이 우측값으로 취급될 수 있게 바꿔주는 함수가 없을까? 229 | std::move를 사용하자! 230 | 231 | **Move Semantics** 232 | ```cpp 233 | #include 234 | #include 235 | 236 | class string 237 | { 238 | char* data; 239 | 240 | public: 241 | 242 | string(const char* p) 243 | { 244 | size_t size = std::strlen(p) + 1; 245 | data = new char[size]; 246 | std::memcpy(data, p, size); 247 | } 248 | ``` 249 | 250 | Move Semantics에 대한 자세한 정보는 [microsoft 공식 문서](https://docs.microsoft.com/ko-kr/cpp/cpp/move-constructors-and-move-assignment-operators-cpp?view=msvc-160), [stackoverflow](https://stackoverflow.com/questions/3106110/what-is-move-semantics)을 참조하면 더 많은 정보를 얻을 수 있다. 251 | 252 | ## Array 253 | 254 | Modern C++에서는 C/C++의 기본 문법이였던 배열을 STL로 지원해준다. 255 | 기존 STL을 사용할 때 우리는 Vector을 많이 사용해왔고 사실 Vector는 생성과 소멸에 드는 비용이 꽤 크고, 한 벡터의 객체는 4byte로 메모리를 비효율적으로 사용할 수 있다. 256 | 257 | ## nullptr 258 | 259 | C++11부터 추가된 키워드로 널포인터를 뜻한다. 260 | nullptr은 포인터만을 위한 NULL상수이다. Modern C++이전까지는 널포인터를 나타내기 위해 상수 '0'을 사용했는데 함수 인자로 넘기게 되었을 때 정수형으로 추론되는 경우가 있어 문제가 발생하였고 이 문제를 해결하기 위하여 nullptr이 등장했다. 261 | 262 | ## 다양한 Library 263 | 264 | ### Thread Library 265 | 기존 C/C++에서는 스레드를 사용할 때 Window는 Win32 API, 리눅스는 pthread를 사용하여야만 했다. Modern C++에서는 스레드에 관한 라이브러리가 표준으로 포함되어 스레드를 다루기에 매우 편리해졌다. 266 | 267 | ### Regex Library 268 | 정규식은 Modern C++에서 표준 라이브러리에 포함되었다. std::regex 등을 사용하여 정규식을 사용할 수 있다. 269 | -------------------------------------------------------------------------------- /contents/language/c++/multithread-programming.md: -------------------------------------------------------------------------------- 1 | # Multithread Programming 2 | 3 | > 작성자 : [박재용](https://github.com/ggjae) 4 | 5 | 본 자료는 작성자인 '박재용'의 개인 Repository의 글을 재구성하여 작성되었습니다. 6 | 자료를 공부하기 이전에 익혀두어야 하는 두가지 기술을 먼저 이야기하겠습니다. 7 | 8 | - 프로세스와 스레드가 무엇인지 이해하기 9 | - 스레드와 프로세스를 구분해보기 10 | 11 |
12 | Table of Contents 13 | 14 | - [프로세스 Review](#프로세스) 15 | - [스레드 Review](#스레드) 16 | - [멀티스레드 프로그래밍의 사용 이유](#멀티스레드-프로그래밍의-사용-이유) 17 | - [멀티스레드 프로그래밍이 힘든 이유](#멀티스레드-프로그래밍이-힘든-이유) 18 | - [병렬 프로그램](#병렬-프로그램) 19 | 20 |
21 | 22 | --- 23 | 24 | ## 프로세스 25 | 26 | 운영체제가 관리하는 프로그램의 단위 27 | 실행파일을 실행하는 것이 곧 프로세스를 만드는 것. 실행파일의 실행은 운영체제가 파일을 읽어서 메모리에 복사해두고 시작 주소로 점프하는 것. 멀티코어가 아니더라도 여러개의 프로그램이 동시에 실행된다. 28 | 29 | 프로세스의 메모리 구조 30 | 31 | - Code : 실행될 명령어가 들어가는 구역 32 | - Data : 전역 변수가 들어가는 구역 33 | - Stack : 지역변수와 함수 리턴 주소가 들어가는 구역 34 | - Heap : malloc이나 New로 할당받은 메모리가 들어가는 구역 35 | - PCB : Process Control Block 36 | 37 | 프로세스는 내부적으로 성격에 따라 여러개의 구역으로 나누어서 관리를 하는 것을 **세그먼트**라 한다. 38 | 39 | ## 스레드 40 | 41 | 프로그램 내에서의 실행되는 흐름의 단위 42 | 프로세스의 부분집합이다. 모든 스레드는 자신 고유의 스택을 가지고 있고 Data, Heap, Code 영역은 다른 스레드와 공유하게 된다. 43 | 44 | 한 프로그램에서의 여러개의 스레드를 사용한다면 코드와 데이터 힙 영역은 스레드간 공유가 된다. 똑같은 메모리를 같이 사용하고 한 장소에 있는 것을 자식과 부모가 같이 사용한다고 생각하면 된다. 45 | 46 | --- 47 | 48 | ## 프로세스와 스레드 관련 49 | 50 | - 스택영역만 새로 하나 파주면 되기 때문에 스레드는 생성 시 overhead가 프로세스보다 작다. 51 | - 캐시 미스가 비교적 적게 일어나기 때문에 스레드는 Context Switching 비용의 overhead가 프로세스보다 적다. 52 | - 스레드간의 통신이 프로세스간의 통신보다 간단하다. 프로세스간의 통신은 overhead가 큼 53 | - 하나의 스레드에서 발생한 문제가 전체 프로세스를 멈추게 한다. 54 | - 하나의 프로그램에서 여러군데가 동시에 실행되므로 스레드의 경우 디버깅이 너무나도 어렵다. 55 | 56 | 57 | # 멀티스레드 프로그래밍의 사용 이유 58 | 59 | - 멀티코어 CPU에서의 프로그램 성능 향상을 위하여 60 | - 멀티 CPU 컴퓨터, 병렬컴퓨터에서의 프로그램 성능 향상을 위하여 61 | 62 | 63 | 하나의 프로그램을 더 빠르게 돌리기 위해서 사용한다. 64 | 10 FPS 게임을 20 FPS로 올리고 싶을 때, 처리량을 높이기 위하여 **멀티스레드** 프로그래밍을 한다. 65 | 66 | 67 | ## 멀티스레드 프로그래밍이 뭐야? 68 | 69 | - 병렬 프로그래밍의 유일한 구현 수단 70 | - 하나의 프로세스 안에서 여러개의 스레드를 실행시켜 병렬성을 얻는 프로그래밍 방법 71 | 72 | 73 | ## 성능을 위하여 멀티스레드 프로그래밍을 하는가? 74 | 75 | 정답은 아니다. 게임은 성능이 중요하기 때문에 상관은 있지만 게임이 느리거나, 프로그램이 느리다면 성능 개선이 우선시 되어야 한다. 다양한 자료구조와 알고리즘을 통해서 성능개선을 진행해야 하고 최후의 보루로 멀티스레드 프로그래밍을 진행하여야 한다. 76 | **주의** 멀티스레드 프로그래밍을 하면 더 느려질지도 모른다. 77 | 78 | ## 멀티스레드 프로그래밍이 힘든 이유 79 | 80 | 매번 같은 코드를 실행함에도 결과값이 달라진다. 우리가 프로그래밍해서 잘못된 결과면 계속 같은 잘못된 결과가 나와야 하는데 멀티스레드 프로그래밍은 값이 매번 다르다. 이것은 잘못 만든 프로그램으로 디버깅이 쉽지 않고 Data Race에 부딪힐 수 있다. 81 | Data Race란 같은 데이터를 두개의 스레드가 동시에 읽고 쓰고 할 때에 모든 전역 변수는 공유 메모리이므로 읽고 쓰는 순서에 따라 실행 결과가 달라지고 이 상태를 경쟁 상태라고 한다. 하지만 C++11에서 lock과 unlock을 지원해주므로 복수개의 스레드가 동시에 접근할 수 없도록 코딩을 진행하여야 한다. 82 | 83 | ``` CPP 84 | #include 85 | #include 86 | #include 87 | 88 | std::mutex mtx_lock; 89 | 90 | int main(){ 91 | 92 | std::thread Threads1([&] (){ 93 | for(int i=0;i<5;++i){ 94 | mtx_lock.lock(); 95 | std::cout << "Thread Num : " << i << std::endl; 96 | mtx_lock.unlock(); 97 | } 98 | }); 99 | 100 | std::thread Threads2; 101 | Threads2 = std::thread([&](){ 102 | for(int i=10;i<15;++i){ 103 | mtx_lock.lock(); 104 | std::cout << "Thread Num : " << i << std::endl; 105 | mtx_lock.unlock(); 106 | } 107 | } 108 | return 0; 109 | } 110 | ``` 111 | 112 | 멀티스레드 프로그래밍 시 mutex 객체는 전역 변수로 코딩하여야 하고 같은 객체 사이에서만 lock과 unlock이 동작하는 것을 주의하여야 한다. 서로 동시에 실행되어도 괜찮은 Critical Section (lot reader, 0 writer)에서는 다른 mutex 객체로 보호하는것이 성능에 좋다. 113 | 114 | ## 멀티코어 CPU 115 | 116 | **한 개 이상의 코어**로 구성된 CPU. i3, i5, i7 117 | 2021년 현재는 모든 것이 다 멀티코어 ex) Xbox, 닌텐도 스위치 118 | 119 | 120 | ## C++ 언어에 멀티스레드 라이브러리가 표준으로 존재 121 | 122 | 과거의 멀티스레드 프로그래밍 방법: 123 | Window에서는 Win32 라이브러리에서 지원되는 API 사용했었고, 윈도우는 멀티스레드에 특화된 OS이고 리눅스는 pthread API를 사용하면 가능했다. 124 | 125 | Windows에서의 thread => 126 | 프로세스의 하위 개념 프로세스는 처음 시작 시 한개의 스레드를 갖고 시작되고 운영체제가 직접 스케쥴링하게 된다. 127 | 128 | --- 129 | 130 | ## 병렬 프로그램 131 | 132 | 병렬 프로그램의 특징 133 | - 실행된 프로세스 내부의 여러곳이 동시에 실행된다. 134 | - 병렬로 실행되는 객체 사이의 동기화가 필수이다. (synchronization) 135 | - **공유메모리 모델**과 메세지패싱 모델이 있다. 136 | 137 | ## 병렬 프로그램의 요구사항 138 | 139 | - 정확성 : 다양한 흐름에서 동시다발적으로 호출해도 문제 없이 실행되는 알고리즘이 필요 140 | - 성능 : Context 증가에 따른 성능 향상이 높아야 함 141 | 142 | --- 143 | 144 | ### 자주 묻는 질문 145 | 146 | 1. **멀티코어 프로세서**를 만드는 이유? 147 | 148 | > CPU의 성능을 올려야하고, 클럭 속도를 높여야 한다. 하지만 클럭 속도를 높일 수 없음. 한계가 정해져 있다. => 클럭 속도가 4GHz가 되면 컴퓨터가 불에 타게 된다. 우주가 그렇게 설계되어 있음. 149 | 4Ghz의 벽이라고도 한다. 사용하려면 실시간으로 액체질소를 들이부어야 함. 남은 것은 멀티코어 뿐이였고, 싱글로는 도저히 안되니까 코어 개수로 싸우고 있다. 듀얼코어 CPU 2개, 쿼드코어 CPU 1개 차이는 크지 않다. 여러 코어가 늦게 개발된 이유는 프로그램을 다시 짜야하는 번거로움뿐만 아니라 디버깅도 어렵고 이미 작성한 알고리즘도 사용하지 못하기 때문이다. 150 | 151 | 2. 스레드의 개수와 코어의 개수는 일치하지 않아도 상관이 없는 이유? 152 | 153 | > 프로세스가 시분할로 돌아가면서 실행되듯 스레드도 시분할로 운영되어 중간에 다른 스레드가 실행될 수 있다. 쉽게 코어의 개수는 CPU 내 물리적 연산부의 개수이고, 스레드는 **작업단위**이므로 일치하지 않아도 된다. 154 | 155 | 3. 멀티스레드 프로그래밍을 진행했을 때 싱글스레드 프로그래밍보다 느려졌다. 그 이유는 무엇일까? 156 | > 사용하는 스레드가 많아지면 많아질수록 bottleneck의 가능성과 쓰레드마다 참조하는 캐시가 다르므로 불일치 문제가 생기거나 스레드 하나만으로도 진행할 수 있는 간단한 코드를 여러 스레드로 진행시켜도 race condition을 기다리기 위해 시간이 사용되곤 한다. 적재적소에 사용하여야 한다. 157 | -------------------------------------------------------------------------------- /contents/language/coroutine.md: -------------------------------------------------------------------------------- 1 | # Coroutine 2 | 3 | > 작성자 : [박재용](https://github.com/ggjae) 4 | 5 | 본 자료는 작성자인 '박재용'의 팀 Repository의 글과 유니티 교과서 책을 재구성하여 작성되었습니다. 다양한 언어(javascript, C++, Kotlin, C#)에서 코루틴을 사용하지만 예제는 C#에 국한된 설명으로 코루틴에 대한 이해를 도와줍니다. 6 | 7 |
8 | Table of Contents 9 | 10 | - [What is Coroutine](#Coroutine이란?) 11 | - [코루틴이 언제 쓰이는데?](#Coroutine이-언제-쓰이는데) 12 | - [코루틴이 주는 이점](#코루틴이-주는-이점) 13 | 14 |
15 | 16 | --- 17 | 18 | ## Coroutine이란? 19 | 20 | 코루틴(Coroutine)은 Cooperative routine을 의미하며 서로 협력하는 루틴이라는 뜻이다. 메인 루틴과 서브 루틴처럼 종속된 관계가 아닌 서로 대등한 관계이다. 글을 전부 정독한 후 다시 올라왔을 때는 충분히 이해가 될 것이다. 21 | 22 | ![image](https://user-images.githubusercontent.com/22047551/128828164-fbf664c0-600c-42c7-b832-9f1b35c88709.png) 23 | 24 | 25 | ## Coroutine이 언제 쓰이는데? 26 | 27 | 직접 누구나 이해할 수 있는 코드로 예시를 들어보겠다. 28 | 예시1) 29 | ```cs 30 | void start(){ 31 | startA(); 32 | startB(); 33 | } 34 | void startA(){ 35 | for(int a=0;a<10;a++){ 36 | Debug.Log("a"); 37 | } 38 | } 39 | void startB(){ 40 | for(int b=0;b<5;b++){ 41 | Debug.Log("b"); 42 | } 43 | } 44 | ``` 45 | 프로그램은 startA의 a가 열번, startB의 b가 다섯번 출력될 것이다. 46 | 47 | 위와는 다르게 **동시에 일어나는 것처럼 프로그램을 구현**하고 싶거나 **약속된 신호를 줬을 때 시작하는 비동기를 구현**할 때 코루틴을 사용한다. 48 | 49 | 예시2) 50 | 코루틴의 공식문서에서는 한 오브젝트의 투명도를 완전히 투명해질 때까지 Fadeout시킬 때 코루틴을 사용하지 않으면 중간값은 시각적으로 알 수 없고 순간적으로 투명해지므로 코루틴을 이용하여 투명도를 설정할 것을 권장하고 있다. 아래 코드는 순식간에 투명도가 0으로 변해버린다. 51 | ```cs 52 | void Fade() { 53 | for (float f = 1f; f >= 0; f -= 0.1f) { 54 | Color c = renderer.material.color; 55 | c.a = f; 56 | renderer.material.color = c; 57 | } 58 | } 59 | ``` 60 | 61 | 아래 코드는 코루틴을 적용시킨 코드이다. 정해진 시간이나 프레임마다 투명도를 서서히 줄게 자신이 속도를 컨트롤할 수 있게 된다. 62 | ```cs 63 | IEnumerator Fade() { 64 | for (float f = 1f; f >= 0; f -= 0.1f) { 65 | Color c = renderer.material.color; 66 | c.a = f; 67 | renderer.material.color = c; 68 | yield return null; 69 | } 70 | } 71 | ``` 72 | 73 | ## 코루틴이 주는 이점 74 | 75 | 결론만 말하자면 코루틴을 협력형 멀티태스킹(cooperative multitasking)을 구현하는 용도로 사용하기 때문에 코루틴이 주는 이점이 많다고 이야기 할 수 있다. 76 | 77 | **협력형 멀티태스킹**은 일종의 **시분할 - time sharing** 방식으로 여러 task가 하나의 CPU를 나눠쓰는 방식인데 운영체제의 개입 없이 각 task가 **독점적으로 CPU를 사용하고 사용이 끝나면 자발적으로 양보**하게 된다. 리소스를 사용하는 도중 강제로 CPU를 선점해가는 일이 없으므로 크리티컬 섹션을 보호하기 위한 동기화 수단이 필요 없다는것이 가장 큰 이점이다. 78 | 79 | --- 80 | 81 | ## 코루틴과 일반함수의 차이점 82 | 83 | 일반적인 함수의 진입점은 함수의 시작 부분이다. 함수를 호출하게 되면 함수의 시작 부분부터 return이나 함수의 끝까지 실행을 한 후 함수를 종료한다. Coroutine은 여러개의 진입점과 여러개의 중단점을 가져 루틴이 종료되기 전에 몇번이든 진입과 중단이 가능하다. 84 | 85 | 또한 위에서 언급한 것과 같이 함수는 return을 사용하지만 코루틴은 suspend(중단)을 사용하여 힙 메모리 영역에 다시 resume(재개)하기 위해 필요한 모든 정보를 저장하고 제어권을 호출자에게 다시 넘기게 된다. 86 | 87 | ## 코루틴과 스레드의 차이점 88 | 89 | Background Task라는 점과 **비동기**로 일어나는 것처럼 보이는 부분에서는 공통점이 있어 보이지만 개념이 다르다. 코루틴은 특정 작업을 단계적으로 쪼개어 순차적으로 발생하게 만들고 메인 루틴과 **동시에 발생하지 않는다**는 큰 특징을 가지고 있다. 또한 가장 큰 차이점은 실제로 코루틴은 **병렬적으로 수행**되는 것이 아닌 **작업을 잘게 쪼개서 동시에 일어나는것처럼 보이게하는 멀티태스킹**과 비슷한 방법을 사용한다. 하지만 스레드의 경우는 **병렬 처리**를 중심으로 **하나의 프로그램에서 동시에 많은 일을 처리하게 된다.** 아래의 사진에서 더 이해를 도와줄것이다. 90 | 91 | ![image](https://user-images.githubusercontent.com/22047551/129317126-ef247969-807b-4034-8c60-9b14b3a3e327.png) 92 | 93 | ## Coroutine의 사용 방법 94 | 95 | StartCoroutine(함수이름())으로 사용할 수 있다. 96 | 위의 코드에 StartCoroutine을 아래의 코드처럼 적용시킬 수 있다. 97 | 98 | > C# 99 | ```cs 100 | void start(){ 101 | StartCoroutine(startA()); 102 | StartCoroutine(startB()); 103 | } 104 | IEnumerator startA(){ 105 | for(int a=0;a<10;a++){ 106 | Debug.Log("a"); 107 | yield return null; 108 | } 109 | } 110 | IEnumerator startB(){ 111 | for(int b=0;b<5;b++){ 112 | Debug.Log("b"); 113 | yield return null; 114 | } 115 | } 116 | ``` 117 | 118 | ## IEnumerator란? 119 | 120 | 데이터의 목록을 하나씩 넘겨줄 때 사용되는 인터페이스로, 코루틴은 호출한 함수와 서로 상호작용하며 진행하도록 설계되어 있다. 호출한 함수에게 데이터를 넘겨주고 쉰 후 데이터를 받아야 할 때 쉬고 있던 코루틴이 일어나서 다시 데이터를 받고, 넘겨주는 반복하는 구조로 동작하게 된다. 121 | 122 | ## yield란? 123 | 프로그래밍 언어마다 해석의 차이점이 있을 수 있지만 C#의 yield 키워드는 호출자(Caller)에게 컬렉션 데이타를 하나씩 리턴할 때 사용한다. 124 | 흔히 Enumerator(Iterator)라고 불리우는 이러한 기능은 집합적인 데이터셋으로부터 데이타를 하나씩 호출자에게 보내주는 역할을 한다. C++의 iterator(반복자)와 같은 개념이다. 125 | 126 | ## 다양한 yield return type 127 | 128 | - yield return null 129 | > 다음 프레임까지 대기 130 | 131 | - yield return new WaitForSeconds(time) 132 | > time초 만큼 대기 133 | 134 | - yield return new WaitForFixedUpdate() 135 | > 다음 FixedUpdate까지 대기 136 | 137 | - yield return StartCoroutine(string name) 138 | > 다른 코루틴이 중단될때까지 대기 139 | -------------------------------------------------------------------------------- /contents/language/java/1.md: -------------------------------------------------------------------------------- 1 | # 자바 언어의 구조와 기본 문법 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 |
6 | Table of Contents 7 | 8 | - [Java 언어의 탄생 배경](#java-언어의-탄생-배경) 9 | - [Java의 10가지 특징](#java의-10가지-특징) 10 | - [Java 플랫폼](#java-플랫폼) 11 | - [Java 프로그램 구조](#java-프로그램-구조) 12 | - [Java 데이터 타입과 변수](#java-데이터-타입과-변수) 13 | - [Java 연산자](#java-연산자) 14 | - [Java의 배열](#java의-배열) 15 | - [Java 제어문](#java-제어문) 16 | 17 |
18 | 19 | ## Java 언어의 탄생 배경 20 | 21 | ### 썬마이크로시스템즈의 Green Project에서 시작 22 | 23 | Green Project에서는 가정용 전자기기의 통합적 제어를 위해, 전자기기에서 사용되는 작은 컴퓨터 언어를 제안했다. 가정용 전자기기에 사용되는 power나 memory가 열악했기 때문에 여기에 사용되는 언어는 작고 견고하고, 신뢰할 수 있는 코드를 필요로 했다. 또한 다른 CPU가 선택될 수 있기 때문에 특정 디바이스에 종속되면 안 되었다. 따라서 **Virtual Machine에서 동작하는 중간 코드를 생성하여 이식성이 높은 언어를 디자인**하게 되었다. 24 | 25 | 초기에는 절차 지향 + 객체 지향 언어인 C++ 기반으로 개발되었다. 하지만 C++ 언어가 가진 한계에 부딪혀 **완벽한 객체 지향 언어**인 Oak 언어를 개발하였고, 이미 존재하는 이름이라 추후 **Java**로 이름이 바뀌게 되었다. 26 | 27 | 그 후 Java를 기반으로 한 HotJava라는 웹 브라우저를 제작하였는데, Java의 Applet 이라는 기능을 보여주기 위해 브라우저가 중간 코드(Bytecode)를 해석할 수 있도록 만들었다. 이 때 자바 붐이 일어나 Java 언어가 급격히 확산되었다. 이후 많은 업체들이 Java를 지원하기 시작하였고, 1996년 자바 2 플랫폼이라고 명명되었던 자바 버전 1.2가 출시되었다. 그리고 현재까지 많은 버전들이 출시되고 있다. 28 | 29 | ## Java의 10가지 특징 30 | 31 | 1. **단순 (Simple)** 32 | C(절차 지향) + C++(객체 지향) 언어를 기초로 하지만, C언어의 복잡한 기능을 제외하여 코드를 단순하게 작성할 수 있다. 특히 가비지 컬렉터(Garbage Collector)에 의한 자동 메모리 관리로 할당된 메모리 해제를 신경쓰지 않아도 된다. 33 | 2. **객체지향 (Object-Oriented)** 34 | 자바는 객체 지향 언어로, 언어라는 객체를 사용해 프로그램을 작성하도록 한다. 이로써 **재사용성(Reusability)** 을 높일 수 있다. 코드의 재사용, 객체의 재사용 뿐만 아니라 수많은 클래스 라이브러리들을 API로 제공함으로서 재사용성을 높이고 있다. 35 | 3. **분산 처리 (Distributed)** 36 | Java는 분산 환경에서 TCP/IP 등의 프로토콜을 통해 효율적으로 실행할 수 있도록 설계된 언어이다. Java는 다음을 지원한다. 37 | - TCP/IP 네트워크 기능 내장 38 | - HTTP, FTP 등과 같은 프로토콜에 대한 라이브러리 제공 39 | - 서로 다른 컴퓨터 상의 객체들도 원격으로 호출하여 실행할 수 있는 원격 메서드 호출과 관련된 RMI(Remote Method Invocation) 기능의 라이브러리 제공 40 | 4. **인터프리터 (Interpreter)** 41 | 프로그래밍 언어는 인터프리터 언어, 컴파일 언어로 나뉜다. 자바는 **컴파일 언어인 동시에 인터프리터 언어**이며, 인터프리터에 의해 실행된다. 자바 프로그램은 다음과 같은 실행 과정을 거친다. 42 | - 소스코드(.java) ▶︎ **컴파일(Compile): javac** ▶︎ 중간코드(.class): 바이트 코드, 클래스 파일 ▶︎ **기계어로 해석(Interprete): java** ▶︎ 실행 43 | 5. **견고 (Robust)** 44 | 다양한 플랫폼(컴퓨터) 상에서 실행되기 위해서는 **높은 신뢰성**이 중요하다. 자바는 다음과 같은 기능들을 지원함으로서 높은 신뢰성을 구현하였다. 45 | - 포인터를 사용하지 않음 46 | - 가비지 컬렉션(Garbage Collector) 기능 47 | - 엄격한 데이터 타입의 검사 : 에러 조기 발견 48 | - Runtime 에러 처리 49 | 6. **안전 (Secure)** 50 | 컴파일 시 엄격한 검사를 통해 프로그램 실행 시 발생할 수 있는 장애를 미리 방지한다. 소스코드가 컴파일된 .class 형태의 바이트코드는 **클래스 로더**, **바이트 코드 검증기**를 거친다. 이 때 문제가 없으면 인터프리터로 넘겨 실행하게 된다. 51 | - 클래스 로더 : 코드가 자신/다른 컴퓨터에서 온 것인지 판단하고 **코드를 분리**한다. 52 | - 바이트 코드 검증기 : **코드를 검증**하여 문제가 없을 시 인터프리터로 넘긴다. 53 | 7. **플랫폼 독립적 (Architecture Neutral)** 54 | 운영체제, CPU 등의 **하드웨어 사양에 관계 없이 실행**될 수 있다. 작성된 자바 프로프램은 자바 컴파일러를 거쳐 자바 가상 머신에서 기계어로 번역되게 된다. 번역된 기계어 코드는 하드웨어 사양에 관계 없이 동일하게 실행된다. 55 | 8. **높은 성능 (High Performance)** 56 | **가비지 컬렉션(Garbage Collection) 기능**이 추가되어 있어 자동으로 메모리 관리가 가능하다. 이는 메모리가 할당된 객체의 상태를 추적하여 더 이상 참조되지 않으면 자동으로 할당된 메모리를 해제하여 불필요한 메모리 낭비를 방지하는 기능이다. 57 | 9. **멀티스레드 (Multithread)** 58 | 자바는 멀티스레드를 지원한다. 이는 한 번에 여러 개의 스레드가 동시에 수행되는 과정으로, 하나의 CPU가 여러 프로그램을 동시에 수행하도록 함으로서 수행 속도를 빠르게 한다. 59 | 10. **동적 (Dynamic)** 60 | 자바는 변화되는 환경에 잘 적응하도록 설계된 언어이다. 따라서 기존의 프로그램에 영향을 주지 않고 라이브러리에 새로운 메서드나 속성들을 추가할 수 있으며, 프로그램 간 라이브러리의 연결을 Runtime에 수행함으로써 라이브러리의 변화를 곧바로 적용할 수 있다. 61 | 62 | ## Java 플랫폼 63 | 64 | 플랫폼(Platform)이란, 프로그램이 실행될 수 있는 HW 및 SW 환경을 의미한다. 65 | 66 | 일반적인 플랫폼은 하드웨어 및 하드웨어를 제어하는 운영체제로 구성되어 있지만, 자바 플랫폼은 소프트웨어만으로 구성된다. 자바 플랫폼은 일반적으로 JDK(Java Development Kit)라는 툴로 설치된다. 67 | 68 | ### 자바 플랫폼의 종류 69 | 70 | - **Java SE (Java 2 Platform Standard Edition)** 71 | 가장 기본이 되는 에디션, 자바 언어 대부분의 패키지가 포함된다. 72 | - **Java EE (Java 2 Platform Enterprise Edition)** 73 | 현업에서 사용되는 API들이 집약된 에디션이다. (JSP, Servlet, JDBC 등) 74 | - **Java ME (Java 2 Platform Micro Edition)** 75 | 모바일 기기 등에서 사용되는 API가 포함된 에디션이다. 76 | 77 | ### 자바 플랫폼의 구조 78 | 79 | 자바 프로그램은 **자바 가상 머신** 위에서 동작한다. 자바 가상 머신에는 **자바 API** (자바 프로그램을 작성하는 데 기본적으로 활용할 수 있는 클래스 라이브러리, 즉 표준 API 라이브러리)가 제공된다. 80 | 81 | ![image](https://user-images.githubusercontent.com/22045163/104326478-20603f80-552d-11eb-8845-4a6bf5218570.png) 82 | 83 | **자바 가상 머신(JVM, Java Virtual Machine)** 은 자바 프로그램과 운영체제 중간에 존재하여 서로를 분리함으로써, 애플리케이션이 운영체제에 영향 받지 않고 동작할 수 있는 환경을 제공한다. 단, JVM은 운영체제와 직접적으로 통신을 해야 하기 때문에 운영체제에 맞는 JVM을 설치해주어야 한다. 84 | 85 | **자바 API(Application Programming Interface)** 는 프로그래머가 필요로 하는 기본적인 클래스(Class)들을 거대한 라이브러리로 미리 만들어서 제공한다. 자바 언어 자체는 작고 단순한 구조를 갖지만, 많은 기능들을 API에서 제공하고 있다. 자바 API는 관련된 기능의 클래스들을 묶어 패키지의 형태로 제공한다. _(주요 패키지 : java.applet, java.awt, java.io, java.lang, java.net, javax.swing, java.util)_ 86 | 87 | ## Java 프로그램 구조 88 | 89 | - **자바 클래스(class)** 는 자바 프로그램의 최소 구성 단위로, 선언된 클래스 내부에 실행에 필요한 변수나 메서드 등이 정의된다. 일반적으로 자바 프로그램은 하나의 `.java` 파일에 하나의 클래스 정의를 원칙으로 한다. 90 | - **자바 애플리케이션** 은 바이트 코드로 번역된 후에 바로 실행할 수 있는 프로그램이다. 클래스 내에 `main()` 메서드를 가지고 있어야 한다. 91 | - 자바는 **블록(`{}`)** 으로 자바 문장들의 집합을 표현한다. 블록의 시작과 끝이 짝을 이루지 않으면 컴파일 오류가 발생하며, 클래스, 메서드, 자바 구문(`if`, `for`, `try-catch` 등)에 사용된다. 92 | - 자바 문장은 **세미콜론(`;`)** 을 사용해 문장들을 구분한다. 93 | 94 | ```java 95 | public class 클래스명 { 96 | // 변수 정의 97 | // 메서드 정의 98 | public static void main(String args[]) { 99 | // 실행될 프로그램 코드 100 | } 101 | } 102 | ``` 103 | 104 | ## Java 데이터 타입과 변수 105 | 106 | - **데이터 타입**은 데이터의 성격을 규정한다. 107 | - 사용할 데이터를 **변수**에 저장하고 관리함으로써 데이터의 의미를 정확하게 해석하여 사용할 수 있다. 108 | 109 | ### 데이터 타입 110 | 111 | | 표현형태 | 데이터 타입 | 크기 | 표현 범위 | (자동 초기화 시) 초기값 | 112 | | :-------: | :---------: | :-----------: | :-------------------------------: | :---------------------: | 113 | | 논릿값 | boolean | 1bit | true/false | false | 114 | | 단일 문자 | char | 16bit (2byte) | `'\u0000'` ~ `'\uffff'` | `'\u0000'` (Null) | 115 | | 정수 | byte | 8bit (1byte) | -2^7 ~ 2^7-1 (-128 ~ 127) | 0 | 116 | | 정수 | short | 16bit (2byte) | -2^15 ~ 2^15-1 (-32768 ~ 32767) | 0 | 117 | | 정수 | int | 32bit (4byte) | -2^31 ~ 2^31-1 (약 -21억 ~ 21억) | 0 | 118 | | 정수 | long | 64bit (8byte) | -2^63 ~ 2^63-1 | 0L | 119 | | 실수 | float | 32bit (4byte) | 1.4E-45 ~ 3.4028235E38 | 0.0F | 120 | | 실수 | double | 64bit (8byte) | 4.9E-324 ~ 1.7976931348623157E308 | 0.0D | 121 | 122 | \* 정수 : 부호가 있는 정수, 형을 명시하지 않으면 `int`가 기본형 123 | \* 실수 : 부호가 있는 부동소수점 실수, 형을 명시하지 않으면 `double`이 기본형 124 | _(부동소수점이란, 컴퓨터가 소수점이 포함된 실수를 표현하는 방식 중 하나이다. 이는 고정소수점과 달리 소수점을 자유로이 움직일 수 있어서 표현할 수 있는 수의 범위가 매우 넓다는 장점이 있다.)_ 125 | \* 초기화 : 메서드(함수) 안에서 선언된 변수인 지역변수는 자동으로 초기화되지 않는다. 126 | \* 참조형(Reference Type) 변수의 초기값 : `null` 127 | 128 | #### 데이터 타입의 범위 초과 시 발생하는 오류 129 | 130 | - 컴파일 오류 : **변수 초기화 시** 범위를 초과하는 경우 발생 131 | - 런타임 오류 : **연산 결과**로 범위를 초과하는 경우 발생 132 | 133 | #### 데이터 타입의 변환, 형변환 134 | 135 | 변수의 타입을 다른 타입으로 변경하고자 할 때, **형변환 연산자**를 사용해 변환할 수 있다. 작은 데이터 타입에서 큰 데이터 타입으로 변환하는 것을 **묵시적 형변환(Promotion)**, 그 반대를 **명시적 형변환(Demotion)** 이라고 한다. 136 | 137 | - **묵시적 형변환(Promotion)** : 데이터 손실의 우려가 없어 자동 캐스팅 가능하다. **(자동 형변환)** 138 | 139 | ```java 140 | int age = 25; 141 | double avgAge = age; // double avgAge = (double)age; 142 | ``` 143 | 144 | - 위와 같이 형변환 연산자인 `(double)`을 생략할 수 있다. 145 | - 단, 다음의 규칙에 따라 자동 형변환이 이루어진다. 146 | - byte -> short -> int -> long -> float -> double 순 147 | - char -> int -> long -> float -> double 순 148 | 149 | - **명시적 형변환(Demotion)** : 더 작은 범위를 나타내는 데이터 타입으로 변환하기 때문에 데이터 손실의 우려가 있어 명시적 캐스팅이 필요하다. **(축소 형변환)** 150 | 151 | ```java 152 | double avgAge = 24.56; 153 | int age = (int)avgAge; 154 | ``` 155 | 156 | - 위와 같이 형변환 연산자를 명시해주어야 한다. 157 | - 단, 데이터 타입이 축소되어 변환된 후에도 해당 값을 표현할 수 있어야 한다. 다음은 올바르지 못한 예이다. 158 | 159 | ```java 160 | int sum = 128 161 | byte data = (byte)sum // -128 162 | ``` 163 | 164 | ### 변수 165 | 166 | - 변수는 컴퓨터 내부의 데이터와 1:1 대응되어 변수를 통해 메모리상의 데이터에 접근 가능하다. 167 | - 기본형(Primitive Type)과 참조형(Reference Type) 두 가지 변수가 존재한다. 168 | - 전역(Global) 변수와 지역(Local) 변수로 선언할 수 있다. 169 | - 변수는 자신이 속한 블록({})을 벗어나면 사용이 불가하다. 170 | 171 | #### 전역(Global) 변수와 지역(Local) 변수 172 | 173 | - **전역(Global) 변수** : 클래스 선언부 밑에 선언된 변수로, 여러 메서드에서 공통으로 사용할 수 있다. 174 | - **지역(Local) 변수** : 메서드 선언부 밑 또는 메서드 매개변수로 선언된 변수로, 해당 변수가 선언된 메서드 내에서만 사용 가능하다. 175 | 176 | #### 변수 선언 177 | 178 | - 자바 가상 머신(JVM)에게 데이터를 저장하기 위한 메모리 할당을 요청하는 것이다. 179 | - 데이터가 필요로 하는 크기의 메모리 할당을 위해 데이터 타입을 명시해야 한다. 180 | 181 | ## Java 연산자 182 | 183 | ### 산술 연산자 184 | 185 | - 정수형, 실수형에 사용됨 186 | - 증감 연산자 : `++`, `--` (단항) 187 | - 부호 연산자 : `+`, `-`, `*`, `/` (이항) 188 | - 나머지 연산자 : `%` (이항) 189 | 190 | ### 비교 연산자 191 | 192 | - 대소 비교나 객체의 타입 비교 등에 사용됨 193 | - 비교 연산을 수행한 결과로 `boolean` 타입의 결과를 리턴함 194 | - `>`, `>=`, `<`, `<=`, `==`, `!=`, `instanceof` 195 | - `값1 instanceof 값2` : `값1`이 `값2` 데이터 타입의 객체인 경우 `true` 반환 196 | 197 | ### 논리 연산자 198 | 199 | - `boolean` 데이터 타입에 적용되며, `boolean` 타입의 결과를 리턴함 200 | - `&`, `&&`, `|`, `||`, `!` 201 | - `값1 && 값2`의 경우, `값1`이 `false`인 경우 `값2`를 수행하지 않고 `false`를 리턴한다. 202 | - `값1 || 값2`의 경우, `값1`이 `true`인 경우 `값2`를 수행하지 않고 `true`를 리턴한다. 203 | - `&`, `|` 연산자는 모든 조건을 다 확인한 후 그 결과를 리턴한다. 204 | 205 | ### 비트 연산자 206 | 207 | - 값을 비트(bit)로 연산하는 연산자 208 | - 메모리를 최대한 효율적으로 활용해야 하는 경우 비트 단위로 데이터를 관리해야 한다. 209 | - `&` 논리곱(and), `|` 논리합(or), `^` 배타 논리합(exclusive or, XOR), `~` 보수(not) 210 | - `>>`, `>>>`, `<<` 시프트 211 | - `값1 >> 값2` : `값1`을 비트 단위로 `값2`의 비트 수만큼 오른쪽으로 시프트, 왼쪽에는 현재 부호 비트가 채워짐 212 | - `값1 >>> 값2` : `값1`을 비트 단위로 `값2`의 비트 수만큼 오른쪽으로 시프트, 왼쪽에는 0이 채워짐 213 | - `값1 << 값2` : `값1`을 비트 단위로 `값2`의 비트 수만큼 왼쪽으로 시프트, 오른쪽에는 0이 채워짐 214 | 215 | ### 기타 연산자 216 | 217 | - 대입 연산자 (`=`) : 변수의 값을 저장, 오른쪽의 값을 왼쪽에 대입 218 | - 조건 삼항 연산자 (`? :`) : `if~else`문을 축약하여 사용할 수 있다. 219 | - `변수 = 조건 ? 값1 : 값2` : 조건이 참이면 `값1`이 변수에 대입되고, 거짓이면 `값2`가 변수에 대입된다. 220 | 221 | ## Java의 배열 222 | 223 | 배열은 같은 타입의 데이터들의 모임으로, 메모리가 절약되고, 쉽고 간결하게 프로그램을 작성할 수 있다. 224 | 225 | ### 일차원 배열의 선언과 사용 226 | 227 | - 배열 객체를 참조할 배열 변수 선언 ▶︎ 배열 객체 생성 228 | - 배열 변수 선언 : `데이터타입 배열변수명[];` 또는 `데이터타입[] 배열변수명;` 229 | - 배열 객체 생성 : `배열변수명 = new 데이터타입[배열의 길이];` 230 | - 두 구문을 합쳐 한 문장으로 표현 가능 231 | 232 | ### 이차원 배열 233 | 234 | - 다차원 배열 : 일차원 배열을 여러 개 사용하여 다시 배열을 구현한 것, 즉 배열의 배열 235 | - 배열의 크기를 나타내는 배열 첨자(`[]`)를 차수에 따라 추가하면 된다. 236 | - 배열 객체를 참조할 배열 변수 선언 ▶︎ 배열 객체 생성 237 | - 배열 변수 선언 : `데이터타입 배열변수명 [][];` 또는 `데이터타입[][] 배열변수명;` 또는 `데이터타입[] 배열변수명 [];` 238 | - 배열 객체 생성 : `배열변수명 = new 데이터타입[배열의 배열 길이][배열 길이];` 또는 239 | `배열변수명 = new 데이터타입[배열의 배열 길이][]; 배열변수명[인덱스 번호] = new 데이터타입[배열 길이];` 240 | 241 | ### 명령행 매개변수 242 | 243 | `main()` 메서드란, 자바 애플리케이션에 필수적으로 포함되어야 하는 메서드로, 애플리케이션이 실행될 때 자동으로 실행된다. 일반적으로 자바 애플리케이션은 `main()` 메서드 내에서 다른 클래스 객체를 생성하고, 그 객체의 메서드 호출 또는 변수 조작 과정을 통해 원하는 결과를 얻는다. 244 | 245 | ```java 246 | public class CommandLineArgTest { 247 | public static void main(String args[]) { 248 | // code 249 | } 250 | } 251 | ``` 252 | 253 | 위 코드의 `main()` 메서드를 살펴보면 `String args[]`라는 매개변수를 볼 수 있는데, 이를 **명령행 매개변수**라고 한다. 명령행 매개변수는 문자열 배열을 매개변수로 받아, 프로그램 실행 시 필요한 정보를 프로그램에 전달한다. 254 | 255 | - String 데이터 타입의 배열이다. 256 | - 사용자가 입력한 문자열의 순서에 따라 차례로 배열에 저장된다. 257 | - 공백을 구분자로 여러 개의 값을 전달할 수 있다. 258 | - 문자열 타입이므로 숫자를 입력했을 시 문자열을 숫자로 변환해야 한다. 259 | - 자바 API에서 메서드 제공 : Integer 클래스의 `parseInt()`, Double 클래스의 `parseDouble()` 260 | 261 | ## Java 제어문 262 | 263 | - 조건 제어문 : `if`, `if-else`, `if-else if`, `switch` 문 264 | - 반복 제어문 : `for`, `while`, `do-while` 문 265 | - 이동 제어문 : `break`, `continue`, `return` 266 | -------------------------------------------------------------------------------- /contents/language/java/2.md: -------------------------------------------------------------------------------- 1 | # 객체지향 핵심 원리 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 |
6 | Table of Contents 7 | 8 | - [객체 지향 핵심 개념](#객체-지향-핵심-개념) 9 | - [객체 지향 언어의 주요 개념](#객체-지향-언어의-주요-개념) 10 | 11 |
12 | 13 | ## 객체 지향 핵심 개념 14 | 15 | ### 객체 지향 패러다임의 등장 16 | 17 | 1960년대 말, 하드웨어 발전 속도에 비해 소프트웨어 발전 속도가 느리다는 현상에 따라 '소프트웨어 위기론'이 등장하였다. 이 때 하드웨어와 소프트웨어의 특징을 비교해보면 다음과 같다. 18 | 19 | - 하드웨어는 각 기능들이 독립적인 모듈로 개발된다. 20 | 여러 모듈을 조립할 수 있고, 특정 모듈이 고장나거나 성능이 떨어지면 교체 및 수정이 용이하다. 21 | - 소프트웨어는 모듈화가 되지 않았다. 22 | 따라서 한 번 작성된 코드는 재사용이 어렵다는 문제점이 있었다. 23 | 24 | 이러한 분석에 따라 **모듈화된 소프트웨어 개발에 대한 요구**가 발생하였고, 이는 **객체 지향 패러다임**으로 이어졌다. 25 | 26 | ### 객체 지향 프로그래밍 (OOP, Object Oriented Programming) 27 | 28 | 객체 지향 프로그래밍이 등장하기 이전에는 **절차 지향 프로그래밍**이었다. 절차 지향 프로그래밍 언어는 **실행 순서가 위 -> 아래로 순차적으로 진행**되었고, 코드 재사용을 위해서는 기존에 만들어진 코드를 **복사 + 붙여넣기** 해야 했다. 29 | 30 | 이러한 문제점을 극복하기 위해 **함수**가 등장하였다. 함수는 **자주 사용되는 특정한 코드를 하나의 모듈(묶음)으로 묶어 놓은 것**이다. 따라서 필요할 때마다 함수를 호출해서 사용하는 방식으로 코드 재사용이 가능했지만, 데이터와 함수 간 유기적인 관계성을 갖지 못한다는 아쉬움이 있었다. 31 | 32 | 이후 **객체 지향 프로그래밍**이 등장하면서 함수보다 더 높은 모듈화를 지원하게 된다. **객체 자신이 가진 고유의 데이터를 처리하는 메서드가 등장**하게 된 것이다. 이를 통해 보다 높은 유지보수성을 가지게 되었고, 객체 간 자유로운 데이터 이동이 가능해지게 되었다. 33 | 34 | ### 객체, 클래스, 인스턴스 35 | 36 | > 관계 : 객체 ▶︎ 클래스 ▶︎ 인스턴스 37 | 38 | - **객체 (Object)** : 현실 세계에 존재하는 유무형의 모든 것 39 | - 정적인 요소 : 변수 (Variable) 40 | - 동적인 요소 : 메서드 (Method) 41 | - **클래스 (Class)** : 현실 세계의 객체를 프로그램에서 이용할 수 있는 객체로 표현하는 것 42 | - 현실 세계의 객체를 컴퓨터 메모리에 생성할 수 있는 템플릿이라고 볼 수 있다. 43 | - 자바 프로그램을 구성하는 가장 기본적인 요소이다. 44 | - **인스턴스 (Instance)** : 컴퓨터 메모리에 존재하는 객체 45 | - 클래스로부터 생성된 컴퓨터 메모리 상의 객체이다. 46 | - Object = Instance 47 | 48 | ## 객체 지향 언어의 주요 개념 49 | 50 | ### 상속 (Inheritance) 51 | 52 | 만일 기존의 클래스에 대하여 속성과 메서드가 약간 다른 객체를 필요로 한다고 생각해보자. 이 경우 **상속**을 이용하면 이미 존재하는 클래스를 바탕으로 필요한 변수, 메서드를 추가 정의하여 새로운 클래스를 작성할 수 있다. 53 | 54 | #### 상속의 특징 55 | 56 | - 코드의 재사용성을 높임 57 | 공통적 속성/기능에 대하여 수정이 필요한 경우 해당 클래스에 대해서만 수정하면 된다. 58 | - 객체들 사이의 계층 구조를 이룰 수 있음 59 | - 상위 계층으로 갈수록 : 일반화, 보편화 60 | - 하위 계층으로 갈수록 : 특수화, 개별화 61 | - 자식 클래스는 자신의 변수&메서드 뿐만 아니라 부모 클래스의 변수와 메서드를 모두 사용할 수 있다. 62 | - 상속 관계는 **논리적으로** 반드시 **is a~ 관계**가 성립되어야 한다. 63 | 부모-자식 클래스의 관계가 일반화, 특별화 관계(is a~)에 있어야 한다. 이는 문법적으로 강제되는 것이 아니라 논리적으로 판단되어야 한다. 그렇지 않으면 유지보수가 어려워진다. 64 | 65 | #### 상속의 종류 66 | 67 | - 다중 상속 : 하나의 하위 클래스가 두 개 이상의 상위 클래스를 갖는 것 68 | - 단일 상속 : 하나의 하위 클래스가 단 하나의 상위 클래스를 갖는 것 69 | 70 | 자바는 단일 상속만을 지원하는 언어이다. 71 | 72 | ### 다형성 (Polymorphism) 73 | 74 | > one interface, multiple implementation 75 | 76 | 인터페이스는 하나지만 인터페이스의 구현체는 여러가지를 만들어 사용할 수 있다는 개념이다. 77 | 78 | 다형성은 메서드 오버로딩과 오버라이딩을 통해 지원된다. 79 | 80 | - **오버로딩(Overloading)** : 한 클래스 안에 같은 이름의 메서드를 여러개 정의하면서, 그 인자의 개수나 유형을 다르게 해 놓은 형태 81 | - **오버라이딩(Overriding)** : 상속 관계에 있는 하위 클래스가 상위 클래스가 가지고 있는 메서드를 재정의하는 것 (재정의된 메서드가 선언된 형태는 상위 클래스에서 선언된 것과 같음) 82 | 83 | ### 추상화 (Abstraction) 84 | 85 | 현실 세계에 존재하는 다양한 객체들의 공통된 특성을 모아 일반화 해놓는 것으로, 클래스를 정의하는 데 중요한 역할을 한다. 86 | 87 | > ex. [비행기, 자동차, 열차, 배]는 화물이나 승객을 운반한다는 공통적 특징이 있다. 88 | > 따라서 **운송 수단**으로 추상화가 가능하며, 더 나아가 클래스로서 정의가 가능하다. 89 | 90 | ### 캡슐화 (Encapsulation) 91 | 92 | 변수와 메서드를 하나의 추상화된 클래스로 묶어 독립적으로 동작하지 않도록 한다. 이들은 따로 분리할 수 없다. 객체를 이용할 때는 객체가 제공하는 메서드를 통해 이용하고 데이터가 실제로 어떻게 처리되는지는 알 필요 없다. 93 | 94 | ### 정보 은닉 (Information Hiding) 95 | 96 | 캡슐화된 변수나 메서드를 선택적으로 공개하거나 숨길 수 있다는 개념이다. 숨겨야 하는 정보는 `private`, 공개하는 정보는 `public`으로 관리하며 외부에서는 `public`만 접근이 가능하다. 97 | 98 | ### 메시지 (Message) 99 | 100 | 객체들은 서로 메시지를 주고 받는 방식으로 **통신**한다. 101 | 102 | 객체 간에 메시지를 주고 받기 때문에 여러 객체는 동일한 프로세스를 가질 필요가 없으며, 서로 메시지를 주고 받는데 객체가 존재하는 위치는 제약이 되지 않는다. 103 | 104 | #### 메시지 형식 105 | 106 | - 메시지를 전달할 대상 객체 ex. `car` 107 | - 전달하고 싶은 메시지 ex. `changeGear` 108 | - 메시지를 통해 전달하고 싶은 부가정보가 있다면 해당 정보 ex. `lowerGear` 109 | 110 | > ex. Person 객체가 Car 객체에게 낮은 기어로 변속을 요청하고 싶으면 `car.changeGear(lowerGear)`와 같은 형식으로 메시지를 전달하면 된다. 111 | 112 | ## 클래스의 구조 113 | 114 | ### 클래스의 구성 요소 115 | 116 | #### 클래스 선언부 117 | 118 | - 접근 제한자 (Access Modifier) 119 | - 클래스 (Class) + 클래스명 120 | 121 | #### 클래스 몸체 122 | 123 | - 생성자 (Constructor) 124 | - 멤버 변수 (Variable) 125 | - 메서드 (Method) 126 | 127 | > 변수와 메서드를 하나의 클래스로 묶음으로서 **캡슐화**를 구현한다. 128 | 129 | ```java 130 | public class Employee { // 클래스 선언부 131 | // 멤버 변수 (Variable) 132 | private String name; 133 | private String dept; 134 | 135 | // 생성자 (Constructor) 136 | public Employee(String name, String dept) { 137 | this.name = name; 138 | this.dept = dept; 139 | } 140 | 141 | // 메서드 (Method) 142 | public String getName() { 143 | return name; 144 | } 145 | public void setName(String name) { 146 | this.name = name; 147 | } 148 | } 149 | ``` 150 | 151 | ### 클래스 Modifier 152 | 153 | > 접근 권한과 관련된 modifier를 지정하지 않으면 같은 패키지 내의 클래스에서만 접근 가능 154 | 155 | - 접근 권한 예약어 156 | - `public` : 모든 클래스에서 접근이 가능한 클래스임을 의미 157 | - 활용 방법 예약어 158 | - `final` : 자식 클래스를 가질 수 없는 클래스임을 의미 159 | - `abstract` : 객체 생성이 불가능한 추상 클래스를 의미 160 | 161 | ### 객체 생성 162 | 163 | #### `[객체 참조 변수 이름] = new [클래스 이름]();` 164 | 165 | - 객체가 사용할 메모리 영역을 할당하는 과정에서 예약어 `new`를 사용한다. 166 | - 생성된 객체에 대한 참조값(메모리 주소)을 객체 참조 변수에 할당한다. 167 | - 하나의 클래스로부터 여러 개의 객체를 생성할 수 있다. 168 | 169 | ```java 170 | Employee emp1 = new Employee('John', 'sales'); 171 | Employee emp2 = new Employee('Alice', 'design'); 172 | ``` 173 | 174 | ### 멤버 변수의 선언 175 | 176 | - 전역(Global) 변수 : 멤버 변수라고도 칭하며, 클래스의 여러 메서드에서 공통으로 사용할 수 있다. 177 | - 지역(Local) 변수 : 해당 변수가 선언된 메서드 내에서만 사용할 수 있다. 178 | 179 | 변수는 **modifier**를 통해 접근 권한이나 활용 방법을 제어한다. 이를 통해 객체 지향의 **정보 은닉(Information Hiding)** 개념을 구현할 수 있다. 180 | 181 | #### 접근 권한 182 | 183 | - public : 모든 클래스에서 접근이 가능 184 | - (default) : 동일 패키지에 속하는 클래스에서 접근 가능 185 | - protected : 동일 패키지에 속하거나 하위 클래스 관계의 클래스가 접근 가능 186 | - private : 해당 클래스 내에서만 접근이 가능 187 | 188 | #### 활용 방법 189 | 190 | - final : 변수를 상수로 이용 (값 변경 불가) 191 | - static : 클래스에 소속된 클래스 변수로 이용 192 | 193 | ### 메서드의 선언 194 | 195 | 메서드 구성 요소로는 **접근 제한자, 반환형, 메서드명, 매개변수**가 있다. 접근 제한자(modifier)로는 다음을 쓸 수 있다. 196 | 197 | #### 접근 권한 198 | 199 | > 멤버 변수와 동일 200 | 201 | #### 활용 방법 202 | 203 | - final : Overriding이 불가능한 메서드 204 | - static : 클래스에 소속된 클래스 메서드 205 | - abstract : 추상 메서드, 하위 클래스에 의해 구현 206 | - synchronized : Thread의 동기화를 위한 메서드 207 | 208 | 메서드의 선언부인 **반환형, 메서드명, 매개변수**의 세 가지 요소를 합쳐 **메서드 시그니쳐(Signature)** 라고 한다. 209 | 210 | ### Getter/Setter 메서드를 통한 정보 은닉 211 | 212 | 대부분의 경우 멤버 변수들을 private으로 선언하고 public 메서드를 통해 접근하도록 클래스를 구성한다. 이를 통해 정보 은닉의 개념을 구현한다. 213 | 214 | - Getter : private 멤버 변수에 저장된 값을 반환 215 | - Setter : private 멤버 변수에 값을 저장 216 | -------------------------------------------------------------------------------- /contents/language/java/appendix.md: -------------------------------------------------------------------------------- 1 | # 부록 - 생각해보기 2 | 3 | > 작성자 : [서그림](https://github.com/Seogeurim) 4 | 5 |
6 | Table of Contents 7 | 8 | - [추상클래스와 인터페이스의 차이](#추상클래스와-인터페이스의-차이) 9 | 10 |
11 | 12 | ## 추상클래스와 인터페이스의 차이 13 | 14 | Java에는 추상 클래스(`abstract`)와 인터페이스(`interface`)가 있다. 이들 각각은 무엇일까? 15 | 16 | #### 추상 클래스 17 | 18 | 상속을 통해서 자식 클래스에서 추상 메서드를 완성하도록 유도하는 클래스이다. 추상 메서드를 선언하고 선언부만 작성하면 해당 클래스를 상속받은 자식 클래스는 그 메서드를 반드시 구현해야 한다. 19 | 20 | ```java 21 | abstract class AbstractClass { 22 | public abstract void AbstractMethod(); 23 | } 24 | ``` 25 | 26 | #### 인터페이스 27 | 28 | 인터페이스는 해당 인터페이스를 구현하는 다른 클래스들에게 도움을 주는 목적으로 사용된다. 인터페이스는 추상 클래스와 다르게 다중 상속(구현)이 가능하다. 29 | 30 | ```java 31 | interface MyInterface { 32 | public abstract void 메서드이름(); 33 | } 34 | ``` 35 | 36 | 이렇게 추상 클래스와 인터페이스가 무엇인지 정의해보았을 때 이런 의문이 들 수 있다. 명시된 메서드를 구현한다는 점에서 둘은 사용 용도가 비슷해보이는데 왜 굳이 추상 클래스와 인터페이스로 나뉘어있을까? 이 둘을 나눠서 사용하는 것에는 반드시 이유가 있다. 그 차이점에 대해 생각해보자. 37 | 38 | ### 추상 클래스와 인터페이스의 차이점 및 사용 용도 39 | 40 | #### 사용 의도의 차이 41 | 42 | 추상 클래스는 **상속**, 인터페이스는 **구현**해서 사용한다. 즉 이 둘은 상속과 구현이라는 점에서 그 용도가 명확히 구분된다. 상속은 `is-a` 관계를, 구현은 `can-do` 관계를 의미한다. Java에서는 하나의 클래스만 상속이 가능하기 때문에 해당 클래스의 **구분**을 추상 클래스 상속을 통해 해결하고, 할 수 있는 **기능**들을 인터페이스로 구현한다. 43 | 44 | #### 공통 기능 사용 여부 45 | 46 | 모든 클래스가 인터페이스를 사용해서 클래스 기능들을 구현한다면 공통적으로 필요한 기능들도 모든 클래스에서 재정의해야할 것이다. **공통된 기능이 필요할 때는 추상 클래스를 이용**해 일반 메서드를 작성하고, 자식 클래스에서 사용할 수 있도록 하는 것이 편할 것이다. 47 | 48 | #### 각각의 사용 용도 49 | 50 | 추상 클래스와 인터페이스는 사용 의도와 공통 기능 사용 여부에서 차이점을 가지고 있다. 둘은 각기 다른 성격을 가지고 있기 때문에 어떤 상황에서 어떤 것을 사용해야하는지 생각해보고 사용하는 것이 중요할 것이다. 51 | 52 | - 추상 클래스 : 부모 클래스의 기능을 이용 또는 확장하고 싶을 때 사용 53 | - 인터페이스 : 해당 인터페이스를 구현한 객체들에 대해 동일한 동작이 약속되어야 할 때 54 | -------------------------------------------------------------------------------- /contents/language/materials/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/language/materials/.gitkeep -------------------------------------------------------------------------------- /contents/network/img/3-way-handshake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/network/img/3-way-handshake.png -------------------------------------------------------------------------------- /contents/network/img/4-way-handshake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/network/img/4-way-handshake.png -------------------------------------------------------------------------------- /contents/network/img/osi-7-layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/network/img/osi-7-layer.png -------------------------------------------------------------------------------- /contents/network/img/osi-and-tcp-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/network/img/osi-and-tcp-ip.png -------------------------------------------------------------------------------- /contents/network/materials/yoongoing_networkflow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/network/materials/yoongoing_networkflow.pdf -------------------------------------------------------------------------------- /contents/operating-system/materials/process-state-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/operating-system/materials/process-state-diagram.png -------------------------------------------------------------------------------- /contents/operating-system/materials/queueing-diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/operating-system/materials/queueing-diagram.jpeg -------------------------------------------------------------------------------- /contents/operating-system/materials/동시성.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/operating-system/materials/동시성.jpeg -------------------------------------------------------------------------------- /contents/operating-system/materials/멀티스레드.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/operating-system/materials/멀티스레드.png -------------------------------------------------------------------------------- /contents/operating-system/materials/이세명_operating-system_memory-management.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/operating-system/materials/이세명_operating-system_memory-management.pdf -------------------------------------------------------------------------------- /contents/software-engineering/README.md: -------------------------------------------------------------------------------- 1 | # Software Engineering (소프트웨어 공학) 2 | 3 | > 작성자 : [장주섭](https://github.com/wntjq68), [이세명](https://github.com/3people) 4 | 5 |
6 | Table of Contents 7 | 8 | - [명령형 프로그래밍 vs 선언형 프로그래밍](#명령형-프로그래밍-vs-선언형-프로그래밍) 9 | - [함수형 프로그래밍](#함수형-프로그래밍) 10 | - [객체지향 프로그래밍](#객체지향-프로그래밍) 11 | - [애자일 개발 프로세스](#애자일-개발-프로세스) 12 | 13 |
14 | 15 | --- 16 | 17 | ## 프로그래밍 패러다임 18 | 19 | ### 명령형 프로그래밍 vs 선언형 프로그래밍 20 | 21 | 함수형 프로그래밍은 선언형 프로그래밍이다. 22 | 23 | - **명령형 프로그래밍** : 24 | 25 | 명령형 프로그래밍은 **어떻게(HOW)** 목적을 달성 할 지에 초점이 맞추어져 있고 프로그래밍의 상태와 그것을 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임이다. 그래서 프로그래밍에 사용된 알고리즘에 대해서는 명시되어 있고 목표에 대해서는 명시되어 있지 않다. 일반적으로 컴퓨터가 수행할 명령들을 순서대로 써내려가는데 일반적인 예시로는 C, C++, Java, Paskal, Ruby 등 우리가 아는 웬만한 절차형, 객체지향 언어들은 명령형 프로그래밍 언어이다. 26 | 27 | - **선언형 프로그래밍** : 28 | 29 | 선언형 프로그래밍은 일반적으로 두 가지 정의로 나뉜다. 30 | 31 | 1. 선언형 프로그래밍은 **무엇을(WHAT)** 할지에 초점이 맞추어져 있는 프로그래밍 패러다임이다. 알고리즘은 명시하지 않고 목표에 대해서만 명시 한다. 예를들어 HTML 과 같이 무엇을 웹에 나타낼지에 대해 고민하는 것이지 어떤 방법으로 나타낼지를 고민 하는 것이 아니다. 32 | 2. 함수형, 논리형, 제한형 프로그래밍 언어로 쓰인 경우 선언형 프로그래밍이라고 한다. 명령형 언어와 대비되는 프로그래밍 언어들을 선언형으로 통칭한다. 하지만 이 중 논리형, 제한형 프로그래밍 언어 같은 경우 명백히 알고리즘을 설명할 수 있고 구현도 가능하기 때문에 첫번째 정의를 따르는 엄밀한 의미의 선언형 프로그래밍은 아니다. 33 | 34 | ### 함수형 프로그래밍 35 | 36 | #### 함수형 프로그래밍을 배워야 하는 이유 37 | 38 | "일반적인 프로그래밍은 그냥 생각하면 되는 것이고, 함수형 프로그래밍은 기존과 다르게 생각하는 방법을 알려줄 것이다. 그러므로 당신은 아마도 예전 방식으로 절대 돌아가지 않을 것이다." 39 | 40 | 함수형 프로그래밍은 프로그래밍 언어나 방식을 배우는 것이 아니라 함수로 프로그래밍 하는 사고를 배우는 것이다. 즉 기존의 사고방식을 전환하여 사고를 유연하게 문제 해결에 접근 하는 것이다. 41 | 42 | #### 함수형 프로그래밍이란? 43 | 44 | 함수형 프로그래밍이란 자료처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다. 명령형 프로그래밍에서 상태를 바꾸는 것을 강조하는 것과는 달리 함수형프로그래밍은 함수의 응용을 강조한다. 따라서 프로그래밍이 식이나 선언으로 수행되는 선언형 프로그래밍 패러다임을 따르고 있다. 45 | 46 | - 명령형 프로그래밍의 함수와 수학적 함수의 차이 47 | 48 | **명령형 프로그래밍의 함수** : 프로그램의 상태의 값을 바꿀 수 있다. 이 때문에 참조 투명성이 없고 같은 코드라 해도 실행되는 프로그램의 상태에 따라 다른 결과값을 낼 수 있다. 49 | 50 | **함수형 프로그래밍 (수학적) 함수** : 이 함수의 출력값은 함수에 입력된 인수에만 의존하므로 인수 x에 대해 함수 f 를 호출 하면 f(x) 라는 결과가 나온다. 이것은 프로그램의 동작을 예측하기 훨씬 쉬워진다. (함수형 프로그래밍 개발 핵심 동기) 51 | 52 | 함수형 프로그래밍은 **순수함수(pure function)** 을 조합하고 **공유상태(shared state)**, **변경 가능한 데이터(mutable data)** 및 **부작용(side-effects)** 을 피하여 소프트웨어를 만드는 프로세스이다. 53 | 54 | #### 함수형 프로그래밍 주요 개념 55 | 56 | 1. 순수 함수(pure function) : 무조건 같은 입력이 주어지면 같은 출력을 반환한다. 또한 부작용이 없는 함수 이다. 57 | 2. 합성 함수(function composition) : 새로운 함수를 만들기 위해 둘 이상의 함수를 조합하는 과정이다. f(g(x))로 이해하면 된다. 합성 함수는 함수형 프로그래밍을 이용하여 소프트웨어를 구성하는 중요한 방법이다. 58 | 3. 공유 상태(shared state) : 공유 범위(shared scope) 내에 있는 변수, 객체 또는 메모리 공간이거나 범위 간에 전달되는 객체의 속성입니다. 59 | 4. 불변성(Immutability) : 변경할 수 없는 객체란 생성한 후에 수정할 수 없는 객체이다. 불변성은 함수형 프로그래밍의 핵심 개념이다. 불변성을 빼면 프로그램의 데이터 흐름이 손실되기 때문이다. 60 | 5. 부작용(side effects) : 반환값 이외에 호출된 함수 외부에 영향을 끼치지는 것이다. 따라서 부작용이 없는 순수한 함수는 스레드 측면에서 안전하고 병렬적인 계산이 가능하다. 61 | - 외부 변수 또는 객체 속성 수정 62 | - 콘솔에서 로깅 63 | - 화면에 쓰기 작업 64 | - 파일에 쓰기 작업 65 | - 네트워크에 쓰기 작업 66 | - 외부 프로세스를 트리거 67 | - 부작용을 동반한 다른 함수 호출 68 | 6. 고차함수(high order function) : 함수를 인수로 취급하거나, 함수를 반환하거나 또는 둘 다인 함수이다. 69 | - 콜백 함수, 프로미스, 모나드 등을 사용하여 액션, 효과 또는 비동기 흐름을 추상화하거나 분리 70 | - 다양한 데이터 타입에 대해 동작할 수 있는 유틸리티 만듬 71 | - 합성 함수나 재사용의 목적으로 커링 함수를 만들거나 인수를 함수에 부분적으로 적용 72 | - 함수 목록을 가져오고, 입력 함수의 합성을 반환 73 | 74 | ### 객체지향 프로그래밍 75 | 76 | #### 객체지향 프로그래밍 이전 패러다임 77 | 78 | 1. 순차적, 비구조적 프로그래밍 : 말그대로 순차적으로 코딩해 나가는 것, 필요한 것이 있으면 순서를 추가해가면서 구현하는 방식이다. 직관적이지만 goto문을 활용하므로서 규모가 매우 커지게 되고 나중엔 코드가 어떻게 연결되었는지 구분하기 매우 어렵다. 79 | 2. 절차적, 구조적 프로그래밍 : 위 프로그래밍 방식을 개선하기 위해 나온 패러다임이다. 반복될 가능성이 높은 함수(프로시저)들을 따로 만들어 사용하는 방식이다. 하나의 큰기능을 처리하기 위해 작은 단위의 기능들으로 나누어 처리하고 비교적 작은 규모의 작업을 수행하는 함수를 생성한다. 하지만 이러한 절차적, 구조적 프로그래밍은 함수(논리적 단위)는 표현이 되지만 실제 데이터에 대한 변수 나 상수(물리적 단위)의 표현에는 어려움이 있다. 이러한 단점은 프로그램을 비효율적으로 코딩하게 되고 결국은 유지보수와 디버깅에 어려움을 가져온다. 즉 큰 규모의 작업으로 갈 수 록 더 효율적인 모듈화와 데이터 관리가 필요하다 그렇기에 나온게 데이터와 함수를 동시에 관리 할 수 있는 객체지향 프로그래밍이 각광받게 된다. 80 | 81 | #### 객체 지향 프로그래밍이란? 82 | 83 | 객체 지향 프로그래밍은 특정한 개념의 함수와 자료형을 함께 묶어서 관리하기 위해 탄생한 것이다. 즉 객체 내부에 자료형(필드)와 함수(메소드) 가 같이 존재 한다. 객체지향 프로그래밍을 하면 객체간의 독립성이 생기고 중복코드의 양이 줄어드는 장점이 있다. 또한 독립성이 확립되면서 유지보수에도 큰 도움을 주게 된다. 84 | 85 | 또한 이러한 객체들 끼리 서로 상호작용하면서 어떠한 문제를 해결해 나가는 것이 객체지향 프로그램이다. 86 | 87 | #### 객체 지향 프로그래밍의 특성 88 | 89 | 1. 추상화(Abstraction) 90 | 91 | 어떤 영역에서 필요로 하는 속성이나 행동을 추출하는 작업 , 즉 모델화 92 | 93 | - 사물들의 공통된 특징, 즉 추상적 특징을 파악해 인식의 대상으로 삼는 행위 94 | - 구체적인 사물들의 공통적인 특징을 파악해서 이를 하나의 개념(집합)으로 다루는 수단 95 | 96 | >구체적인 개념에 의존 97 | > 98 | >```java 99 | >switch(동물의 종류){ 100 | > case 사자 : //somthing 101 | > case 호랑이 : // something 102 | > case 토끼 : // someting 103 | >} 104 | >``` 105 | > 106 | >새로운 종이 추가 되면 코드를 새로 추가 해야함 107 | > 108 | > 109 | > 110 | >추상적인 개념에 의존 111 | > 112 | >```java 113 | >void something(Animal a){ 114 | > a.something(); 115 | >} 116 | >``` 117 | > 118 | >새로운 종이 나와도 코드를 변경 할 필요가 없다. 뒤에서 다형성에 의한 오버라이드로 처리 119 | 120 | 2. 캡슐화(Encapsulation) 121 | 122 | 높은 응집도(Cohesion) 과 낮은 결합도(Coupling) 를 유지해야 요구사항에 맞춰 유연하게 대처 할 수 있다. 123 | 124 | - 응집도 : 모듈(클래서) 내에서 요소들이 얼마나 밀접하게 관련이 있는지, 응집도는 정보 은닉을 통해 높일 수 있다. 즉, 내부에서만 사용하는 정보는 외부에서 접근하지 못하도록 제한하는 것이다. Ex) private 125 | - 결합도 : 어떠한 기능을 수행하는데 다른 모듈(클래스)에 얼마나 의존적인지 126 | 127 | 3. 일반화(Generalization) : 상속 128 | 129 | 이미 정의된 상위클래스의 모든 속성과 연산을 하위클래스가 물려 받는 것을 의미한다. 130 | 131 | - 일반화를 이용 하면 상위 클래스로 부터 상속받은 하위 클래스는 모든 속성(필드)과 연산(메소드)을 다시 정의하지 않고 자신의 것으로 사용 할 수있다. 132 | - 상속받은 속성과 연산 외에 새로운 속성과 연산을 추가하여 사용 가능하다. 133 | - 클래스를 재사용 하므로서 소프트웨어 재사용성을 증대시키는 중요한 개념이다. 134 | 135 | 4. 다형성(Polymorphism) 136 | 137 | 1. Pure Polymorphism : 서로 다른 클래스의 객체가 같은 메세지를 받았을때 각자의 방식으로 동작하는 능력이다. 138 | - 일반화와 함께 프로그램을 변화에 유연하게 만든다. 139 | - 다형성을 사용할 경우 어떤 클래스가 참조 되었는지 무관하게 프로그래밍 할 수 있다. 140 | 141 | 2. Ad hoc polymorphism : Overridng & Overloading 142 | 143 | 3. Generics (parameter type) : T (type parameter, 어떤 타입이 와도 무관) 144 | 145 | #### 객체지향 설계 원칙 SOLID 146 | 147 | 1. SRP(단일 책임의 원칙 : Single Responsibility Principle) : 작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는데 집중되어 있어야 한다는 원칙 148 | 149 | 2. OCP(개방폐쇄의 원칙 : Open Close Principle) : 소프트웨어의 구성요소는 확장에는 열려있고, 변경에는 닫혀있어야 한다는 원칙. 변경은 최소화 하고 확장을 최대화 150 | 151 | 3. LSP(리스코브 치환의 원칙 : The Liskov Substitution Principle) : 서브타입은 언제나 기반 타입으로 교체할 수 있어야 한다는 원칙, 즉 항상 하위 클래스는 상위 클래스를 대신할 수 있다. 상위클래스가 할 수 있는 일들에 대해선 하위클래스는 당연히 할 수 있다는 원칙이다. 152 | 153 | 4. ISP(인터페이스 분리의 원칙 : Interface Segregation Principle) : 인터페이스 분리 원칙은 클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙이다. 인터페이스 분리 원칙은 큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클라이언트들이 꼭 필요한 메서드들만 이용할 수 있게 한다. SRP 는 클래스의 단일 책임이라면 ISP는 인터페이스의 단일 책임을 강조하는 것이다. 154 | 155 | 5. DIP(의존성 역전의 원칙 : Dependency Inversion Principle) : 자주 변화하는 구체적인 클래스 보다는 변화하기 어려운 인터페이스나 상위(추상) 클래스와 관계를 맺으라는 원칙 156 | 157 | --- 158 | 159 | ## 애자일 개발 프로세스 160 | 161 | 아래의 자료에서 자세한 설명과 코드를 볼 수 있다. 162 | 163 | - 작성자 이세명 | [Agile Software Development](./materials/CS_(Agile).pdf) 164 | - 작성자 정희재 | [eXtreme Programming(XP)](./eXtremeProgramming.md) 165 | 166 | --- 167 | 168 | ## 질의응답 169 | 170 | _질문에 대한 답을 말해보며 공부한 내용을 점검할 수 있으며, 클릭하면 답변 내용을 확인할 수 있습니다._ 171 | 172 | 173 | 174 |
175 | 애자일처럼 소프트웨어 공학에서의 개발 프로세스를 적용하여 프로젝트를 진행한 경험이 있으신가요? 어떻게 진행했나요? 176 |

177 | 178 | 지원자의 경험을 스크럼, XP, 폭포수 모델 개발 방법 등의 진행 과정을 설명하면 됩니다. 아래는 답안의 한 예시로 참조하시면 되겠습니다. 179 | 180 | 매일매일 짧게라도 회의를 진행하여 계획하고, 리팩토링을 두려워하지않고 프로토타입을 만드는 것을 먼저 생각했다. 181 | 사용할 기능에 대한 우선순위를 두어 개발 주기마다 적용할 기능의 목록을 만들어보았고 적용시킨 후 팀원들의 리뷰를 통하여 제품을 학습하고 이해하였다. 이 후 또 다음 백로그를 준비하는 스크럼 방법을 사용했다. 182 | 183 |

184 |
185 |
186 | 애자일 개발 방법이 생겨나게 된 배경에 대해서 설명 해주실 수 있나요? 187 |

188 | 189 | 전통적인 프로그램 개발 방법론에서는 대규모 프로젝트를 진행할 때 개발보다는 문서화, 회의, 설계 등의 업무에 더 많은 시간을 소비했다. 190 | 또한 전통적인 개발 방법론은 소비자의 요구사항이 변동되었을 때 그에 대해 빠르게 대응하지 못하는 단점이 있었다. 191 | 애자일은 이러한 전통적인 방법론보다 더 나은 방법을 찾고자 하는 데에서 시작되었다. 192 | 193 |

194 |
195 | 196 |
197 | 요즘 많은 기업들이 애자일 프로세스를 따르고 있습니다. 왜 그럴까요? 198 |

199 | 200 | - 변화에 대해 신속하고 능동적인 대응이 가능합니다. 201 | - 이해관계자들과 효과적인 소통이 가능합니다. 202 | - 개발자들이 개발에 좀 더 집중할 수 있습니다. 203 | 204 |

205 |
206 | 207 |
208 | 개발 단계를 기획-설계-구현-테스트 이렇게 네 가지 단계로 분류한다고 했을 때 본인이 생각하기에 가장 시간과 비용이 많이 요구되는 단계는 어디라고 생각 하시나요? 그 이유와 함께 답변 부탁드립니다. 209 |

210 | 211 | 2021년 현재 소프트웨어 생명 주기 단계 중 시간과 비용이 가장 많이 요구되는 단계는 '테스트' 단계입니다. 212 | 이해하기 쉽게 예시를 들어 설명하겠습니다. 213 | 214 | 자동차에 관한 소프트웨어라면 처음부터 안전을 우선순위로 설계하여 구현함에도 실제 테스트단계에서 일어나는 예외사항에 많은 시간과 비용이 들어갑니다. 이상이 일어날 시에 불가피한 리팩토링은 많은 시간과 비용을 사용하여야 합니다. 또한 자동차 뿐만 아니라 스마트폰 및 게임에서도 테스트가 시간과 비용이 가장 많이 요구되었습니다. 215 | 216 |

217 |
218 |
219 | Validation과 Verification 이라는게 있습니다. 한국어로 풀이하면 둘다 검증, 확인이라는 뜻으로 똑같이 해석이 되는데 소프트웨어공학의 입장에서 보면 이 두 단어는 서로 비슷하지만 다른 뜻을 가지고 있습니다. 이 둘의 차이점에 대해서 설명해주실 수 있나요? 220 |

221 | 222 | - Validation : 소프트웨어의 기능과 목적이 요구사항을 반영하는가, 일치하는가, 누락된 요구사항은 없는가 223 | - Verification : 소프트웨어의 입력에 대해 출력 값이 요구사항에서 원하는 바와 일치하는가 즉, 버그가 없는가 224 | 225 |

226 | 227 |
228 | 229 |
230 | 요구사항 명세서란 무엇인가요? 왜 필요한가요? 231 |

232 | 233 | 개발할 소프트웨어의 기능적, 비기능적 요구사항을 서술해 놓은 것을 의미합니다. 이해 관계자와 소프트웨어 개발자들의 의사소통 도구로 사용됩니다. 234 | - 제품의 기능에 대한 기본적인 합의를 수월하게 도출해낼 수 있습니다. 235 | - 각 기능을 개발하기 위한 비용과 스케줄을 예측할 수 있는 지표가 되기도 합니다. 236 | - 효율적인 개발 계획을 세울 수 있고 제품 테스트, 검증을 위한 베이스를 제공해줍니다. 237 | - 제품의 성능 향상을 위한 기반이 되는 자료 입니다. 238 | 239 |

240 | 241 |
242 | -------------------------------------------------------------------------------- /contents/software-engineering/eXtremeProgramming.md: -------------------------------------------------------------------------------- 1 | # eXtreme Programming (XP) 2 | 3 | > 작성자 : [정희재](https://github.com/Hee-Jae) 4 | 5 | 참조 : [위키피디아](https://ko.wikipedia.org/wiki/%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%A6%BC_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D) 6 |
7 | Table of Contents 8 | 9 | - [eXtreme Programming이란?](#extreme-programming-이란) 10 | - [다른 애자일 방법론과 구분되는 특징](#다른-애자일-방법론과-구분되는-특징) 11 | - [XP 실천 방법](#xp-실천-방법) 12 | 13 |
14 | 15 | --- 16 | 17 | ## eXtreme Programming 이란? 18 | **익스트림 프로그래밍(eXtreme Programming, XP)** 은 `켄트 백` 등이 제안한 소프트웨어 개발 방법이다. 비즈니스 상의 요구가 시시각각 변동이 심한 경우에 적합한 개발 방법이다. 1999년 `켄트 백`의 저서인 'Extreme Programming Explained - Embrace Change'에서 발표되었다. **애자일 개발 프로세스**라 불리는 개발 방법 중의 대표적인 하나로 꼽히며, 약칭인 'XP'로 잘 알려져 있다. 19 | 20 | ## 다른 애자일 방법론과 구분되는 특징 21 | 1. 프로그래머들이 코딩을 할 때 **테스트 코드**도 함께 작성한다. 22 | 2. **테스트를 기반**으로 프로젝트를 완성해 나간다. 23 | 3. **반복적으로 고객에게 프로토타입을 전달**함으로써 고객의 요구사항 변화에 민첩하게 대응한다. 24 | 4. 매번 프로토타입을 고객에게 전달함에 있어서 프로토타입 자체로써 **버그가 상대적으로 적은 완벽에 가까운 데모**를 경험하게 해준다. 25 | 26 | 27 | ## XP 실천 방법 28 | 29 | #### Whole Team 30 | 프로젝트에 참여하는 모든 팀원들을 가리킨다. 디자이너, 프로젝트 관리자, 개발자, 테스터, 유저 등 역할들이 많은데 이중 **유저가 제일 중요하다.** 유저가 프로젝트의 키를 가지고 있는 `Stakeholder`이기 때문이다. 31 | 32 | #### Planning Game 33 | 어떤 개발 과정을 끝마칠 것인가, 다음에는 어떤 일을 할 것인가에 중점을 두어 `iteration`을 계획한다. 일반적으로 2주를 주기로 한 `iteration`을 진행한다. 한 `iteration` 동안 프로토타입을 만들어서 의뢰인에게 보여주고 회의를 한다. 따라서 **기한 내에 프로토타입이 반드시 개발되어야 한다.** 이를 통해 기업은 본인들의 실력을 입증할 수 있고, 의뢰인 입장에서는 프로토타입이 마음에 안들 때 빠르게 손절해서 비용을 아낄 수가 있다. 34 | 35 | #### Customer Tests 36 | 의뢰인이 원하던 결과물을 만들어내는게 중요하다. 따라서 주기적으로 **결과물이 정말 의뢰인이 원하는 게 맞는지 테스트를 해본다.** 37 | 38 | #### Small Releases 39 | **개발자는 주기적으로 의뢰인에게 프로토타입을 보여준다.** 의뢰인은 이를 통해 추가적인 요구사항을 제시할 수 있으며, 개발자는 이를 통해 현재까지 개발 상황이 올바르게 가고 있음을 알 수 있다. 40 | 41 | #### Simple Design 42 | 개발을 하다 보면 코드가 복잡해진다. 따라서 **코드를 가능한 심플하게 설계를 한다.** (KISS 원칙 : Keep It Small, Simple) 43 | 44 | #### Test-Driven Development 45 | XP에서 가장 중요한 요소이다. **테스트를 거치면서 코딩한다.** 46 | 47 | #### Pair Programming 48 | **두명 혹은 그 이상의 프로그래머가 함께 코딩을 한다.** 코딩 하는 역할, Quality Assurance 역할 등으로 나누어서 개발과 테스트에 모두 집중한다. 49 | -------------------------------------------------------------------------------- /contents/software-engineering/materials/CS_(Agile).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seogeurim/CS-study/830643c08333fb6d4df11c7c6c3b8ecf70264e23/contents/software-engineering/materials/CS_(Agile).pdf --------------------------------------------------------------------------------