<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>SMY TIL</title>
    <link>https://moonwalk0515.tistory.com/</link>
    <description>TIL 게시용 블로그. (26.04.23~)</description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 23:53:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>samooyang</managingEditor>
    <item>
      <title>[C++ 코딩테스트 연습] 5. 시뮬레이션</title>
      <link>https://moonwalk0515.tistory.com/25</link>
      <description>&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;[1. 시뮬레이션]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서 시뮬레이션은 현실 세계의 물리적 시스템이나 복잡한 논리적 프로세스를 컴퓨터 모델로 구현하여 &lt;b&gt;시간이 흐름에 따라 그 상태가 어떻게 변화&lt;/b&gt;하는지 모의 실험하는 것을 의미한다.&lt;b&gt; 상태와 객체&lt;/b&gt;를 클래스로 정의하고, 일정한 &lt;b&gt;시간&lt;/b&gt; 간격마다 객체들의 상태를 업데이트하는 루프를 실행하며, 객체들이 서로 어떻게 &lt;b&gt;상호작용&lt;/b&gt;하는지 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 C++ 라이브러리는 벡터(vector), 큐(queue), 스택(stack) 등 다양한 자료구조를 제공해서 &lt;b&gt;시뮬레이션 내의 데이터 관리&lt;/b&gt;를 용이하게 하고, &amp;lt;random&amp;gt; 라이브러리로 &lt;b&gt;확률적인 변수&lt;/b&gt;를 생성하는 것도 제공한다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. 좌표 연산&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1) y, x좌표 표현하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2차원 평면을 다루는 문제를 대비해서 2차원 배열을 활용해 좌표를 표현하는 방식을 연습해보자.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2차원 배열로 좌표를 표현할 때는&lt;b&gt; y좌표를 행의 인덱스&lt;/b&gt;로 표현하고,&lt;b&gt; x좌표를 열의 인덱스&lt;/b&gt;로 표현한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) dy, dx 배열로 현재 좌표에서 이동하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 좌표에서 주변 좌표로 이동해야 하는 경우에는 하드코딩하거나, &lt;b&gt;이동 방향에 대한 오프셋 배열을 활용&lt;/b&gt;해서 구현할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 현재 좌표에서 인접한 8개의 좌표를 한 번씩 순회하기&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하드코딩도 가능하지만, &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;현재 좌표에 오프셋 배열을 더하는 방식으로 구현해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778803746647&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//4방향 배열
int dy[4] = {-1, 0, 1, 0}
int dx[4] = {0, 1, 0, -1};

//8방향 배열(대각선)
int dy[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dx[8] = {0, 1, 1, 1, 0, -1, -1, -1};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 배열을 선언 및 초기화 해놓고, 현재 좌표에서 이 배열들을 반복문과 함께 사용하여 다음 좌표를 계산할 수 있다. 이 과정에서 맵의 범위를 벗어나지 않는지 확인하는 경계 검사 로직이 포함되는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1778804707747&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

int dy[4] = {-1, 0, 1, 0};
int dx[4] = {0, 1, 0, -1};

int MAX_Y = 5;
int MAX_X = 5;

int main() {
    int y = 1; 
    int x = 1; 

    for (int i = 0; i &amp;lt; 4; i++) {
        int ny = y + dy[i]; 
        int nx = x + dx[i];

        // 맵 범위를 벗어나는지 확인
        if (ny &amp;lt; 0 || ny &amp;gt;= MAX_Y || nx &amp;lt; 0 || nx &amp;gt;= MAX_X) {
            continue; // 범위를 벗어나면 무시하고 다음 방향 탐색
        }

        // 이동 가능한 좌표라면 원하는 로직 수행
        cout &amp;lt;&amp;lt; &quot;탐색 가능한 주변 좌표: y=&quot; &amp;lt;&amp;lt; ny &amp;lt;&amp;lt; &quot;, x=&quot; &amp;lt;&amp;lt; nx &amp;lt;&amp;lt; &quot;\n&quot;;
    }

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3) dy, dx 배열로 좌우 대칭하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;좌우 대칭은 X축의 원소들이 이동하는 대칭이므로 Y좌표는 변하지 않는다. 즉, 행 번호는 그대로이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열 크기가 N이라면, 좌우 대칭하면 특정 원소의 X좌표는 (N-1) - X&lt;/b&gt; 가 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(Y, X) -&amp;gt; (Y, (N-1) - X)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4) dy, dx 배열로 상하 대칭하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;상하 대칭은 Y축의 원소들이 이동하는 대칭이므로, X좌표는 변하지 않는다. 즉, 열 번호는 그대로이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열 크기가 N이라면, 상하 대칭하면 특정 원소의 Y좌표는 (N-1) - Y&lt;/b&gt; 가 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(Y, X) -&amp;gt; ((N-1) - Y, X)&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4) dy, dx 배열로 90도 회전하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;90도 회전의 경우,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열의 크기가 N이라면 90도 회전하면 ((N-1) - X, Y)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;180도 회전은 90도 회전을 2번하고, 270도 회전은 90도 회전을 3번하면 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이게 직관적으로 생각할 때, 행렬의 인덱스가 0부터 시작해서 기존에 수학 공부할 때랑 미묘하게 달라서 항상 헷갈리는 것 같다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;좀 외우기 힘드니까, 두가지 방식을 병행하기로 했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;lt;1&amp;gt; 기본적으로 암기할 건 암기한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;lt;2&amp;gt; 규칙을 이해한다. &lt;b&gt;행과 열이 바뀌고, 새로운 열은 역순이 된다&lt;/b&gt; .&lt;b&gt;기존의 행은 새로운 열이 되는데, 방향이 반대로 뒤집힌다&lt;/b&gt;(N - 1 - Y)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위치가 x축 y축 기준으로 뒤집어지면 N-1이다. 인덱스가 0이어서 그런 건데, 그냥 반대로 뒤집히는 행 or 열에 N-1을 넣고 시작하자.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;시계방향&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;90도 : (X, N-1-Y)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;180도: (N-1-Y, N-1-X)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;270도: (N-1-X, Y)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;반시계방향&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;90도: (N-1-X, Y)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;180도: (N-1-Y, N-1-X)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;270도: &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;(Y, N-1-X)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 추가적으로, 직사각형 배열은?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예를 들어 N x M 배열이라고 해보면, 중요한 특징이 생긴다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;바로 행과 열의 크기가 서로 바뀐다는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;회전 후 배열의 형태만 M x N 으로 미리 만들고, 동일한 90도 회전 공식을 적용하여 값을 채워넣는다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5) dy, dx 배열로 대각선 방향 이동하기&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 좌표에서 정방향 대각선 방향으로 이동하는 방법에 대해 알아보자.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 좌표가 (Y, X)일 때 (Y+1, X+1)을 하면 정방향 대각선 기준 다음 원소가 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;응용해서, &lt;b&gt;특정 좌표 (A, B) 가 (Y, X)와 같은 대각선 상에 있는지&lt;/b&gt; 확인하려면 &lt;b&gt;(Y-A)와 (X-B)가 동일한지 비교하는 것으로 확인&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반대로 역방향 대각선으로 이동하기 위해선, (Y, X) -&amp;gt; (Y+1, X-1)을 하면 역방향 대각선 기준 다음 원소가 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;응용해서, &lt;b&gt;특정 좌표 (A, B) 가 (Y, X)와 같은 대각선 상에 있는지&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;확인하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;(Y-A)와 (X-B)가 동일한지 비교하는 것으로 확인&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. 행렬 연산&lt;/h3&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1) 행렬 덧셈&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 &lt;b&gt;행렬의 크기가 같아야 할 수 있는 연산&lt;/b&gt;이다. 각 행렬에서 같은 위치에 있는 값끼리 더하거나 뺀다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 행렬 덧셈&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A행렬의 X행과, B행렬의 Y열의 원소를 각각 곱한 후 더해서 C[X,Y]의 값을 완성하는 연산이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;A의 행의 개수와 B의 열의 개수가 같아야&lt;/b&gt; 연산이 진행될 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 3x4 행렬과 4x3 행렬을 곱하면 3x3 행렬이 나온다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 풀어보기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[1. 달팽이 수열 만들기]&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;n을 입력받아 n x n 크기의 2차원 배열을 생성하여 달팽이 수열을 채우는 solution함수 구현하기. (시계방향, 나선형)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;n은 2이상 10미만의 자연수.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;숫자는 배열의 첫 번째 행, 첫 번째 열에서 시작.&lt;/p&gt;
&lt;pre id=&quot;code_1778811106823&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
using namespace std;
vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; solution(int n) {
 // N*N 2차원 벡터를 선언하고 초깃값을 0으로 함
 vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; snail_array(n, vector&amp;lt;int&amp;gt;(n, 0));
 int num = 1;
 // 행과 열의 시작과 끝 인덱스를 설정
 int start_row = 0, end_row = n - 1;
 int start_col = 0, end_col = n - 1;
 // 제일 외각부터 달팽이 수열 규칙대로 채움
 while (start_row &amp;lt;= end_row &amp;amp;&amp;amp; start_col &amp;lt;= end_col) {
 // 가장 왼쪽 윗부분 에서 가장 아래 바로 직전 까지 채우기
 for (int i = start_col; i &amp;lt;= end_col; ++i) {
 snail_array[start_row][i] = num++;
 }
 ++start_row;
 // 가장 왼쪽 아래부분 에서 가장 오른쪽 바로 직전 까지 채우기
 for (int i = start_row; i &amp;lt;= end_row; ++i) {
 snail_array[i][end_col] = num++;
 }
 --end_col;
 // 가장 오른쪽 아래부분 에서 가장 위 바로 직전 까지 채우기
 if (start_row &amp;lt;= end_row) {
 for (int i = end_col; i &amp;gt;= start_col; --i) {
 snail_array[end_row][i] = num++;
 }
 --end_row;
 }
 // 가장 윗부분 에서 가장 왼쪽 바로 직전 까지 채우기
 if (start_col &amp;lt;= end_
  for (int i = end_row; i &amp;gt;= start_row; --i) {
 snail_array[i][start_col] = num++;
 }
 ++start_col;
 }
 }
 return snail_array;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;생각보다는 참신한 아이디어를 요구하는 문제는 아니었고, 행렬의 인덱스를 잘 활용해서 시작지점과 끝지점을 정하고 순차적으로 채워나가는 형태였다. 이때 행과 열을 잘 구분하는 게 중요했다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[2. 캐릭터의 좌표]&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RPG 게임에서, up, doun, left, right 방향이 있고 각 키를 누르면 해당 방향으로 한칸씩 이동한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;머쓱이가 입력한 방향 키의 배열 keyinput과 맵의 크기 board가 주어지고, 캐릭터는 [0, 0] 에서 시작한다. 키 입력이 모두 끝난 뒤에 캐릭터의 좌표 [x, y]를 반환하는 solution() 함수 구현하기. ([0, 0]은 board의 정중앙에 위치)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;board는 [가로 크기, 세로 크기] 형태로 주어짐&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;board의 가로 크기와 세로 크기는 홀수임&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;board의 크기를 벗어난 방향 키 입력은 무시할 것.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;0 &amp;lt;= keyinput의 길이 &amp;lt;= 50&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= board[0] &amp;lt;= 99&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= board[1] &amp;lt;= 99&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;keyinput은 항상 up, down, left, right만 주어짐&lt;/p&gt;
&lt;pre id=&quot;code_1778812158216&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;string&amp;gt; keyinput, vector&amp;lt;int&amp;gt; board)
{
 // 현재 위치를 나타 내는 크기가 2이고 값이 모두 0인 벡터 선언
 vector&amp;lt;int&amp;gt; v(2,0);

 // 키 입력순으로 캐릭터 이동
 for(string s : keyinput)
 {
 if (s==&quot;up&quot; &amp;amp;&amp;amp; v[1]&amp;lt;+board[1]/2) v[1]++;
 else if(s==&quot;down&quot; &amp;amp;&amp;amp; v[1]&amp;gt;-board[1]/2) v[1]--;
 else if(s==&quot;left&quot; &amp;amp;&amp;amp; v[0]&amp;gt;-board[0]/2) v[0]--;
 else if(s==&quot;right&quot; &amp;amp;&amp;amp; v[0]&amp;lt;+board[0]/2) v[0]++;
 }
 return v;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;if로 board를 넘어가는 입력값을 받지 못하도록 설정하는 것이 중요했다. 조건을 충족한다면 문제에서 요구한대로 값을 주도록&amp;nbsp; 설계했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[추가] 3차원 배열의 시뮬레이션 관련 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 직접 경험할 게임 개발 환경에서는 무엇보다 3차원을 바라보는 시각이 중요할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3차원 배열 시뮬레이션은 기본적으로 2차원의 논리를 확장한 것이지만 축이 하나 추가된다는 큰 차이점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 직관적으로 눈에 들어오지 않는 것들이 생기는데(공간 지각 능력이 뛰어나지 않은 편임), 3차원을 다룰 때에 무엇이 중요해지는지 간략하게 살펴보고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 좌표계와 방향 벡터&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깊이 or 높이를 나타내는 Z축이 추가되기 때문에 3차원에서는 (&lt;b&gt;z, y, x&lt;/b&gt;) (&lt;b&gt;면, 행, 열&lt;/b&gt;)형태로 접근하게 된다. 이에 따라 상화좌우 4방향을 탐색하던 델타 배열도 위, 아래,&lt;b&gt; 앞, 뒤,&lt;/b&gt; 좌, 우의 6방향 탐색으로 확장이 된다. 거기에 대각선까지 고려하면 26방향으로 늘어나게 된다. 26방향을 설정해주어야하는 만큼 오타 없이 꼼꼼하게 선언하는 구조적 설계가 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 메모리 접근 순서와 캐시 지역성 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다차원 배열도 '&lt;b&gt;행 우선 방식&lt;/b&gt;'을 따르기때문에, 배열을 순회할 때 가장 바깥쪽 반복문에 z, 가장 안쪽 반복문에 x를 두어야 CPU 캐시 히트율이 높아져 성능 저하를 막을 수 있다. 따라서 반복문 설계시 접근 순서에 유의하며 설계해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3차원 배열은 arr[z][y][z]&lt;/b&gt; 이다. 컴퓨터에서는 행 우선 방식으로 저장하므로, 컴퓨터는 첫 번째 면(z=0)의 첫 번째 행(y=0)에 있는 모든 열(x)를 메모리에 나란히 배치한다. 즉, arr[0][0][0], arr[0][0][1], arr[0][0][2].... 순으로 배치가 되기 시작한다. 하나의 면(z=0)이 다 끝나면 그제야 다음 면(z=1)의 데이터를 이어붙인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 컴퓨터가 읽기 편하도록 하기 위해 x를 가장 안쪽 반복문에 두어서 &lt;b&gt;x가 가장 빈번하게&lt;/b&gt; 변하도록 해서 메모리를 물리적으로 읽어오는 순서에 코드를 맞춰주는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 1차원 배열로의 평탄화 기법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난번에도 학습했지만 vector&amp;lt;vector&amp;lt;vector&amp;gt;&amp;gt; 형태로 선언하는 것 보다 전체크기가 Z * Y * X 인 1차원 배열이나 단일 vector를 선언하고, 특정 &lt;b&gt;3차원 좌표를 1차원 인덱스인 z*(Y*X) + y*X + x로 변환&lt;/b&gt;하여 접근하는 방법을 자주 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 시간 및 공간 복잡도의 급증과 상태 공간 제어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;축이 하나 늘어나면 연산해야 할 데이터의 크기가 기하급수적으로 커진다. 2차원은 O^2 이었던 복잡도가 3차원은 O^3으로 늘어나므로 시간 초과나 메모리 초과를 발생시킬 확률이 높아진다. 따라서 불필요한 연산을 줄이는 가지치기 조건이나 방문처리 배열을 훨씬 더 엄격하게 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 가지칙기 조건의 경우 어차피 진행하다가 안될 것 같으면 그 끝까지 가서 굳이 확인하지 말고 즉시 되돌아가라는 조건이다. ( if (current_z &amp;lt; best_z) continue; ) 형식으로 조건을 작성해서 3차원에서의 연산량을 줄이는 것이다. 3차원에서는 하나의 조건으로 불필요한 연산을 쳐낸다면 N^2 만큼의 경우의 수를 제거하는 것이므로 그 효과가 훨씬 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 3차원에서는 방문 배열 ( visited[z][y][x] = true ) 를 통해서 이미 왔던 곳은 절대 다시 안간다라고 조건을 걸어줘야 &lt;b&gt;중복 탐색하는 경우의 수를 줄여서&lt;/b&gt; 연산량이 급증하지 않도록 보완할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;*오늘의 코드카타*&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열 s를 숫자로 변환한 결과를 반환하는 함수, solution을 완성하세요.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;s의 길이는 1 이상 5이하입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;s의 맨 앞에는 부호(+, -) 가 올 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;s는 부호와 숫자로만 이루어져있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;s는 &quot;0&quot;으로 시작하지 않습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778801960580&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int solution(string s) {
    int answer = 0;
    answer = stoi(s);
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;C++ 라이브러리 string 헤더의 stoi() 함수를 이용해서 간단하게 변환할 수 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열 s는 특이한 조건이 없는 숫자 형태의 문자열이어서, 단순히 stoi 함수에 집어넣기만 하면 쉽게 정수로 변환할 수 있다,&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 라이브러리를 사용하지 않고 직접 하고 싶으면 이렇게 하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1778802450735&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
using namespace std;
int solution(string s) {
	int answer = 0;
	bool isNegative = false;
	int startIndex = 0;
	
	if (s[0] == '-') {
		isNegative = true;
		startIndex = 1;
	} else if (s[0] == '+') {
		startIndex = 1;
	}
	for (int i = startIndex; i &amp;lt; s.length(); i++) {
		answer = answer * 10 + (s[i] - '0');
	}
	if (isNegative) {
		answer = -answer;
	}
	
	return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;*그 외에 오늘 한 것*&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[언리얼 엔진 에디터 연습] : 캐릭터 빙의, 조작, 메쉬, 스켈레톤, 카메라, 애니메이션 설정&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[C++로 게임 만들기] : Text RPG 처음부터 다시 만들기(반복) + std::사용해서 using namespace std; 없애기 + unique_ptr 사용 교체&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 코딩테스트</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/25</guid>
      <comments>https://moonwalk0515.tistory.com/25#entry25comment</comments>
      <pubDate>Fri, 15 May 2026 11:54:13 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 코딩테스트 연습] 4. 배열, 정렬</title>
      <link>https://moonwalk0515.tistory.com/24</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;[1. 배열]&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;1. 배열의 특징&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1) 동질성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 원소는 모두 &lt;b&gt;동일한 타입&lt;/b&gt;을 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 연속된 메모리 할당&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단편화 문제가 발생할 확률이 적어 접근 속도가 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3) 인덱스 기반 접근&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배열의 첫 번째 원소는 인덱스 0을 가지고, 이후의 원소들은 순차적으로 1씩 증가하는 인덱스를 가진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. 배열의 선언&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입에 더해 배열은 &lt;b&gt;배열의 크기&lt;/b&gt;를 추가로 명시해야 한다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1) 1차원 배열&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 선형형태로 저장하는 가장 기본적인 배열이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;int arr[5]; 처럼 선언한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;int arr[5] = {1,2,3,4,5}; 처럼 선언 후 초기화도 가능하다. 만약 초기화하는 값의 개수가 size보다 적으면 &lt;b&gt;나머지 원소는 0으로 자동 초기화&lt;/b&gt;된다. 반대로 선언하고 아예 초기화하지 않으면 값은 Garbage Value로 남는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 2차원 배열&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 행과 열로 구성된 표 형태로 저장하는 배열이다. 가로줄은 행. 세로줄은 열로 읽는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;int arr[2][3]; 처럼 선언한다. 이 경우 2행, 3열의 2차원 배열을 선언한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;int arr[2][3]={{1,2,3},{4,5,6}}; 기본적으로 가로줄 먼저 쓴다고 생각하면 된다. 첫번째 행은 {1,2,3}, 두번째 행은 {4,5,6}의 2차원 배열로 초기화된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3) 3차원 배열&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 게임 개발에서 어떻게 3차원 배열을 사용하는지 알아봤다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;복셀(Voxel)기반 지형 : 대표적으로 마인크래프트. 월드의 특정 구역(Chunk) 데이터를 저장할 때 chunk[x][y][z] 형태의 3차원 배열을 사용해서 해당 위치에 어떤 블록이 있는지를 저장한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공간 분할 : FPS게임에서 총알이 적과 부딪혔는지 검사하기 위해 3D 공간을 거대한 3차원 배열로 쪼갠뒤, 총알이 위치한 해당 배열 칸과 그 주변 칸의 적들만 검사하게 해서 성능을 향상시킨다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3D 길찾기 : 공중을 날아다니는 비행기 같은 AI가 장애물을 피해 목표 지점으로 이동하게 하려면 3차원 노드 배열을 기반으로 알고리즘을 수행해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3차원 배열을 위해서는 1차원 배열을 3차원처럼 다루는 평탄화 기법을 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, int width, height, depth 를 이용해서 가로, 세로, 깊이가 각각 10인 3차원 그리드를 만든다고 하면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1차원 배열 하나로 다음과 같이 전체 공간 10*10*10 을 한번에 할당할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vector&amp;lt;int&amp;gt; grid(width * height * depth, 0);&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 전체 공간을 할당해 둔 뒤, [x][y][z] 위치의 데이터에 접근하기 위해서&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;int index = x + (y*width) + (z*width*height);&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;grid[index] =1;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 3차원 공간을 1차원 배열처럼 만들고 사용할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. 배열의 메모리 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다차원 배열이라고 할지라도, 컴퓨터의 메모리 구조는 결국 1차원이므로 &lt;b&gt;배열의 원소들은 메모리 상에 연속적인 메모리 할당 방식으로 저장, 즉 한 줄로 저장&lt;/b&gt;된다. C++에서는 2차원 이상의 배열도 내부적으로는 &lt;b&gt;행 우선 순서&lt;/b&gt;로 배치된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4. 배열의 효율성&amp;nbsp;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1) 맨 뒤에 삽입하는 경우&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;O(1) 연산. 다른 원소에 영향을 미치지 않고 그냥 추가된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 맨 앞에 삽입하는 경우&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;O(n) 연산. 현재 존재하는 배열의 원소들이 한 칸씩 밀리면서 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 풀어보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[1. 배열 제어하기]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt; 정수 배열 lst가 주어질 때, 배열의 중복값을 제거하고 배열 데이터를 내림차순으로 정렬해서 반환하는 solution() 함수 구현하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lst의 길이는 2 이상 1,000 이하&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lst의 원소 값은 -100,000 이상 100,000이하&lt;/p&gt;
&lt;pre id=&quot;code_1778718141411&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt; // sort, unique를 위해 선언
using namespace std;
bool compare(int a, int b) { // 정렬 기준 정의
 return a &amp;gt; b;
}
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; lst) {
 sort(lst.begin(), lst.end(), compare); // 내림차순으로 정렬
 lst.erase(unique(lst.begin(), lst.end()), lst.end()); // 중복값 제거
 return lst;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 먼저 배열 데이터를 내림차순으로 정렬한다. sort() 와 compare()를 결합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 중복값을 제거한다. 중복값을 제거할 땐 unique() 와 erase()를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[2. 두 수를 뽑아서 더하기]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt; 정수 배열 numbers가 주어질 때, numbers에서 서로 다른 인덱스에 있는 2개의 수를 더해 만들 수 있는 모든 수를 배열에 오름차순으로 담아 반환하는 solution()함수를 구현하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 길이는 2 이상 100 이하&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numbers의 모든 수는 0 이상 100 이하&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778718214600&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; numbers) {
 set&amp;lt;int&amp;gt; sum; // 두 수의 합을 저장할 저장공간 선언
 for(int i = 0;i&amp;lt;numbers.size();++i) // 반복문을 수행하면서 두 수의 합을 저장
 for(int j = i+1 ; j&amp;lt; numbers.size();++j)
 sum.insert(numbers[i] + numbers[j]);
 vector&amp;lt;int&amp;gt; answer(sum.begin(), sum.end()); // 반환타입을 맞추기 위헤 벡터 자료형
 return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set라는 컨테이너를 사용해서 애초에 그냥 중복을 없애버리고 오름차순으로 담아버리자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[3. 가장 많은 문제를 맞춘 사람 찾기]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3명이 모의고사의 문제를 전부 찍으려 할 때, 1번 문제부터 마지막 문제까지의 정답이 순서대로 저장된 배열 answers가 주어졌을 때 가장 많은 문제를 맞춘 사람이 누구인지 배열에 담아 반환하도록 solution() 함수를 작성하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 사람 : 1, 2, 3, 4, 5 를 반복해서 찍음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 사람 : 2, 1, 2, 3, 2, 4, 2, 5를 반복해서 찍음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 사람 : 3, 3, 1, 1, 2, 2, 4, 4, 5, 5를&amp;nbsp; 반복해서 찍음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험은 최대 10,000문제로 구성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 정답은 1, 2, 3, 4, 5 중 하나임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 높은 점수를 받은 사람이 여럿이면 반환하는 값을 오름차순으로 정렬.&lt;/p&gt;
&lt;pre id=&quot;code_1778718574593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
// 각 수포자가 찍는 패턴을 정의
vector&amp;lt;int&amp;gt; firstPattern = {1,2,3,4,5};
vector&amp;lt;int&amp;gt; secondPattern = {2,1,2,3,2,4,2,5};
vector&amp;lt;int&amp;gt; thirdPattern = {3,3,1,1,2,2,4,4,5,5};
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; answers) {
 // 최종적으로 가장 많이 문제를 맞힌 사람이 저장될 벡터
 vector&amp;lt;int&amp;gt; answer;

 // 각 수포자들의 패턴대로 답안을 작성할때 문제를 맞힌 갯수가 저장될 벡터
 vector&amp;lt;int&amp;gt; matchCnt(3);
 // 실제 정답과 각 수포자들의 패턴을 비교해서 맞춘 갯수
 for(int i=0; i&amp;lt;answers.size(); i++) {
 if(answers[i] == firstPattern[i % firstPattern.size()]) matchCnt[0]++;
 if(answers[i] == secondPattern[i % secondPattern.size()]) matchCnt[1]++;
 if(answers[i] == thirdPattern[i % thirdPattern.size()]) matchCnt[2]++;
 }
 // 가장 많이 맞춘 수포자가 얻은 점수
 int max_score = *max_element(matchCnt.begin(),matchCnt.end());
  // 가장 많이 맞춘 수포자의 번호를 저장
 	for(int i = 0; i&amp;lt; 3; i++) {
 	if(matchCnt[i] == max_score) answer.push_back(i+1);
 	}
	
	return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0) &lt;b&gt;채점을 한 다음&lt;/b&gt;에, &lt;b&gt;가장 높은 점수를 받은 사람의 번호를 배열 형태로 돌려주는&lt;/b&gt; 메인 함수를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) vector&amp;lt;int&amp;gt; matchCnt(3); 수포자 3명의 점수가 담길 벡터를 만들어놓은 것. 크기가 3인 배열 matchCnt를 선언하고, 기본적으로 배열은 초기화 하지 않으면 0으로 채워지기 때문에 채점 준비를 마치게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 채점 로직 : for문을 사용해서 i = 0 부터 정답표를 스캔하면서 3명을 동시에 채점하기 위한 반복문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찍는 패턴이 반복되므로 나머지 연산자를 사용해서 배열의 크기 이내에서 패턴이 반복될 수 있도록 만든 것. 값이 일치하면 matchCnt를 1 증가 시킴. 첫번째 사람의 경우 인덱스 0번에 카운트가 저장됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) matchCnt 배열을 확인한 뒤에 max_element 함수를 사용해 가장 높은 점수가 몇 점인지 확인하고, 그게 max_score로 된다. 담만 값이 있는 위치를 알려주어야 하기 때문에 *max_element를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 인덱스 i에 1을 더한 값이 학생의 번호가 되므로 최고 점수를 받은 학생이 vector&amp;lt;int&amp;gt; answer 배열에 들어가게 된다. 이렇게 해야 공동 1등이 있어도 번호가 작은 순서대로 배열에 담기게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;[2. 정렬]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬이란?: 사용자가 정의한 순서대로 데이터를 나열하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬을 하는 이유: 원하는 값을 빨리 찾을 수 있다. 또한 정렬이 전제 조건인 알고리즘이 존재하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. 정렬의 종류&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1) 삽입 정렬&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬되지 않은 데이터를 이미 정렬된 부분 배열의 적절한 위치에 차례로 삽입하여 전체 데이터를 정렬하는 알고리즘.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[의사 코드]&lt;/p&gt;
&lt;pre id=&quot;code_1778721014883&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for j = 2 to A.length
 key = A[j]
 // A[j]를 정렬된 부분 배열 A[1..j-1]에 삽입한다.
 	i = j - 1
 	while i &amp;gt; 0 and A[i] &amp;gt; key
	 	A[i + 1] = A[i]
 		i = i - 1
	A[i + 1] = key&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[세부 동작]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로, 두 원소를 비교하고, 삽입 정렬 연산을 수행하면 &lt;b&gt;앞의 원소가 뒤로 밀려나고&lt;/b&gt;, 그 &lt;b&gt;빈칸을 뒤의 원소가 채워주는&lt;/b&gt; 방식. 최종적으로는 오름차순의 경우 작은 숫자가 맨 앞으로 오게 되고, 큰 숫자가 맨 뒤로 가게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 첫 번째 원소는 이미 정렬된 것으로 간주하므로, 두 번째 원소부터 시작한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) 뒷자리의 원소와 비교해서 (오름차순인 경우) 뒷자리의 원소가 더 크면 연산을 수행하지 않고, 뒷자리의 원소가 더 작으면 연산을 수행한다. 이 연산은 뒷자리에 있던 원소가 정렬 상태를 만들 때까지 밀어내고 채워넣는 식으로 반복된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[시간 복잡도]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;O(N^2). 최악의 경우에는 처음부터 의도한 정렬과 반대로 데이터가 저장된 경우가 문제가 된다. 따라서, 삽입 정렬은 거의 정렬이 완료된 상황에서 매우 효율적인 알고리즘이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778721406910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 삽입 정렬을 이용하여 정수 배열을 오름차순으로 정렬하는 예제
#include &amp;lt;iostream&amp;gt;
using namespace std;
int main() {
 int arr[] = {5, 2, 4, 6, 1, 3};
 int n = sizeof(arr) / sizeof(arr[0]);
 for (int i = 1; i &amp;lt; n; i++) {
 int key = arr[i];
 int j = i - 1;
 // key보다 큰 요소를 한 칸씩 뒤로 이동
 	while (j &amp;gt;= 0 &amp;amp;&amp;amp; arr[j] &amp;gt; key) {
 	arr[j + 1] = arr[j];
 	j--;
 	}
 	arr[j + 1] = key;
 	}
 	for (int i = 0; i &amp;lt; n; i++)
 		cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
 		cout &amp;lt;&amp;lt; endl;
// 출력결과
// 1 2 3 4 5 6
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 병합 정렬&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬되지 않은 데이터를 계속 반으로 나눈 뒤, 작은 단위부터 정렬하며 병합해 나가는 방식의 정렬 알고리즘.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[의사 코드]&lt;/p&gt;
&lt;pre id=&quot;code_1778721701079&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MERGE_SORT(A, p, r)
if p &amp;lt; r
 q = (p + r) / 2
 MERGE_SORT(A, p, q)
 MERGE_SORT(A, q + 1, r)
 MERGE(A, p, q, r) // 이미 정렬된 A[p...q]와 A[q+1....r]을 병합해서 하나의 배열로 만듬&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[세부 동작]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 분할&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬할 배열의 중간 지점을 기준으로 두 개의 하위 배열로 나눈다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) 정복&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 하위 배열은 재귀적으로 병합 정렬을 호출해서 정렬한다.&lt;br /&gt;(3) 결합&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬된 두 개의 하위 배열을 하나의 정렬된 배열로 병합한다. 이 과정에서 &lt;b&gt;두 배열의 원소를 순서대로 비교해서 작은 값을 먼저 새로운 배열에 추가&lt;/b&gt;한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[시간 복잡도]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터가 N 개일 때 데이터가 1개가 될 때까지 계속 반으로 나누면 분할트리의 높이 h는 logN이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각각의 분할 단계마다 모든 데이터를 병합하므로, 병합 연산은 단계마다 총 N번 발생한다. 따라서 전체 시간 복잡도는&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;O(N) * logN = O(NlogN)이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778722057616&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
void merge(int arr[], int left, int mid, int right) {
 int size = right - left + 1; // 임시 배열의 크기
 int* temp = new int[size]; // C-style 동적 배열 할당
 int i = left; // 첫 번째 부분 배열(arr[left...mid])의 시작 인덱스
 int j = mid + 1; // 두 번째 부분 배열(arr[mid+1...right])의 시작 인덱스
  int k = 0; // 임시 배열(temp)의 시작 인덱스
  
   // 두 부분 배열을 비교하며 임시 배열에 정렬하여 저장
     while (i &amp;lt;= mid &amp;amp;&amp;amp; j &amp;lt;= right) {
     if (arr[i] &amp;lt;= arr[j]) {
     temp[k++] = arr[i++];
     } else {
     temp[k++] = arr[j++];
     }
 }

 // 첫 번째 부분 배열에 남은 요소가 있다면 임시 배열에 복사
     while (i &amp;lt;= mid) {
     temp[k++] = arr[i++];
     }
     // 두 번째 부분 배열에 남은 요소가 있다면 임시 배열에 복사
     while (j &amp;lt;= right) {
     temp[k++] = arr[j++];
     }
     // 임시 배열(temp)에 정렬된 요소들을 원래 배열(arr)의 해당 위치에 복사
     for (int idx = 0; idx &amp;lt; size; ++idx) {
     arr[left + idx] = temp[idx];
     }
     delete[] temp;
}

// mergeSort 함수는 변경할 필요 없음
void mergeSort(int arr[], int left, int right) {
 if (left &amp;lt; right) {
     // (left + right) / 2 는 큰 값에서 오버플로우를 일으킬 수 있음
     int mid = left + (right - left) / 2;
     // 왼쪽 부분 배열 정렬
     mergeSort(arr, left, mid);
     // 오른쪽 부분 배열 정렬
     mergeSort(arr, mid + 1, right);
     // 정렬된 두 부분 배열 병합

 	merge(arr, left, mid, right);
 	}
}

int main() {
 int arr[] = {9, 3, 1, 5, 13, 12};
 int n = sizeof(arr) / sizeof(arr[0]);
 cout &amp;lt;&amp;lt; &quot;원본 배열: &quot;;
 for (int i = 0; i &amp;lt; n; i++)
 cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
 cout &amp;lt;&amp;lt; endl;
 mergeSort(arr, 0, n - 1);
 cout &amp;lt;&amp;lt; &quot;정렬된 배열: &quot;;
 for (int i = 0; i &amp;lt; n; i++)
 cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
 cout &amp;lt;&amp;lt; endl;
 // 출력 결과
 // 원본 배열: 9 3 1 5 13 12
 // 정렬된 배열: 1 3 5 9 12 13
 return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) mergesort() 함수로 배열을 더 이상 쪼갤 수 없을 때까지 계속 반으로 나눈다. 최종적으로는 요소가 1개씩 남는다. 이 과정에서 재귀함수가 사용된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) merge() 함수로 정렬하면서 다시 합친다. 임시 배열(temp)을 만들어서 정렬된 결과를 이곳에 쌓아놓으면서 왼쪽 배열의 첫번째가 i부터 시작하고, 오른쪽 배열의 첫번째가 j부터 시작하게 해서 i와 j를 비교하면서 더 작은 숫자를 임시 배열(k번째 인덱스)에 채워 넣는다. 이후, 다 수행하고 나서 보니 한쪽 배열의 숫자가 남아있는 경우, 남아있는 숫자들을 차례대로 temp에 그대로 옮겨 담는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(3) 원본 배열로 되돌린다. temp배열의 내용물을 원본 배열인 arr의 원래 위치에 덮어씌운다. temp는 delete[] temp를 통해 메모리에서 지운다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3) 계수 정렬&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터값 자체를 인덱스로 사용하는 데이터에 의존적인 정렬 방식이다. 각 값의 빈도 수를 세어 빈도 수 기반으로 정렬을 수행한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[의사 코드]&lt;/p&gt;
&lt;pre id=&quot;code_1778723459548&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//A는 입력 배열이고, k는 입력 배열의 최대값
COUNTING-SORT(A, k)
 count &amp;larr; 크기가 k + 1인 0으로 채워진 배열
 output &amp;larr; 입력 배열과 같은 크기의 배열
 for i = 0 to A.length - 1
 count[A[i]] &amp;larr; count[A[i]] + 1
 idx &amp;larr; 0
 for i = 0 to k-1
  while (count[i]--) {
 	arr[idx++] = i;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[세부 동작]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 카운트 배열을 생성한다. 입력 배열의 최대값 k를 기준으로 크기가 (k+1)인 count 배열을 생성하고 모든 원소를 0으로 초기화.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) 빈도수를 계산한다. 입력 배열을 순회하면서 각 원소의 값을 인덱스로 사용해서 배열의 해당 위치에 빈도수를 누적한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(3) 결국 값을 인덱스로 사용해서 그대로 끼워넣으며 정렬하는 방식이다. 굉장히 직관적인듯.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만, 계수 정렬은 음수 값이 존재하여 해당 값을 배열의 인덱스로 사용할 수 없는 경우 계수 정렬을 사용할 수 없고, 원소 값의 범위가 넓거나 듬성듬성 있는 경우 메모리 사용 면에서 비효율적이라는 한계점이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[시간 복잡도]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;count 배열을 초기화 할 때 O(K)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;빈도수를 셀 때 O(N)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬된 배열을 생성하는 데 O(N)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최종적으로는 O(N+K)이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778723589492&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 목적: 양의 정수만으로 이루어진 배열을 계수 정렬로 정렬한다.
// 동작: 배열에서 최댓값을 찾아 그 크기만큼 카운트 배열을 만들고, 카운트 정보를 기반으로
#include &amp;lt;iostream&amp;gt;
using namespace std;
int main() {
 int arr[] = {4, 2, 2, 8, 3, 3, 1};
 int n = sizeof(arr) / sizeof(arr[0]);
 // 1. 최댓값 찾기
 int max = arr[0];
 for (int i = 1; i &amp;lt; n; ++i)
 if (arr[i] &amp;gt; max) max = arr[i];
 // 2. 카운트 배열 생성 및 초기화
 int* count = new int[max + 1]{};
 // 3. 각 요소 개수 세기
 for (int i = 0; i &amp;lt; n; ++i)
 count[arr[i]]++;
 // 4. 정렬 결과 저장
 int idx = 0;
 for (int i = 0; i &amp;lt;= max; ++i) {
 while (count[i]--) {
 arr[idx++] = i;
 }
 }
 // 5. 출력
 for (int i = 0; i &amp;lt; n; ++i)
 cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &quot; &quot;;
 delete[] count;
 return 0;
}
//
// 출력결과:
// 1 2 2 3 3 4 8
//&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4) 힙 정렬&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;힙이란 조건을 만족하는 이진 트리이다. 보통 최대힙과 최소힙으로 구분된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 힙은 모든 부모 노드의 값이 자식 노드의 값보다 크거나 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최소 힙은 반대로 부모 노드의 값이 자식 노드보다 작거나 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대힙을 구축하는 과정을 통해 알아보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1)&lt;/b&gt; 최대힙을 구축하기 위해선 먼저 &lt;b&gt;max_heapify(int idx)를 정의&lt;/b&gt;한다. 이 함수는 &lt;b&gt;특정 원소를 기준으로 최대 힙의 성질을 만족하도록 재구성&lt;/b&gt;하는 함수이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778724015946&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전역 변수: A (배열), heap_size (힙 크기)
// 인덱스 i를 루트로 하는 트리를 최대 힙 속성을 만족하도록 수정 (Heapify)
max_heapify(i)
 l = LEFT(i) // 왼쪽 자식 인덱스
 r = RIGHT(i) // 오른쪽 자식 인덱스
 largest = i // 가장 큰 값의 인덱스 (일단 부모 자신으로 초기화)
 // 왼쪽 자식과 비교
 if l &amp;lt; heap_size and A[l] &amp;gt; A[largest] then
 largest = l
 // 오른쪽 자식과 비교
 if r &amp;lt; heap_size and A[r] &amp;gt; A[largest] then
 largest = r
 // 만약 자식 노드가 부모 노드보다 크다면
 if largest != i then
 swap A[i] and A[largest] // 두 노드의 값을 교환
 max_heapify(largest) // 교환된 위치(자식 노드)에서 재귀적으로 Heapify 수행&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;마지막 non-leaf 노드부터 루트 노드까지 차례대로 max_heapify()를 수행하면 배열을 최대 힙 구조로 변환할 수 있다. 이 과정을 &lt;b&gt;bulid_max_heap()&lt;/b&gt; 이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[의사코드]&lt;/p&gt;
&lt;pre id=&quot;code_1778724263159&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// A : 힙으로 만들 배열
build_max_heap()
 heap_size = A.length // 배열의 크기를 설정합니다
 // 마지막 non-leaf 노드부터 루트(1번 인덱스)까지 역순으로 반복합니다
 for idx = floor(A.length / 2) down to 1
 max_heapify(idx) // 각 내부 노드에 대해 MAX_HEAPIFY를 호출합니다&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서, 반복문을 A.length / 2 부터 시작한 것에 주목해보자면, 힙을 구축할 때 자식 노드가 없으면 아무런 동작을 하지 않는다는 점을 이용한 것이다. 현재 노드 인덱스가 N/2 을 넘으면 자식 노드의 인덱스가 N을 넘게된다. 힙의 크기는 N이므로 이러한 일이 발생하지 않으니까 굳이 현재 노드 인덱스가 N/2을 넘는 일은 고려하지 않아도 된다는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3)&lt;/b&gt; max_heapift()를 수행하고 나서는 &lt;b&gt;힙 정렬&lt;/b&gt;을 수행한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;힙 정렬의 최종 목적은 배열을 오름차순 또는 내림차순으로 정렬하는 것이다. 최대 힙은 가장 큰 값이 루트에 위치하게만 할 뿐, 배열 전체가 정렬된 상태는 아니기 때문에 힙 정렬이 필요한 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 힙을 구성한 후, &lt;b&gt;루트 노드를 배열의 마지막 요소와 교환&lt;/b&gt;하고, 나머지 구간에 대해 다시 최대힙을 구성하는 과정을 &lt;b&gt;반복&lt;/b&gt;해서 계속 뒤에서부터 끼워넣는 방식으로 &lt;b&gt;전체 배열을 정렬&lt;/b&gt;한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[의사 코드]&lt;/p&gt;
&lt;pre id=&quot;code_1778724629856&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전역 변수: A (배열), heap_size (힙 크기)
HEAP_SORT()
 // 1. 최대 힙 구성
 BUILD_MAX_HEAP()
 // 이 시점에서 heap_size는 A.length와 같음
 // 2. 정렬 단계 (힙에서 최대값을 꺼내 정렬된 위치로 이동)
 // 배열의 마지막 요소부터 두 번째 요소까지 반복
 for i = A.length - 1 down to 1
 // 현재 루트(최대값)와 힙의 마지막 요소(A[i]) 교환
 swap A[0] and A[i]
 // 힙 크기 1 감소 (정렬된 요소 A[i]를 힙에서 제외)
 heap_size = heap_size - 1
 // 루트(0번 인덱스)에 대해 MAX_HEAPIFY를 호출하여 힙 속성 복원
 MAX_HEAPIFY(0)
 // 루프 종료 후 전역 변수 A는 오름차순으로 정렬됨&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬된 요소 A[i]를 힙에서 제외하는 heap_size = heap_size - 1 (힙 크기 1 감소) 과정을 잘 숙지해놓자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[시간 복잡도]&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;힙은 높이가 최대 log N인 이진트리가 되고, 각 단계에서 힙 속성을 재구성하는데 O(log N)의 시간이 걸리므로, 전체 정렬의 시간 복잡도는 O(N logN)이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;힙 자료구조를 이용하게 되면 &lt;b&gt;새로운 원소를 삽입 또는 삭제&lt;/b&gt;할 때 힙의 성질을 유지할 수가 있어 트리의 높이만큼만 연산이 필요하게 되어 O(log N)이 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 풀어보기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1. 계수 정렬 구현하기]&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인수로 받은 문자열 s를 사전순으로 정렬된 문자열로 반환하는 solution() 함수를 구현하기. 정렬은 계수 정렬을 활용할 것.&lt;/p&gt;
&lt;pre id=&quot;code_1778724900611&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
string solution(string s) {
 // 알파벳 개수(26개)만큼 빈도수 배열 생성
 vector&amp;lt;int&amp;gt; counts(26, 0);
 // 문자열의 각 문자에 대한 빈도수를 빈도수 배열에 저장
 for (char c : s) {
 counts[c - 'a']++;
 }
 // 빈도수 배열을 순회하면서 정렬된 문자열을 생성
 string sorted_str = &quot;&quot;;
 for (int i = 0; i &amp;lt; 26; i++) {
 sorted_str += string(counts[i], i + 'a');
 }
 return sorted_str;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;알파벳 개수만큼 빈도수 배열을 생성하면, 그 배열의 인덱스 0 은 'a'문자의 개수를 저장하는 공간이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;counts[c - 'a']++. 아스키 코드 값을 이용할 때, 현재 글자(s[i])에서 'a'를 빼면 알파벳의 순서대로 인덱스 값을 얻을 수 있다. 예를 들어 s[i]가 b면 'b'의 아스키 코드값은 98이고, 'a'의 아스키 코드값은 97이다. 따라서 값이 1이 되어서 인덱스 1에 개수를 더할 수 있게 되는 것이다. 아스키코드를 외우는게 문제가 아니고, 그냥 보통 이런 식으로 사용해서 함수를 작성하는 경우가 많아서(성능이 좋음) 잘 숙지해두자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;sorted_str += string(counts[i], i + 'a'). 아까 아스키 코드 값을 이용해서 인덱스 i에 개수를 더했으니까, 이제는 다시 인덱스 숫자 i에 다시 문자 'a'를 더해서 string() 함수를 이용해서 문자로 변환하는 식이다. 그렇게해서 복원된 문자를 sorted_str에 저장하고 반환받는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;string의 길이는 1 이상 10,000이하&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;s는 알파벳 소문자로 이루어져 있음.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[2. 문자열 정렬하기]&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열로 구성된 배열 strings와 정수 n이 주어졌을 때, 각 문자열의 인덱스 n번째 문자를 기준으로 오름차순 정렬하기.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;strings는 길이 1 이상, 50 이하인 벡터이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;strings의 원소는 소문자 알파벳으로 이루어져 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;strings의 원소는 길이 1 이상, 100 이한인 문자열이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 strings의 원소 길이는 n보다 크다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인덱스 n의 문자가 같은 문자열이 여럿이면, 사전 순으로 앞선 문자열이 앞쪽에 위치한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778725550756&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int idx;
// 비교 함수
bool compare (string a, string b) {
 return a[idx] == b[idx] ? a &amp;lt; b : a[idx] &amp;lt; b[idx];
}
vector&amp;lt;string&amp;gt; solution(vector&amp;lt;string&amp;gt; strings, int n) {
 idx = n;

 // 각 문자열의 idx번째 문자를 기준으로 정렬
 sort (strings.begin(), strings.end(), compare);
 return strings;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;sort()와 comp()를 이용해서 구현해보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비교함수는 삼항연산자를 이용해서 작성해본다. a[idx] == b[idx]면 문자열 전체를 사전순으로 비교(a&amp;lt;b) 하고, 그렇지 않으면 idx번째 문자 자체를 비교해서 오름차순으로 배치하라는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[3. 가장 큰 수 구하기]&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;문제&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;0 또는 양의 정수 두 숫자가 주어졌을 때 정수를 이어붙여 만들 수 있는 가장 큰 수를 알아내기. 0 또는 양의 정수가 담긴 배열 numbers가 주어질 때, 순서를 재배치해 만들 수 있는 가장 큰 수를 문자열로 바꾸어 반환하는 solution() 함수 작성하기.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;조건&amp;gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;numbers의 길이는 1 이상 100,000 이하&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;numbers의 원소는 0 이상 1,000 이하&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정답이 너무 클 수 있으므로 문자열로 바꾸어 반환할 것.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778725958961&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
// 문자열로 바뀐 두 수를 조합해서 크기를 비교
bool compare(const string&amp;amp; lhs, const string&amp;amp; rhs) {
 return (lhs + rhs) &amp;gt; (rhs + lhs);
}
string solution(vector&amp;lt;int&amp;gt; numbers) {
 string answer = &quot;&quot;;
 vector&amp;lt;string&amp;gt; strings;
 for (auto elem : numbers) {
 // numbers의 원소를 문자열로 변형해서 푸시
 strings.push_back(to_string(elem));
 }
 // 정렬함수를 기준으로 정렬
 sort(strings.begin(), strings.end(), compare);
 // 정렬된 문자열을 앞에서 부터 추가
 for (auto elem : strings) {
 answer += elem;
 }
 // 최종 숫자가 0이면 0을 반환하고 그렇지 않으면 answer 반환
 return answer[0] == '0' ? &quot;0&quot; : answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 입력된 정수를 문자열로 변환&lt;/b&gt;한 뒤, &lt;b&gt;comp() 함수를 통해서 이어 붙였을 때 더 큰 경우를 찾아내서&lt;/b&gt; 결과를 완성하는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) lhs, rhs를 문자열(const string&amp;amp; )로 받아와서&lt;/b&gt; 비교한 뒤 만약 &lt;b&gt;왼쪽 + 오른쪽 순서로 문자열을 이어붙인 결과가 크면 true를 반환&lt;/b&gt;하고 그렇지 않으면 false를 반환하는 식이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(3) &lt;b&gt;string&lt;/b&gt; solution(vector&amp;lt;int&amp;gt; numbers) -&amp;gt; 정수형 배열 numbers를 입력받아서 가장 큰 수를 문자열 형태로 반환하는 메인함수&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(4) string answer = &quot;&quot;; -&amp;gt; 정답을 문자열로 바꾸어 반환한다. 누락하지 않도록 유의!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(5) sort + comp 이용해서 정렬함수를 기준으로 vector안에 담겨있는 문자열 형태의 원소를 정렬한다. 이렇게 되면 앞에 두었을 때 더 큰 숫자를 만들어내는 녀석으로 배열의 맨 앞으로 밀어내게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(6) 이렇게 정렬된 문자열 형태의 숫자를 결국 앞에서부터 추가한다. 예를 들어 sort(comp())를 거치고 나서 정렬된 배열이 {6, 10} 이면, 정렬된 순서대로 앞에서부터 문자열을 추가해서 answer = &quot;&quot;; 에 붙이는 것이다. 결과적으로 &quot;610&quot;의 문자열로 정답이 반환되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;*오늘의 코드카타*(05.13)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #282a2c; color: #e3e3e3; text-align: start;&quot;&gt;자연수 n을 뒤집어 각 자리 숫자를 원소로 가지는 배열 형태로 리턴해주세요. 예를 들어 n이 12345이면 [5,4,3,2,1]을 리턴합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1) malloc(C언어)&lt;/p&gt;
&lt;pre id=&quot;code_1778713574541&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int* solution(long long n) {
	long long temp = n;
    int len = 0;
    if (n == 0) {
    	len = 1;
	} else {
    	while (temp &amp;gt; 0) {
        	len++;
            temp /= 10;
		}
    }

	int* answer = (int*)malloc(sizeof(int)*len);
	int i = 0;
	
    if (n == 0) {
		answer[0] = 0;
	} else {
    	while (n&amp;gt;0) {
        	answer[i++] = n % 10;
            n /= 10;
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 메모리 할당 함수인 malloc을 사용하기 위해서 먼저 n의 자릿수를 구한뒤에 필요한 메모리 공간의 크기를 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 n을 10으로 나눈 나머지를 인덱스 0번부터 배열에 채워나가는 식으로 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2) vector&lt;/p&gt;
&lt;pre id=&quot;code_1778713752830&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(long long n) {
	vector&amp;lt;int&amp;gt; answer;
    
    if (n==0) {
    	answer.push_back(0);
        return answer;
    }
    
    while (n&amp;gt;0) {
    	answer.push_back(n%10);
        n /= 10;
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서 vector라는 컨테이너를 사용하면, 자릿수를 미리 구하지 않아도 알아서 메모리를 확보하고 push_back을 사용해서 쉽게 vector배열의 끝에 원소를 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3) to_string&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 문제풀이에 많이 응용되기도 하는 방법. 숫자를 문자열로 변환하여 처리하는 방법이다. n을 문자열로 바꾼 뒤에, 문자열의 뒤에서 앞으로 순회하며 문자를 다시 숫자로 변환해 배열에 담는 방법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778714013243&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(long long n) {
    vector&amp;lt;int&amp;gt; answer;
    string s = to_string(n);
    
    for (int i = s.length() - 1; i &amp;gt;= 0; i--) {
        string temp = s.substr(i, 1);
        answer.push_back(stoi(temp));
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로, to_string과 stoi 함수를 결합 + 반복문을 사용해서 풀이하는 방법. 이 경우에 substr() 함수를 이용해서 현재 위치에서 1글자를 잘라내어 temp에 추가하고, 그 temp를 stoi함수로 숫자로 변환한 뒤 배열에 밀어넣는 과정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778714074936&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(long long n) {
    vector&amp;lt;int&amp;gt; answer;
    string s = to_string(n);
    
    for (int i = s.length() - 1; i &amp;gt;= 0; i--) {
        answer.push_back(s[i] - '0');
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째, 문자열의 특정 문자를 숫자 타입으로 변환할 때, '0'을 빼주면 아스키코드 값을 기준으로 실제 정수값을 얻을 수 있다. 즉 굳이 stoi를 쓰지 않아도 그냥 문자열에서 -'0' 을 해주는 것 만으로도 알아서 실제 정수값으로 바꿔준다. 이게 무슨말이냐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아스키코드를 보면 기본적으로 숫자 형태의 문자들을 실제 정수값으로 저장해 놓은 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 '0'은 정수 48로 저장되어있고, 문자 '2'는 50으로 저장되어있는 식이다. 이렇게 해서 9까지 정수 1 단위로 저장해놓았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 예를 들어, '2' - '0' 을 해주는 것 만으로도 컴퓨터 내부에서는 50 - 48 처럼 정수끼리의 연산을 통해 2라는 숫자값을 하는 것과 같다는 것이다. 결국 우리가 원하는 0에서 9 사이의 정수값만 남기게 할 수 있다. 잘 알아두자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*그 외에 오늘 한 것*&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Text RPG 만들기]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 던전 탐험 시스템(eventRoll을 사용한 확률 분기, 기존의 몬스터를 활용한 강적 시스템)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 직업별 스킬(virtual 함수를 이용해서 player-&amp;gt;useSkill()로 각 직업별 스킬 오버라이딩)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 연금술 공방(레시피 검색 및 재료 확인, 포션 제작, 포션 저장소 기능을 map을 사용해서 아이템을 관리하는 자료 구조 작성)&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 코딩테스트</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/24</guid>
      <comments>https://moonwalk0515.tistory.com/24#entry24comment</comments>
      <pubDate>Thu, 14 May 2026 11:47:02 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 코딩테스트 연습] 3. 재귀함수</title>
      <link>https://moonwalk0515.tistory.com/22</link>
      <description>&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 재귀의 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀란 자기 자신을 정의하거나 호출하는 것을 말한다. 프로그래밍에서는 함수가 실행 중에 자기 자신을 다시 호출하는 방식의 함수를 말한다. 재귀함수는 호출되고 나서 언젠가 반드시 종료되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 왜 재귀함수를 사용해야 하는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 더 작은 크기의 문제의 해를 이용해 해결하고, 그렇게 하다가 더 이상 줄일 수 없을 정도로 작아진 문제의 해를 직접 구하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 재귀 함수 설계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 팩토리얼의 사례&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N! 을 Fact(N)으로 표현한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fact(N) = N * Fact(N-1) 로 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fact(N-1) = N-1 * Fact(N-2) 로 표현할 수 있으니까, 둘을 합치면, Fact(N) = N * (N-1) * Fact (N-2) 가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 계속 반복하다보면 결국 1! 이 나오게 되고, 더이상 작게 할 수 없으므로 1을 반환하게 만들면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1778551060991&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
// 팩토리얼을 구하는 재귀 함수
int Fact(int N) {
 if (N == 0 || N == 1) return 1; // 종료 조건 (0! = 1)
 return N * Fact(N - 1); // 재귀 호출
}
int main() {
 int num = 5;
 cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; &quot;! = &quot; &amp;lt;&amp;lt; Fact(num) &amp;lt;&amp;lt; endl;
 return 0;
}
/*
출력 결과:
5! = 120
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 재귀함수는 첫 번째 기저 조건을 충족(즉, 가장 작아진 문제의 해를 직접 구할 수 있으면)하면, k번째 함수가 실행되었을 때 k+1 번째 함수가 반드시 실행된다는 특징을 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 메모리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 성능 및 메모리를 고려해야 한다. 재귀 함수가 반복적으로 호출되면 각각의 호출마다 새로운 스택 프레임이 생성되어 스택 메모리에 쌓인다. 그리고 최종적으로 마지막 함수(가장 작아진 문제의 해, 첫 번째 기저 조건)까지 호출이 완료되면 각 함수는 자신을 호출했던 위치로 되돌아가면서 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 재귀 함수가 호출될 때마다 스택 메모리에 함수의 실행 정보가 저장되는데, 과도하게 누적되다보면 스택 오버플로우가 발생할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 및 메모리를 위해서라면 &lt;b&gt;종료 조건을 명확히 정의&lt;/b&gt;하고 &lt;b&gt;호출 깊이를 적절히 제한&lt;/b&gt;해서 메모리 사용을 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 종료 조건 명확히 정의하기 : 기저 조건 설정하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 호출 깊이 제한하기 : 메모이제이션 사용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 재귀 함수 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 대부분의 반복 작업은 반복문으로 구현한다. 그럼에도 재귀를 사용하는 것이 더 효율적인 경우는 뭘까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 깊이 우선 탐색 알고리즘(DFS)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;함수의 호출 과정은 스택 구조를 따르고, 가장 최근에 호출된 함수부터 가장 먼저 종료된다. 이를 이용해서 스택 기반 알고리즘의 대표적인 사례인 깊이 우선 탐색 알고리즘(Deep-First Search)을 구현할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DFS는 현재 노드를 방문 처리하고, 인접한 노드 중 방문하지 않은 노드를 재귀적으로 방문한다. 이후 더 이상 방문할 노드가 없으면 이전 노드로 되돌아간다. 즉, &lt;b&gt;각 함수는 자신과 인접한 노드의 관리만 하면 되므로 코드의 가독성과 유지보수성이 향상&lt;/b&gt;된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778552049167&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
// 그래프를 인접 리스트로 표현
vector&amp;lt;int&amp;gt; graph[9];
bool visited[9];

// DFS 함수 정의
void DFS(int node) {
 visited[node] = true;
 cout &amp;lt;&amp;lt; node &amp;lt;&amp;lt; ' ';
 for (int i = 0; i &amp;lt; graph[node].size(); i++) {
 int next = graph[node][i];
 if (!visited[next]) {
 DFS(next);
 }
 }
}
int main() {
 // 그래프 초기화
 graph[1] = {2, 3, 8};
 graph[2] = {1, 7};
 graph[3] = {1, 4, 5};
 graph[4] = {3, 5};
 graph[5] = {3, 4};
 graph[6] = {7};
 graph[7] = {2, 6, 8};
 graph[8] = {1, 7};
 // DFS 실행
 DFS(1);
 return 0;
}
/*
출력 결과:
1 2 7 6 8 3 4 5
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;게임 개발에 사용되는 사례를 살펴본다면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;미로 생성 알고리즘 :&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;미로의 출구를 기저 조건이라고 생각해보면, 무작위 방향으로 이동하며 벽을 뚫고 막다른 길에 도달하면 다시 이전 갈림길로 돌아와 다른 방향(방문하지 않은 노드의 방향)으로 뚫게 만든다. 최종적으로는 미로의 출구에 도달하게 된다면 반드시 하나의 경로만 존재하는 미로를 만들 수가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대화 시스템 및 퀘스트:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보통 RPG 게임에서 선택지 시스템을 구현할 때 DFS 구조를 사용할 수 있다. 대화의 흐름을 트리 구조로 관리하기 때문에, 선택지에 따른 결말을 관리하거나, 특정 대화가 끝난 뒤 다시 이전 갈림길(상위 선택지)로 돌아와야 할 때 노드를 탐색하며 대화 내용을 불러오게 할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;퍼즐 게임&amp;nbsp;:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI가 퍼즐을 풀어나가는, 혹은 플레이어에게 힌트를 주고 싶을 때 사용할 수 있다. 현재 상태에서 가능한 수를 두고 그 다음 가능한 수를 계속 파고드는 형식이다. 정답(승리)을 기저 조건으로 두고 계속 해답을 찾아나가는 방식이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RTS 게임 &lt;b&gt;:&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;플레이어가 유닛을 이동시킬 때의 길찾기 시스템에 활용될 수 있다. 가장 빠른 이동 경로를 찾거나 막힌 길을 판단할 때 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 분할 정복 알고리즘(Divide and Conquer)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;분할 정복 알고리즘은 문제를 작게 나누고 동일한 방법으로 해결한 뒤에 결과를 결합해서 전체 문제를 푸는 방식이다. 사람이 머리를 회전시킬 때를 생각해서 대입해보면, 복잡하고 거대한 문제를 마주했을 때 감당 가능한 수준으로 쪼갠다음에 푸는 것이라고 봄면 될 것 같다. 대표적인 사례로 &lt;b&gt;병합 정렬&lt;/b&gt;이 있다. 계속 절반으로 쪼개나가면서, 각 부분 배열을 재귀적으로 병합 정렬하고 최종적으로는 정렬된 부분 배열을 병합하여 하나의 정렬된 배열로 만드는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;게임 개발에 사용되는 사례를 살펴본다면, 주로 &lt;b&gt;성능 최적화&lt;/b&gt;와 &lt;b&gt;무한한 배경 생성&lt;/b&gt; 분야에 사용된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 광범위한 충돌 감지&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대규모의 유닛이 서로 부딪히는지 확인하는 경우에, 연산량이 기하급수적으로 늘어나지 않게 하도록 분할 정복을 활용한 쿼드트리(Quadtree)를 활용한다. 전체 맵을 4개의 구역으로 나누고 각 구역에 유닛이 너무 많으면 다시 4개로 쪼개고, 재귀적으로 반복하여 유닛이 적당히 적은 상태가 되면 &lt;b&gt;같은 구역에 있는 유닛끼리만 충돌 계산&lt;/b&gt;을 하게 만드는 식이다. 충돌 걱정이 없는 구역에 있는 유닛 까지 충돌 계산을 할 필요는 없다. 불필요한 계산을 줄여 프레임을 방어하는데 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 대규모 유닛의 경로 탐색&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비슷한 논리로 대규모 유닛의 경로 탐색도 가능하다. 수백 명의 병사를 이동시킬 때 모든 타일을 전부 계산하면 연산량이 너무나 커지게되므로, 맵을 여러 개의 구역(Chunk)으로 나누어 계산하는 것이다. 구역과 구역 사이의 큰 길부터 찾고, 그 안에서 세부적인 경로를 찾아 최종적으로 경로들을 취합한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. 절차적 지형 생성&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마인크래프트처럼 무작위 지형을 만들 때 다이아몬드-스퀘어 알고리즘을 사용한다. 큰 지형에서 시작해서 세부적인 굴곡으로 파고드는 전형적인 분할 정복 방식이다. 큰 사각형의 네 꼭짓점 높이를 정하고, 중심점의 높이를 네 점의 평균값에 무작위 변수를 더해 결정한다. 계속 재귀적으로 반복해서 큰 돌덩어리를 자연스러운 굴곡을 가진 산과 계곡으로 변하게 한다고 보면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3) 재귀 함수 사용시 주의점&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 중복되는 함수 호출&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;함수가 중복되어 호출되다보니 호출되는 함수의 수가 급격히 증가하면서 금방 메모리를 고갈시킬 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서, 이미 계산한 함수의 결과를 저장해두고, 그 다음에는 다시 계산하지 않고 결과값만 불러오는 &lt;b&gt;메모이제이션&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778553543199&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
// 메모이제이션을 위한 배열 초기화
vector&amp;lt;int&amp;gt; memo(1000, -1);
// 피보나치 수열을 메모이제이션을 활용하여 재귀적으로 계산하는 함수
int fibo(int n) {
 if (n == 1 || n == 2) return 1;
 if (memo[n] != -1) return memo[n];
 memo[n] = fibo(n - 1) + fibo(n - 2);
 return memo[n];
}
int main() {
 int n = 10;
 cout &amp;lt;&amp;lt; &quot;fibo(&quot; &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;) = &quot; &amp;lt;&amp;lt; fibo(n) &amp;lt;&amp;lt; endl;
 return 0;
}
/*
출력 결과:
fibo(10) = 55
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메모이제이션을 위해서 vector&amp;lt;int&amp;gt; memo(1000, -1); 처럼 컨테이너를 만들어 초기화를 시켰고, 재귀 함수의 결과값이 나올때마다 벡터 컨테이너에 저장해두게 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 함수의 결과값이 memo에 이미 있다면, 그냥 memo[n] 을 불러온다. -&amp;gt; if (memo[n] != -1)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) 성능 개선하기&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;X의 N승을 반환하는 함수를 재귀로 구현해본다고 하자.(N은 음이 아닌 정수)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;X의 0승은 1이므로 1은 기저 조건이 된다. 따라서 X의 0승은 직접 반환한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 X의 N승을 계산하는 함수 pow(X,N)이 있다고 가정하면, pow(X,N) = pow(X, N-1) * X 이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 N이 짝수일 경우, pow(X,N) = pow(X, N/2)^2 형식으로도 나타낼 수 있다. 즉, 문제의 크기를 절반으로 바로 줄여버려서 재귀 호출 횟수와 연산 횟수를 대폭 감소시키는 것이다. 이런 논리는 꽤 많이 사용하므로 잘 숙지해두면 좋을 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1778553883252&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;
// X의 N승을 계산하는 분할 정복 재귀 함수
double pow(double X, int N) {
 if (N == 0) return 1; // 기저 조건: X^0 = 1
 double half = pow(X, N / 2); // X^(N/2) 계산
 if (N % 2 == 0) {
 return half * half; // N이 짝수인 경우: X^N = (X^(N/2))^2
 } else {
 return half * half * X; // N이 홀수인 경우: X^N = (X^(N/2))^2 * X
 }
}
int main() {
 double X = 2.0;
 int N = 10;
 cout &amp;lt;&amp;lt; &quot;pow(&quot; &amp;lt;&amp;lt; X &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; N &amp;lt;&amp;lt; &quot;) = &quot; &amp;lt;&amp;lt; pow(X, N) &amp;lt;&amp;lt; endl;
 return 0;
}
/*
출력 결과:
pow(2, 10) = 1024
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 조건문을 달아서, N이 짝수이면 N을 2로 나눈 값을 사용해서 연산을 대폭 줄이고, N이 홀수이면 N이 짝수인 경우처럼 절반을 줄여서 계산하고 X를 한번 더 곱해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*오늘의 코드카타*&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 solution은 정수 x와 자연수 n을 입력 받아, x부터 시작해 x씩 증가하는 숫자를 n개 지니는 리스트를 리턴해양 합니다. 조건을 만족하는 함수 solution을 완성해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x는 -10000000 이상, 10000000이하의 정수입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n은 1000 이하인 자연수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;==&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어에서는 지난번에 배웠던 것처럼 malloc을 이용해서 공간을 확보한 뒤 값을 도출해내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이번 문제에서는 기본적으로 vector가 포함되어있다. 즉, C++ 라이브러리 기능을 활용해서 작성하는 것을 권장한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778631599340&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;long long&amp;gt; solution(int x, int n) {
    vector&amp;lt;long long&amp;gt; answer;
    
    answer.reserve(n);
    
    for (int i = 1; i &amp;lt;= n; i++) {
        answer.push_back((long long)x * i);
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vector 컨테이너를 선언한다. 이때 x 값이 계산 도중 커질 수 있으므로 64비트 정수형인 long long을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산된 값을 push_back을 이용해서 컨테이너의 맨 끝에 계속 밀어넣을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 제시한 조건을 x*1, x*2, x*3, .... x*(n-1), x*n 식으로도 표현할 수 있으므로, x * i 를 i = 1~n 으로 반복한 후에 그 값을 밀어넣는 것과 같다.&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 코딩테스트</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/22</guid>
      <comments>https://moonwalk0515.tistory.com/22#entry22comment</comments>
      <pubDate>Tue, 12 May 2026 11:48:31 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 코딩테스트 연습] 2. 입출력 데이터 다루기, STL 사용하기, 효율적인 코드 구현하기</title>
      <link>https://moonwalk0515.tistory.com/21</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[1. 입출력 데이터 다루기]&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값을 유형별로 정리해서 효율적으로 처리해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 숫자 다루기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmath 헤더를 추가하여 사용할 수 있는 연산들.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 반올림하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 숫자를 '&lt;b&gt;가장 가까운' 정수로 변환&lt;/b&gt;하는 것이며 &lt;b&gt;round 함수&lt;/b&gt;를 사용하면 된다. &lt;b&gt;만약 0.5면 0에서 먼쪽으로 반올림&lt;/b&gt;한다. 예를 들어,&amp;nbsp; round(2.5) = 3, round(-2.5) = -3이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2) 올림, 소수점 버리기/내림&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 숫자보다 크거나 같은 가장 작은 값을 구할 때 &lt;b&gt;ceil&lt;/b&gt;함수를 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 가까운 작은 정수로 변환할 때 &lt;b&gt;floor&lt;/b&gt; 함수를 사용. 예를 들어, floor(-3.7) = -4이다. 음수에 대해서는 소수점 이하를 단순히 버리는 것이 아니라 숫자가 더 줄어든다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;2. 문자열 스트림&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;stringstream&lt;/b&gt;은 문자열을 마치 스트림처럼 다룰 수 있도록 해주는 클래스이다. 즉, 문자열을 입력 스트림처럼 처리해서 cin에서 데이터를 추출하듯 문자열에서 값을 추출할 수 있다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1) 공백 기준으로 분리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리할 문자열을 stringstream의 생성자에 전달하여 입력 스트림으로 설정한다. 그 후, &amp;gt;&amp;gt; 연산자를 이용해서 문자열에서 원하는 데이터를 &lt;b&gt;차례대로 변수에 저장&lt;/b&gt;한다. 예를 들어,&lt;/p&gt;
&lt;pre id=&quot;code_1778494729450&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;ssstream&amp;gt;
#incldue &amp;lt;string&amp;gt;

using namespace std;

int main() {

	string str = &quot;123 * 67&quot;;
	stringstream stream(str);

	int num;
	char c;
	float f;

	stream &amp;gt;&amp;gt; num &amp;gt;&amp;gt; c &amp;gt;&amp;gt; f;
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환되는 값은 123 X 67 구분되어 반환.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2) 특정 문자 기준으로 분리&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공백 대신 문자로 구분하기 위해서는 &lt;b&gt;getline&lt;/b&gt; 함수를 사용할 수 있다. 예를 들어, getline(ss, buf, ',') 와 같이 사용하면 &quot;,&quot; 기준으로 문자열을 분리할 수 있다. 일반적으로 &lt;b&gt;while 반복문과 함께&lt;/b&gt; 사용해서 문자열을 끝까지 순차적으로 분리한다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2) 진법 변환&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출력시 10진수를 16진수로 변환해야 할 때,&lt;b&gt; stringstream과 hex(조작자)&lt;/b&gt;를 활용해서 간단하게 처리할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;stringstream과 &lt;b&gt;&amp;lt;&amp;lt; 연산자&lt;/b&gt;를 활용한다. 예를 들어, ss &lt;b&gt;&amp;lt;&amp;lt; hex &amp;lt;&amp;lt;&lt;/b&gt; decimalNumber; 식으로 사용한다. ss는 16진수 문자열로 출력될 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;16진수를 10진수로 변환할 때. stringstream과 &lt;b&gt;&amp;gt;&amp;gt; 연산자&lt;/b&gt;를 활용한다. ss &lt;b&gt;&amp;gt;&amp;gt; hex &amp;gt;&amp;gt;&lt;/b&gt; decimalNumber;식으로 사용한다. ss가 16진수 &quot;ff&quot; 였다면, decimalNumber는 10진수로 변환되어 나올 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[2. STL 사용하기]&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서 표준 라이브러리로 제공되는 STL의 구조와 주요 구성 요소를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서는 컨테이너, 반복자, 알고리즘 등에 익숙해져야한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;1. STL 구성요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너, 알고리즘, 반복자가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너&lt;/b&gt;는 데이터를 저장하고 관리하는 객체이고, -&amp;gt; vector, map, list&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;알고리즘&lt;/b&gt;은 컨테이너에 저장된 데이터를 처리하는 다양한 함수를 제공하며, -&amp;gt; sort(), next_permutation()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;반복자&lt;/b&gt;는 컨테이너의 요소들을 순회하고 접근하는 방법을 제공한다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1) 반복자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너와 알고리즘이 서로 독립적으로 작동하도록 연결해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;순방향 반복자&lt;/b&gt;는 컨테이너의 요소들을 앞에서부터 차례로 순회하며, 각 원소에 접근할 수 있게 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;begin(), end(). 반복자가 가리키는 곳을 읽거나 수정할 수 있다. ++ 연산자는 지원하지만 --연산자는 지원하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역방향 반복자는 rbegin(), rend(). ++, -- 연산자 모두 지원한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2) 컨테이너&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 벡터&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배열과 매우 유사한 컨테이너지만 크기를 동적으로 조절할 수 있다. &amp;lt;vector&amp;gt; 헤더 파일이 필요. 인덱스를 통해 특정 위치의 원소에 쉽게 접근할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vector&amp;lt;int&amp;gt; vec 같이 기본 생성자를 활용한다. 선언과 동시에 초기값을 직접 지정할 수 있도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배열과 마찬가지로 [] 연산자를 사용하여 특정 위치의 원소에 직접 접근할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;벡터는 내부 데이터를 자동으로 관리하므로 메모리 구조를 직접 고려하지 않고 삽입과 삭제를 수행할 수 있다. 이때 사용하는 것이 push_back() 메서드, pop_back() 메서드, insert() 메서드이다. 모든 원소를 삭제하고 싶으면 clear() 메서드를 활용한다. 중간 삽입 시에는 지정한 번호 뒤에 삽입되고 나머지가 뒤로 한칸씩 밀리게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1778496743016&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 짝수인 원소만 0으로 변경하는 예제
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; vec = {1, 2, 3, 4, 5};
 for (size_t i = 0; i &amp;lt; vec.size(); ++i) {
 if (vec[i] % 2 == 0)
 vec[i] = 0;
 }
 for (int num : vec) {
 	cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; &quot; &quot;;
 	}
 	return 0;
}
// 출력결과: 1 0 3 0 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터를 사용할 때 비효율을 방지하기 위해서는 &lt;b&gt;reserve&lt;/b&gt;를 활용해서 메모리 재할당을 방지하는 것이 좋다. (그냥 미리 메모리를 확보하는 것)&lt;/p&gt;
&lt;pre id=&quot;code_1778496922011&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// reserve를 사용하여 메모리 재할당을 방지하는 예제
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; vec;
 vec.reserve(10); // 최대 10개의 원소를 넣을 예정이므로 미리 메모리 확보
 for (int i = 0; i &amp;lt; 10; ++i) {
 vec.push_back(i);
 cout &amp;lt;&amp;lt; &quot;size: &quot; &amp;lt;&amp;lt; vec.size() &amp;lt;&amp;lt; &quot;, capacity: &quot; &amp;lt;&amp;lt; vec.capacity() &amp;lt;&amp;lt; end
 }
 return 0;
}
/*
출력결과:
size: 1, capacity: 10
size: 2, capacity: 10
...
size: 10, capacity: 10
유의사항: reserve를 사용하면 불필요한 재할당 없이 성능 최적화 가능
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 셋(set)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중복되지 않는 원소들을 정렬된 상태로 저장하는 컨테이너&lt;/b&gt;이다. &amp;lt;set&amp;gt; 헤더 파일을 포함해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;set&amp;lt;int&amp;gt; s 와 같이 빈 셋을 선언할 수 있고, 초기화도 가능하다. 또한, set&amp;lt;int&amp;gt;s2 = s1 과 같이 기존 셋을 복사해서 새로운 셋을 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;삽입시 insert() 메서드, 삭제는 erase() 메서드, 모든 원소 삭제시에는 clear()메서드. &lt;b&gt;set의 경우에는 삽입을 하던 삭제를 하던 매번 정렬된 상태를 유지&lt;/b&gt;한다. 따라서 삽입/삭제시 시간 복잡도는 O(logN) 이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬 상태를 유지하는 특성 때문에 원소를 바로 수정할 수는 없다. 그래서 기존 원소를 삭제하고, 새로운 값을 삽입하는 방식으로 처리해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;set은 중복된 원소를 허용하지 않기 때문에 중복된 값을 저장해야 한다면 multiset을 사용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원소의 정렬이 필요하지 않고 빠른 탐색이 중요한 경우에는, 해시 기반의 unordered_set을 사용하는 것이 더 적합할 수&lt;/b&gt; 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;set은 삽입과 동시에 자동 정렬되므로 원소의 삽입 순서가 유지되지 않는다. 삽입 순서가 중요하다면 vector나 list 등의 컨테이너를 사용하는 것이 좋다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;set은 인덱스를 지원하지 않으므로 인덱스를 사용해서 원소에 접근해야 하는 경우 vector를 사용하는 것이 적합하다.&lt;/p&gt;
&lt;pre id=&quot;code_1778497238181&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// set에서 원소를 변경하려면 erase 후 insert를 사용해야 함
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;set&amp;gt;
using namespace std;
int main() {
 set&amp;lt;int&amp;gt; s = {1, 2, 3, 4, 5};
 // 3을 30으로 변경하려면, 먼저 3을 삭제하고 30 삽입
 s.erase(3);
 s.insert(30);
 for (int v : s) {
 cout &amp;lt;&amp;lt; v &amp;lt;&amp;lt; &quot; &quot;;
 }
 return 0;
}
// 출력결과: 1 2 4 5 30
// 원소를 직접 수정할 수 없기 때문에 반드시 erase + insert 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 맵(map)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;키와 값을 쌍으로 갖는 연관 컨테이너&lt;/b&gt;이다. &amp;lt;set&amp;gt; 헤더 파일을 포함해야 한다. &lt;b&gt;중복 키를 허용하지 않고 키를 기준으로 데이터가 자동으로 오름차순 정렬&lt;/b&gt;된다. &amp;lt;map&amp;gt; 헤더 파일을 포함해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;map&amp;lt;keyType, valueType&amp;gt; m 과 같이 선언한다. map&amp;lt;string, int&amp;gt; m1 = m2 와 같이 기존 맵을 복사할 수도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[] 연산자를 활용해서 맵의 값을 변경할 수 있다. &lt;b&gt;m[&quot;Alice&quot;] = 31&lt;/b&gt;. key값 Alice에 접근해서 값을 31로 변경한다. 다만 &lt;b&gt;해당 키가 없다면 새로운 키-값 쌍을 만든다&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;at() 메서드를 사용해서 맵의 값을 변경할 수도 있다. m.at(&quot;Bob&quot;) = 26. Bob 키에 연결된 값을 26으로 변경한다. 다만 [] 연산자와는 다르게 해당 키가 없으면 out_of_range 예외가 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;삽입에는 insert() 메서드를 사용한다. 이미 존재한다면 삽입은 무시되고 삽입된 위치의 반복자와 함께 false가 반환된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;삭제에는 erase() 메서드를 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 원소를 삭제하려면 clear() 메서드를 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순히 키만 필요하고 값이 필요하지 않는 경우 : set 사용이 적합&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원소의 삽입 순서를 유지해야 한다면 : vector 또는 list&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778497708395&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// [] 연산자를 사용하여 기존 값 변경 및 없는 키 추가
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
int main() {
 map&amp;lt;string, int&amp;gt; myMap = {{&quot;Apple&quot;, 1}, {&quot;Banana&quot;, 2}, {&quot;Cherry&quot;, 3}};
 myMap[&quot;Apple&quot;] = 10; // 기존 키의 값 변경
 myMap[&quot;Durian&quot;] = 4; // 새로운 키-값 쌍 추가
 for (auto&amp;amp; p : myMap) {
 cout &amp;lt;&amp;lt; p.first &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; p.second &amp;lt;&amp;lt; endl;
 }
 return 0;
}
// 출력결과:
// Apple: 10
// Banana: 2
// Cherry: 3
// Durian: 4
// [] 연산자는 키가 없을 경우 새로 추가함&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778497737096&quot; class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// insert()를 이용한 삽입 및 중복 키 처리 결과 확인
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
int main() {
 map&amp;lt;int, string&amp;gt; myMap;
 auto result1 = myMap.insert({1, &quot;Apple&quot;});
 auto result2 = myMap.insert({2, &quot;Banana&quot;});
 auto result3 = myMap.insert({1, &quot;Cherry&quot;}); // 중복 키
 cout &amp;lt;&amp;lt; &quot;삽입 1 성공 여부: &quot; &amp;lt;&amp;lt; result1.second &amp;lt;&amp;lt; endl; // true
 cout &amp;lt;&amp;lt; &quot;삽입 2 성공 여부: &quot; &amp;lt;&amp;lt; result2.second &amp;lt;&amp;lt; endl; // true
 cout &amp;lt;&amp;lt; &quot;삽입 3 (중복) 성공 여부: &quot; &amp;lt;&amp;lt; result3.second &amp;lt;&amp;lt; endl; // false
 for (auto&amp;amp; p : myMap) {
 cout &amp;lt;&amp;lt; p.first &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; p.second &amp;lt;&amp;lt; endl;
 }
	 return 0;
}
// 출력결과:
// 삽입 1 성공 여부: 1
// 삽입 2 성공 여부: 1
// 삽입 3 (중복) 성공 여부: 0
// 1: Apple
// 2: Banana&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778498040035&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// erase()를 사용하여 키 또는 반복자를 통해 원소 삭제
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;
int main() {
 map&amp;lt;int, string&amp;gt; myMap = {
 {1, &quot;Apple&quot;}, {2, &quot;Banana&quot;}, {3, &quot;Cherry&quot;}
 };
 myMap.erase(2); // 키 2 삭제
 auto it = myMap.find(3); // 키 3의 반복자 찾기
 if (it != myMap.end()) {
 myMap.erase(it); // 반복자를 통한 삭제
 }
 for (auto&amp;amp; p : myMap) {
 cout &amp;lt;&amp;lt; p.first &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; p.second &amp;lt;&amp;lt; endl;
 }
 return 0;
}
// 출력결과:
// 1: Apple
// 키 기반 삭제는 O(logN), 반복자 삭제는 평균 O(1)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;3) 알고리즘&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 사용하려면 기본적으로 &amp;lt;algorithm&amp;gt; 헤더를 포함해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) sort()&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;sort() 함수는 &lt;b&gt;기본적으로 오름차순&lt;/b&gt;으로, 범위 &lt;b&gt;[First, last)&lt;/b&gt;에 있는 원소들을 정렬한다. first는 포함하고, last는 포함하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;sort(first, last)&lt;/b&gt; 형식으로 사용하거나, &lt;b&gt;sort(first, last, comp)&lt;/b&gt; 형식으로 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보통 사용자 정의 비교 함수는 bool comp(a,b) 형식으로 선언한다. 그러니까 a, b를 비교했을 때 true 를 반환하면 a는 b보다 먼저 와야 한다는 의미가 된다. 즉, 정렬 기준을 직접 정의할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1778498201695&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용자 정의 비교 함수(comp)를 사용한 내림차순 정렬
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
bool comp(int a, int b) {
 return a &amp;gt; b; // a가 b보다 크면 앞에 와야 한다 &amp;rarr; 내림차순
}
int main() {
 vector&amp;lt;int&amp;gt; vec = {5, 2, 9, 1, 7};
 sort(vec.begin(), vec.end(), comp); // 내림차순 정렬
 for (int v : vec) {
 	cout &amp;lt;&amp;lt; v &amp;lt;&amp;lt; &quot; &quot;;
 }
 return 0;
}
// 출력결과: 9 7 5 2 1&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778498217931&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 문자열 벡터를 길이 기준으로 정렬하는 사용자 정의 comp 함수 사용
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
bool compareByLength(const string&amp;amp; a, const string&amp;amp; b) {
 return a.length() &amp;lt; b.length(); // 짧은 문자열이 앞에 오도록 정렬
}
int main() {
 vector&amp;lt;string&amp;gt; names = {&quot;Alice&quot;, &quot;Bob&quot;, &quot;Christina&quot;, &quot;Dan&quot;};
 sort(names.begin(), names.end(), compareByLength);
 for (const string&amp;amp; name : names) {
 cout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &quot; &quot;;
 }
 return 0;
}
// 출력결과: Bob Dan Alice Christina&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) find()&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;범위 [first, last) 내에서 특정 값과 일치하는 첫 번째 원소를 선형 탐색한다. &lt;b&gt;일치하는 값이 있으면 해당 원소를 가리키는 반복자를 반환하고, 없으면 last 반복자를 반환&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;find(first, last, value)&lt;/b&gt; 형식으로 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778498299525&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 찾으려는 값이 없을 때 find()가 반환하는 값 확인
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; vec = {1, 2, 3, 4, 5};
 auto it = find(vec.begin(), vec.end(), 99); // 99는 없음
 if (it == vec.end()) {
 cout &amp;lt;&amp;lt; &quot;값을 찾을 수 없습니다.&quot; &amp;lt;&amp;lt; endl;
 }
 return 0;
}
// 출력결과: 값을 찾을 수 없습니다.
// 값이 없으면 마지막 반복자(end) 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) count()&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;범위 [firrst, last) 내에서 &lt;b&gt;특정 값이 몇 번 등장하는지 계산하여 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;count(first, last, value) 형식&lt;/b&gt;으로 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778498451695&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 존재하지 않는 값에 대해 count()를 수행하는 예제
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; v = {1, 2, 3, 4};
 int result = count(v.begin(), v.end(), 10);
 cout &amp;lt;&amp;lt; &quot;10의 개수: &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; endl;
 return 0;
}
// 출력결과: 10의 개수: 0
// 값이 존재하지 않으면 count는 0 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) unique()&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;범위 [firrst, last) 내에서 &lt;b&gt;연속된 중복 요소를 제거&lt;/b&gt;한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 컨테이너에서 원소가 삭제되는 것은 아니고,&lt;b&gt; 각 원소의 첫번째 발생만 유지되도록 원소들을 앞쪽으로 덮어씌워 재배열&lt;/b&gt;한다. 재배열한 뒤 이후의 영역은 &lt;b&gt;보통 erase()와 함께 완전히 제거&lt;/b&gt;한다(그제서야 원소가 삭제됨). erase 안하면 값이 남아있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;unique 함수는 중&lt;b&gt;복 제거 후 마지막 고유 원소의 다음 위치를 가리키는 반복자를 반환&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778498643708&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// unique()는 연속된 중복 요소만 제거 (재배열만 함)
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; v = {1, 1, 2, 2, 2, 3, 1, 1};
 auto newEnd = unique(v.begin(), v.end());
 for (auto it = v.begin(); it != newEnd; ++it) {
 cout &amp;lt;&amp;lt; *it &amp;lt;&amp;lt; &quot; &quot;;
 }
 return 0;
}
// 출력결과: 1 2 3 1
// 주의: 실제 컨테이너 크기는 그대로이고, 중복 제거된 결과는 앞쪽에만 있음&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778498669778&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// unique()와 erase()를 조합해 실제로 중복 원소 삭제
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;
int main() {
 vector&amp;lt;int&amp;gt; v = {1, 1, 2, 2, 3, 3, 3};
 v.erase(unique(v.begin(), v.end()), v.end());
 for (int val : v) {
 cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; &quot; &quot;;
 }
 return 0;
}
// 출력결과: 1 2 3
// erase&amp;ndash;remove idiom을 통해 연속된 중복 요소가 완전히 제거됨&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[3. 효율적인 코드 구현하기]&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞에서 배운 것들을 활용하는 데에 있어서 중요한점이 성능을 고려해야 한다는 것이다. 굳이 쓰지 않는 기능을 가진 컨테이너, 알고리즘 등을 사용할 필요는 없다. 시간 복잡도와 실제 성능을 고려하자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1) 벡터 vs 덱&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;벡터와 덱은 둘 다 임의접근이 되기 떄문에 비슷한 컨테이너처럼 보이지만 사실 이 둘의 내부는 완전히 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;벡터&lt;/b&gt; : 배열과 동일하게 메모리가 연속적으로 할당됨. -&amp;gt; 별다른 조치 없이 임의 접근이 가능.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;덱 :&lt;/b&gt; 맨 앞의 원소와 맨 뒤의 원소를 가리키는 포인터가 있고, 내부적으로 데이터가 여러 개의 청크(chunk)라는 영역으로 쪼개짐.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) 맨 앞에 원소를 삭제(추가)하는 경우 -&amp;gt; &lt;b&gt;'덱'을 사용&lt;/b&gt;!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;벡터의 경우에는 맨 앞에 원소를 삭제(추가)하는 경우 뒤의 원소들이 전부 이동해야 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;덱&lt;/b&gt;의 경우 청크를 나눠서 관리하므로 이동하지 않아도 됨. O(1) 연산.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) 임의 접근을 하는 경우 -&amp;gt;&lt;span&gt; 임의 접근을 빈번하게 하는 경우에는 &lt;b&gt;'벡터'를 사용&lt;/b&gt;&lt;/span&gt;!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;벡터와 덱 모두 점근적 상한 기준으로는 O(1) 연산이나, 데이터가 커지면 덱이 불리해지게 됨. 덱의 경우 각 청크가 불연속적이기에 이들을 관리하는 map을 내부적으로 관리하게 되는데, 데이터가 커지면 맵을 통해 청크 위치를 찾고 접근하는 데 필요한 연산 횟수가 증가하기 때문.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2) 정렬이 필요없는데 정렬하는 경우&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컨테이너 중 set과 같은 컨테이너는 원소가 삽입될 때마다 자동으로 정렬해준다. 그런데 굳이 정렬을 할 필요가 없다면 사용할 필요가 없다는 뜻이기도 하다. 또한 map의 경우에도 키를 삽입할 때마다 연산이 발생되므로 코딩테스트에서 잘못 사용하면 불리할 수도 있다. 이경우에는 키값을 기준으로 정렬이 필요 없다면 unordered_map 사용을 고려해보아야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778499376870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ---------------- map ----------------
 map&amp;lt;int, int&amp;gt; ordered_map;
 auto start_map = chrono::high_resolution_clock::now();
 for (int i = 0; i &amp;lt; N; ++i) {
 ordered_map[i] = i;
 }
 
 // ---------------- unordered_map ----------------
 unordered_map&amp;lt;int, int&amp;gt; unordered_map;
 auto start_umap = chrono::high_resolution_clock::now();
 for (int i = 0; i &amp;lt; N; ++i) {
 unordered_map[i] = i;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;3) 특정 원소를 찾는 경우&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 원소를 찾는 경우에도 컨테이너에 따라 성능 차이가 크게 발생할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;벡터는&lt;/b&gt; 선형 컨테이너이고, 특정 데이터에 접근하기 위해서는 해당 데이터의 인덱스를 통해 임의 접근해야한다. 다만 인덱스에는 데이터에 대한 특별한 정보가 없어 결국 &lt;b&gt;순차 탐색을 해야&lt;/b&gt;한다. O(N).&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;set과 map&lt;/b&gt;은 데이터를 레드 블랙 트리로 관리한다. O(logN).&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;unordered_set과 unordered_map&lt;/b&gt;. unordered가 붙는 컨테이너는 해시 기반으로 관리한다. O(1).&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;4) 문자열을 결합하는 경우&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자열에 문자를 추가하는 연산의 경우에도 큰 성능의 차이가 발생할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(1) + 연산자 : 매번 새로운 문자열을 다시 만든다. 기존 문자열 길이가 N 이라면, O(N)이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(2) &lt;b&gt;+= 연산자와 append() 메서드&lt;/b&gt; : 매번 새로운 문자열을 만들지 않고 기존 문자열에 덧붙이는 방식이다. O(1)에 가깝다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;5) auto &amp;amp; 과 auto&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반복문을 사용할 때&amp;nbsp; auto와 auto&amp;amp;로 받는 것은 큰 성능 차이가 발생할 수 있다. auto는 복사가 발생하지만, &lt;b&gt;auto&amp;amp;는 복사가 발생하지 않는다&lt;/b&gt;. 다만 auto&amp;amp;로 접근해서 값을 받아올 때 값의 변경이 발생하지 않도록 const auto&amp;amp; 형식으로 보통 같이 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;*오늘의 코드카타*&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자연수 n이 매개변수로 주어진다. n을 x로 나눈 나머지가 1이 되도록 하는 가장 작은 자연수 x를 return 하도록 solution 함수를 완성해주세요. (제한사항 : 3 &amp;lt;= n &amp;lt;= 1,000,000)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778540498909&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int solution(int n) {
    int answer = 0;
    
    int x;
    for (x = 2; x &amp;lt; n; x++) {
        if (n % x == 1) {
            answer = x;
            break;
        }
    }
        
        
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나머지가 1이려면 최소한 2 이상의 수로 나눠야 하므로 x는 2부터 시작해서 n보다는 작은 반복문 for를 작성했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 조건에 맞는 수를 찾으면 멈추도록 break;를 사용했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 코딩테스트</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/21</guid>
      <comments>https://moonwalk0515.tistory.com/21#entry21comment</comments>
      <pubDate>Mon, 11 May 2026 20:42:31 +0900</pubDate>
    </item>
    <item>
      <title>[Text RPG 만들기] 4. 전투 중 아이템 사용(시스템 연결) (도전 STEP4)</title>
      <link>https://moonwalk0515.tistory.com/19</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. Battle.h&lt;/h4&gt;
&lt;pre id=&quot;code_1778225871017&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Battle {

public:
	static Item startBattle(Player* player, Monster&amp;amp; monster, vector&amp;lt;Item&amp;gt;&amp;amp; inventory);

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Battle 클래스가 인벤토리 데이터에 직접 접근하고 아이템을 차감할 수 있도록 startBattle()의 매개변수에 인벤토리를 추가로 넘겨줌. vector라는 상자 안에는 오직 Item 형태의 데이터만 들어갈 수 있고, &amp;amp;(레퍼런스)을 이용해서 직접 복사하는 게 아니라 inventory 원본 자체에 직접 연결함.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1-2. Battle.cpp&lt;/h4&gt;
&lt;pre id=&quot;code_1778226441261&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;algorithm&amp;gt;	//min 사용을 위해 추가

while (player-&amp;gt;getHP() &amp;gt; 0 &amp;amp;&amp;amp; monster.getHP() &amp;gt; 0) {	//둘다 살아있을 때 전투루프

	cout &amp;lt;&amp;lt; &quot;\n--- 플레이어 턴 ---&quot; &amp;lt;&amp;lt; endl;				//플레이어 턴, 플레이어 메뉴
	cout &amp;lt;&amp;lt; &quot;1. 공격&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;2. 아이템 사용&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;선택: &quot;;

	int inBattleChoice;
	cin &amp;gt;&amp;gt; inBattleChoice;

	if (inBattleChoice == 1) {
		player-&amp;gt;attack(&amp;amp;monster);
	}

	else if (inBattleChoice == 2) {
		cout &amp;lt;&amp;lt; &quot;\n[ 인벤토리 ]&quot; &amp;lt;&amp;lt; endl;
		//인벤토리에 아이템이 없을 때
		if (inventory.empty()) {
			cout &amp;lt;&amp;lt; &quot;사용할 수 있는 아이템이 없습니다.&quot; &amp;lt;&amp;lt; endl;
		}
		else {
			int i = 1;
			for (const auto&amp;amp; item : inventory) {
				cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;. &quot;;	//인벤토리 번호 출력
				item.PrintItemInfo();	//Item.h 에 정의되어있는 함수
				i++;
			}
			cout &amp;lt;&amp;lt; &quot;0. 취소&quot; &amp;lt;&amp;lt; endl;
			cout &amp;lt;&amp;lt; &quot;사용할 아이템 번호: &quot;;

			int itemChoice;
			cin &amp;gt;&amp;gt; itemChoice;

			if (itemChoice == 0) {
				continue;

			}
			//아이템을 선택하고 사용하는 로직.
			else if (itemChoice &amp;gt; 0 &amp;amp;&amp;amp; itemChoice &amp;lt;= inventory.size()) {
				int itemIndex = itemChoice - 1;
				string itemName = inventory[itemIndex].name;

				if (itemName == &quot;HP포션&quot;) {	//hp포션
					int currentHP = player-&amp;gt;getHP();
					int healAmount = 50;
					player-&amp;gt;setHP(min(currentHP + healAmount, player-&amp;gt;getMaxHP()));
					cout &amp;lt;&amp;lt; &quot;* HP 포션 사용! HP &quot; &amp;lt;&amp;lt; healAmount &amp;lt;&amp;lt; &quot; 회복 (&quot; &amp;lt;&amp;lt; currentHP &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; player-&amp;gt;getHP() &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
				}
				else if (itemName == &quot;MP포션&quot;) {	//mp포션
					int currentMP = player-&amp;gt;getMP();
					int healAmount = 50;
					player-&amp;gt;setMP(min(currentMP + healAmount, player-&amp;gt;getMaxHP()));
					cout &amp;lt;&amp;lt; &quot;* MP 포션 사용! MP &quot; &amp;lt;&amp;lt; healAmount &amp;lt;&amp;lt; &quot; 회복 (&quot; &amp;lt;&amp;lt; currentMP &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; player-&amp;gt;getMP() &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
				}
				else {
					cout &amp;lt;&amp;lt; &quot;전투 중에 사용할 수 없는 아이템입니다.&quot; &amp;lt;&amp;lt; endl;
					continue;
				}
				//아이템을 사용했으면 for문을 빠져나와서 인벤토리에서 차감
				if (itemName == &quot;HP포션&quot; || itemName == &quot;MP포션&quot;) {
					inventory[itemIndex].count--;
					if (inventory[itemIndex].count == 0) {
						inventory.erase(inventory.begin() + itemIndex);	//사용 후 아이템이 없으면 인벤토리에서 지워줌.
					}
				}
			}
			else {
				cout &amp;lt;&amp;lt; &quot;숫자를 잘못 입력하였습니다.&quot; &amp;lt;&amp;lt; endl;
				continue;
			}
		}
	}
	else {
		cout &amp;lt;&amp;lt; &quot;올바른 번호를 입력하여 메뉴를 선택하세요.&quot; &amp;lt;&amp;lt; endl;
		continue;
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;min 함수를 사용하기 위해 #include &amp;lt;algorithm&amp;gt;을 추가하고,&lt;span&gt;&amp;nbsp; int inBattleChoice; 를 선언해서 전투 중 메뉴 선택을 구현. 1을 눌렀을 때 부모 클래스에서 선언한 순수 가상함수를 불러와서 몬스터에게 공격, 2를 눌렀을 때 인벤토리를 보여주고 아이템 목록이 출력되도록 변경. 잘못된 숫자를 입력하면 다시 입력하라는 문자열이 출력되고 다시 이전 메뉴로 돌아감. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인벤토리에서 아이템을 문자열로 출력하는 데에는 for문을 사용. int i = 1;로 초기화 시켜 놓고 인덱스 첫번째 부터 i = 1, 2, 3 순서대로 출력되도록 했음. 짝을 맞추어 item.printItemInfo() 함수를 사용해서 아이템 이름과 번호를 매칭시켰음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출력된 아이템을 선택하는 부분에서는 입력한 번호를 아까 저장해 놓은 i와 대조해서 일치하면 해당 아이템이 사용가능한지 확인하고, 0을 누르면 이전 메뉴로 돌아가는 로직을 작성했음. 0을 누르면 문자열이 출력되고 continue;를 통해서 while문이 시작되는 위치로 돌아가게 됨.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용할 아이템 번호를 선택하는 것에서는 세가지 케이스가 있을 수 있음. 1. 사용할 수 있는 아이템을 선택, 2. 사용할 수 없는 아이템을 선택, 3. 이상한 번호를 입력.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 사용할 수 있는 아이템의 경우 : 우선 입력한 숫자의 크기가 인벤토리의 i 숫자보다 작거나 같은 지 테스트 해야 함. 즉, (itemChoice &amp;gt; 0 &amp;amp;&amp;amp; itemChoice &amp;lt;= inventory.size()) 조건을 만족해야 함. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인벤토리안에 만들어진 방의 크기가 현재 i 변수의 끝 값과 같다는 점을 이용. 조건을 만족한 경우, inventorty에 저장되어 있는 itemIndex와 itemChoice-1 을 비교해서 같은 경우에,&amp;nbsp; inventory에서 itemIndex와 매칭되는 name을 뽑아와서 string itemName에 대입하게 됨. 여기서&amp;nbsp; itemIndex는 0부터 시작하고, itemChoice는 우리가 i = 1 부터 시작하도록 했기 때문에 -1을 해야 함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후 HP포션을 잘 찾아서 번호를 잘 입력하면 HP포션이 사용됨. 체력을 채우기 위해 int currentHP와 int healAmount를 선언하고, 각각 player-&amp;gt;getMP(), 50으로 초기화. player-&amp;gt;setMP()를 이용해서 체력을 채우게 했고, 이때 min값을 활용해서 최대치를 넘어 회복되지 않게 작성하였음. (min(currentHP + healAmount, player-&amp;gt;getMaxHP()).&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아이템을 성공적으로 사용한 경우, 즉, 내가 검색한 itemChoice로 인해 뽑아온 item.name이 HP포션이거나 MP포션일 경우, inventory[itemIndex].count--; 를 실행. 이후 또다시 조건문을 달아서 count--를 실행한 결과 inventory[itemIndex].count가 0이 된 경우 inventory.erase(inventory.begin() + itemIndex); &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;풀어서 생각해보면, 2번째 칸 아이템을 다 써서 삭제하고 싶을 때 itemIndex 는 0에서부터 시작한다. 따라서 itemIndex는 1이다. inventory.begin()은 기본적으로 첫번째 원소를 찾으므로 원소들이 줄을 서있는 맨 앞쪽으로 가서 시작 위치를 잡는 거라고 생각하면 된다.&amp;nbsp; 항상 컴퓨터식 사고방식을 해야한다. &quot;inventory의 맨 처음 위치에서 시작해서 itemIndex만큼(한 칸 뒤에 있는) 이동한 곳에 있는 데이터를 삭제해라.&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 사용할 수 없는 아이템의 경우 : 문자열을 출력하고 continue를 사용해서 while문 처음으로 다시 돌아가도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778228669477&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	if (monster.getHP() &amp;lt;= 0) {
		cout &amp;lt;&amp;lt; &quot;\n★ 전투 승리!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; &quot; &amp;lt;&amp;lt; monster.getDropItemName() &amp;lt;&amp;lt; &quot; 획득!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; 인벤토리에 저장되었습니다.&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;\n계속하려면 아무 키나 누르세요.&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause &amp;gt; nul&quot;);

		return Item(monster.getDropItemName(), monster.getDropItemPrice(), 1);
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후 전투 승리 시 아이템을 얻을 때 Monster.h에서 정의한 함수를 가져와 아이템이름, 아이템가격, 갯수(1개)를 받아오게 수정.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가적으로 Monster.h에 getDropItemPrice(); 함수가 선언되어 있지 않아 추가로 선언하였음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. Player.h&lt;/h4&gt;
&lt;pre id=&quot;code_1778229100862&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;public:
	int getMaxHP() { return maxHp; }
	int getMaxMP() { return maxMp; }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;포션 사용의 최대 회복치를 구현하기 위해 Player.h에 getMaxHP, getMaxMP 함수를 추가했음.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 텍스트게임 만들기</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/19</guid>
      <comments>https://moonwalk0515.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 8 May 2026 17:33:49 +0900</pubDate>
    </item>
    <item>
      <title>[C++학습] 8. 객체지향적 설계</title>
      <link>https://moonwalk0515.tistory.com/18</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[1] 객체지향적 설계&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 응집도&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응집도는 클래스 또는 모듈 내부의 구성 요소들이 얼마나 밀접하게 관련되어 있는지를 나타낸다. 응집도는 높을수록 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 응집도가 낮은 경우란 서로 관련 없는 기능들이 하나의 클래스에 포함된 경우를 말한다. 관련 없는 기능들이 모여있으면, 나중에 유지 보수가 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 클래스에는 밀접하게 관련 있는 기능들만 모아놓을 수 있도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 결합도&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결합도는 모듈 또는 클래스 간의 의존성을 나타낸다. 결합도는 낮을수록 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 또는 클래스 간의 의존성이 강해지면 하나의 모듈이 변경될 때 다른 모듈도 영향을 받게 된다. 다시말해 유지보수가 어려워진다.&lt;/p&gt;
&lt;pre id=&quot;code_1778197080215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

class Engine {
public:
 string state;
 Engine() : state(&quot;off&quot;) {}
 void start() {
 state = &quot;on&quot;;
 }
};

class Car {
public:
 Engine engine;
 
 void startCar() {
 if (engine.state == &quot;off&quot;) {
 engine.start();
 cout &amp;lt;&amp;lt; &quot;Car started&quot; &amp;lt;&amp;lt; endl;
 }
 }
};

int main() {

 return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Car 클래스가 Engine 클래스에 강하게 의존하고 있다. 이렇게 되면 main() 함수에서 ElectricEngine을 사용하고 싶을 때 Car 클래스를 넘어 Engine까지 전체 구조를 수정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결합도가 낮아지는 일을 방지하려면 결국 코드를 작성하기 전에 아키텍처를 그리고 가는게 좋아보인다. 예를 들어, 위의 Car과 Engine의 관계를 보면, 자동차는 Engine이 장착되어 있고, 자동차에 시동이 걸리면 엔진이 동작한다. 엔진의 종류가 Diesel과 Electric으로 나뉘기 때문에 자동차가 여러 종류의 엔진에 엮여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 결합도를 낮추기 위해서는 Car 클래스가 DieselEngine, ElectricEngine으로 직접 포함하게 할 것이 아니라, 인터페이스를 활용해서 새로운 엔진을 추가해도 자동차 코드를 수정할 필요가 없게 만드는 것이 좋아보인다.&lt;/p&gt;
&lt;pre id=&quot;code_1778197738773&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
using namespace std;

//Engine 클래스
class Engine {
public:
 virtual void start() = 0;
 virtual ~Engine() = default;
};

//DieselEngine 클래스는 Engine 클래스에 붙이고
class DieselEngine : public Engine {
public:
 void start() {
 cout &amp;lt;&amp;lt; &quot;Diesel Engine started&quot; &amp;lt;&amp;lt; endl;
 }
};

//Electric 클래스는 Engine 클래스에 붙이고
class ElectricEngine : public Engine {
public:
 void start() {
 cout &amp;lt;&amp;lt; &quot;Electric Engine started silently&quot; &amp;lt;&amp;lt; endl;
 }
};

// Car 클래스는 Engine 인터페이스에만 의존하게 하기
class Car {
private:
 unique_ptr&amp;lt;Engine&amp;gt; engine;
public:
 Car(unique_ptr&amp;lt;Engine&amp;gt; eng) : engine(move(eng)) {}
 void startCar() {
 engine-&amp;gt;start();
  cout &amp;lt;&amp;lt; &quot;Car started&quot; &amp;lt;&amp;lt; endl;
 }
};

int main() {
 // DieselEngine을 사용할 때
 auto dieselEngine = make_unique&amp;lt;DieselEngine&amp;gt;();
 Car dieselCar(move(dieselEngine));
 dieselCar.startCar();
 
 // ElectricEngine을 사용할 때
 auto electricEngine = make_unique&amp;lt;ElectricEngine&amp;gt;();
 Car electricCar(move(electricEngine));
 electricCar.startCar();
 return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1) Car 클래스는 Diesel/Electric엔진을 참조하지 않고 추상 클래스인 Engine 인터페이스에만 의존하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;2) Car 클래스에서는 unique_ptr을 생성자 매개변수로 받아서 사용. Car클래스는 어떤 엔진과 동작하는지 알 필요가 없어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;3) 다른 엔진 모델이 추가가 된다고 해도, Car 클래스의 코드는 수정할 필요가 없어졌다. 마찬가지로 Engine 추상 클래스의 start()함수만 유지되면 Car 클래스는 영향을 받지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;4) Engine 클래스의 경우 순수 가상 함수를 사용하면서 추상 클래스가 되었다. 부모 클래스로서 자식 클래스(Diesel, Electric, ...) 의 기능을 일관되게 호출할 목적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. SOLID 원칙&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진정한 객체지향 프로그래밍을 하기 위한 설계 원칙이 SOLID 원칙이다. 유지 보수성과 확장성을 위해서 코드를 작성할 때 체크할 수 있도록 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;단일 책임 원칙(Single Responsibility Principle, SRP) : 하나의 클래스는 하나의 역할을&lt;/li&gt;
&lt;li&gt;개방-폐쇄 원칙(Open-Closed Princciple, OCP) : 기존 코드 수정 없이 기능을 확장할 수 있도록&lt;/li&gt;
&lt;li&gt;리스코프 치환 원칙(Liskov Substitution Principle, LSP) : 자식은 부모를 완벽히 대체 가능하도록&lt;/li&gt;
&lt;li&gt;인터페이스 분리 원칙(Interface Segregation Principle, ISP) : 필요한 인터페이스만 제공하도록&lt;/li&gt;
&lt;li&gt;의존 역전 원칙(Dependency Inversion Principle, DIP) : 인터페이스나 추상 클래스에 의존하도록&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[2] 디자인 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 생성 패턴(싱글톤 패턴의 경우)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 객체를 만들어내는 방법과 관련된 패턴. 싱글톤 패턴의 경우, &lt;b&gt;프로그램 전체에서 특정 클래스의 인스턴스를 단 하나만 생성하고 어디서든 동일한 인스턴스에 접근할 수 있도록 보장하는 패턴&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778200675203&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

class Airplane {

private:	// 1)
	static Airplane* instance;
	int positionX;
	int positionY;

	Airplane(): positionX(0), positionY(0) {
		cout &amp;lt;&amp;lt; &quot;Airplane Created at (&quot; &amp;lt;&amp;lt; positionX &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; positionY &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
	}

public:
	Airplane(const Airplane&amp;amp;) = delete;	// 3)
	Airplane&amp;amp; operator = (const Airplane&amp;amp;) = delete;

	static Airplane* getInstance() {	//2)
		if (instance == nullptr) {
			instance = new Airplane();
		}
		return instance;
	}

	void move(int deltaX, int deltaY) {
		positionX += deltaX;
		positionY += deltaY;
		cout &amp;lt;&amp;lt; &quot;Airplane moved to (&quot; &amp;lt;&amp;lt; positionX &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; positionY &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
	}

	void getPosition() const {
		cout &amp;lt;&amp;lt; &quot;Airplane Position: (&quot; &amp;lt;&amp;lt; positionX &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; positionY &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
	}
};

Airplane* Airplane::instance = nullptr;

int main() {

	Airplane* airplane = Airplane::getInstance();	// 4)
	airplane-&amp;gt;move(10, 20);
	airplane-&amp;gt;getPosition();

	Airplane* sameairplane = Airplane::getInstance();	// 4)
	sameairplane-&amp;gt;move(-5, 10);
	sameairplane-&amp;gt;getPosition();

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;1) &lt;b&gt;Airplane() 생성자를 private 영역에 배치&lt;/b&gt;해서 외부에서 new 키워드를 사용해서 마음대로 비행기 객체를 생성하지 못하도록 막는다. 그 대신 &lt;b&gt;Airplane* 타입의 정적 변수인 instance를 두어서 이 변수가 유일한 객체의 주소를 저장&lt;/b&gt;하도록 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;2) 외부에서 이 클래스를 사용하려면 반드시 &lt;b&gt;public영역에서 static으로 선언된 getInstance() 함수를 호출&lt;/b&gt;해야 함. 처음 호출될 때만 객체를 생성하고 그 이후부터는 &lt;b&gt;이미 만들어진 객체의 주소만 반환&lt;/b&gt;하기 때문에 어떤 프로그램의 위치에서든 유일한 비행기 객체를 불러내도록 함. (Airplane::getInstance(); 방식으로 접근)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;3) 싱글톤 패턴의 안전장치로써 코드 중간에 delete키워드가 붙은 복사 생성자와 대입 연산자를 사용. 실수로 객체를 복사하는 상황을 막는 장치.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;4) 결국 airplane, sameairplane이라는 다른 이름의 포인터를 사용해도 실제로 같은 메모리 공간을 가리키게 됨. 두 변수가 하나의 비행기를 조종하게 하였음.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 구조 패턴(데코레이터 패턴의 경우)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러 개의 객체들의 구조를 어떻게 구성할지에 관련된 패턴. &lt;b&gt;객체에 동적으로 새로운 책임(기능)을 추가할 수 있게 해주는 패턴&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본 객체를 중심에 두고, 다른 기능을 가진 클래스로 감싸는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;추상 클래스 Pizza,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본 객체인 BasicPizza,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데코레이터 PizzaDecorator,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구체적인 기능을 가진 클래스 Cheese, Pepperoni, Olive.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;메인함수에서는 토핑을 더한 후의 피자 이름과 피자 가격이 출력되도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778204039419&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

class Pizza {

public:
	virtual ~Pizza() {}
	virtual string getName() const = 0;
	virtual double getPrice() const = 0;
};


class BasicPizza : public Pizza {

public:
	string getName() const {
		return &quot;Basic Pizza&quot;;
	}
	double getPrice() const {
		return 5.0;
	}
};

class PizzaDecorator : public Pizza {

protected:
	Pizza* pizza;
	
public:
	PizzaDecorator(Pizza* p) : pizza(p) {}

	virtual ~PizzaDecorator() {
		delete pizza;
	}
};

class CheeseDecorator : public PizzaDecorator {
public:
	CheeseDecorator(Pizza* p) : PizzaDecorator(p) {}
	string getName() const {
		return pizza-&amp;gt;getName() + &quot; + Cheese&quot;;
	}
	double getPrice() const {
		return pizza-&amp;gt;getPrice() + 1.5;
	}
};

class PepperoniDecorator : public PizzaDecorator {
public:
	PepperoniDecorator(Pizza* p) : PizzaDecorator(p) {}
	string getName() const {
		return pizza-&amp;gt;getName() + &quot; + Pepperoni&quot;;
	}
	double getPrice() const {
		return pizza-&amp;gt;getPrice() + 2.0;
	}
};

class OliveDecorator : public PizzaDecorator {
public:
	OliveDecorator(Pizza* p) : PizzaDecorator(p) {}
	string getName() const {
		return pizza-&amp;gt;getName() + &quot; + Olive&quot;;
	}
	double getPrice() const {
		return pizza-&amp;gt;getPrice() + 0.7;
	}
};

int main() {

	Pizza* pizza = new BasicPizza();

	pizza = new CheeseDecorator(pizza);
	pizza = new PepperoniDecorator(pizza);
	pizza = new OliveDecorator(pizza);

	cout &amp;lt;&amp;lt; &quot;Pizza: &quot; &amp;lt;&amp;lt; pizza-&amp;gt;getName() &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;Price: $&quot; &amp;lt;&amp;lt; pizza-&amp;gt;getPrice() &amp;lt;&amp;lt; endl;

	delete pizza;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;1) 추상 클래스 Pizza를 정의하고 모든 피자가 갖출 규칙을 정의. Pizza를 상속받는 모든 클래스는 반드시 이름과 가격을 알려주는 기능을 구현해야 함.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;2) Pizza를 상속받는 BasicPizza 클래스. 이름과 가격을 &quot;Basic Pizza&quot;, 5.0 으로 구현했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;3) Pizza를 상속받는 PizzaDecorator 클래스. 생성자에서 Pizza* p 를 받아서 멤버 변수에 저장하고, 소멸자에서는 자신이 감싸고 있던 피자 객체까지 함께 메모리에서 해제한다. Pizza* pizza; 라고 하면, 최종적으로는 모든 토핑이 붙은 pizza가 남아있게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;(소멸자?)소멸자는 클래스 이름 앞에 ~ 기호를 붙여 정의하고, 객체가 사라질 때 그 자원을 다시 시스템에 돌려준다. 상속관계가 있는 클래스에서는 소멸자에 virtual 키워드를 붙이는 것이 매우 중요. 부모 클래스인 Pizza의 소멸자에 virtual이 없다면 부모 타입의 포인터로 자식 객체를 삭제할 때 부모의 소멸자만 호출되고 자식 클래스의 토핑이나 기능들이 메모리에 그대로 남아버린다. 모든 자원을 해제하기 위해 virtual을 붙이는 것.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;4) 구체적인 토핑 클래스에서는 PizzaDecorator 클래스를 상속받는다. 따라서 반드시 부모 클래스의 생성자를 먼저 호출해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;CheeseDecorator(Pizza* p) : ==&amp;gt; 치즈 자식 클래스가 만들어질 때 피자 p 하나를 받고, : (콜론) 을 통해 초기화를 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;PizzaDecorator(p) {} ==&amp;gt; 내가 받은 p (피자)를 PizzaDecorator 클래스의 생성자에게 그대로 전달한다. 부모가 할일을 줬으니 딱히 더 할건 없다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;5) 메인 함수는 이름과 가격이 합산된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;생성자를 통해 새로운 BasicPizza가 만들어진다. 이 때 부모 클래스인 Pizza* 타입의 포인터로 받는다. pizza라는 객체의 주소값을 계속 Decorator에서 가리키게 만들어서 새로운 pizza 객체로 만들어줘야 하기 때문이다. 또한 pizza-&amp;gt;getName(), pizza-&amp;gt;getPrice()와 같이 매번 똑같은 명령을 쉽게 내릴 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;6) 결론적으로 새로운 토핑이 필요하면 새로운 Decorator 클래스 하나를 추가하면 된다. (개방-폐쇄 원칙의 사례이기도 함)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 Decorator 가 없으면 어떻게 될까.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 코드 중복의 발생, 관리 효율성 문제이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1) 포인터와 로직 변경&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;포인터 Pizza* pizza 대신에 unique_ptr을 사용하기로 변경되었을 때, Decorator가 있으면 그냥 포인터 타입과 로직을 한번만 수행하면 끝난다. 그 아래 자식 클래스는 건드릴 필요가 거의 없다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 Decorator가 없으면 Cheese, Pepperoni... 등의 클래스에 포인터 Pizza* pizza가 직접 삽입되어 있을 것이기 때문에, 일일이 전부 찾아가서 포인터와 로직을 수정해주어야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2) 토핑을 추가하는 과정에서 추가적인 기능을 삽입할 때 (토핑 개수를 세는 기능을 삽입하는 사례)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 Decorator가 있으면 부모 클래스인 Decorator에 함수를 만들면 모든 자식 클래스에 기능이 자동으로 적용된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 Decorator가 없으면 모든 개별 토핑 클래스에 변수와 로직을 복사해서 일일이 붙여넣어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3) delete 과정을 보장함.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Decorator 클래스는 토핑 클래스들의 공통된 포인터와 공통된 소멸 행위를 하나로 모아 관리할 수 있게 해준다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Decorator 클래스가 없다면 delete 과정도 모든 개별 토핑 클래스에 붙여 넣어주며 신경써야한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3&lt;/b&gt;&lt;b&gt;. 행동 패턴(옵저버 패턴의 경우)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 개체가 변할 때 다른 객체들과의 상호작용과 관련된 패턴. &lt;b&gt;데이터의 변경을 여러 객체에 자동으로 알린다&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Subject&lt;/b&gt;는 상태를 관리하고 변경되었음을 옵저버에게 알리고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Observer&lt;/b&gt;는 Subject를 관찰하며 상태 변경 시 반응한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778207563258&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

// 관찰 대상의 변화를 전달받음(인터페이스)
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(int data) = 0;
};

// 데이터의 상태를 가지고 있는 관찰 대상(주체)
class ExcelSheet {
private:
    vector&amp;lt;Observer*&amp;gt; observers;
    int data;

public:
    ExcelSheet() : data(0) {}

    //옵저버를 등록
    void attach(Observer* observer) {
        observers.push_back(observer);
    }

    //옵저버를 해제
    void detach(Observer* observer) {
        observers.erase(remove(observers.begin(), observers.end(), observer), observers.end());
    }

    //모든 옵저버에게 변경 사항을 알리기
    void notify() {
        for (Observer* observer : observers) {
            observer-&amp;gt;update(data);
        }
    }

    // 데이터를 수정하고 알림 보내기
    void setData(int newData) {
        data = newData;
        cout &amp;lt;&amp;lt; &quot;ExcelSheet: Data updated to &quot; &amp;lt;&amp;lt; data &amp;lt;&amp;lt; endl;
        notify();
    }
};

//옵저버: 막대 그래프
class BarChart : public Observer {
public:
    void update(int data) override {
        cout &amp;lt;&amp;lt; &quot;BarChart: Displaying data as vertical bars: &quot;;
        for (int i = 0; i &amp;lt; data; ++i) {
            cout &amp;lt;&amp;lt; &quot;|&quot;;
        }
        cout &amp;lt;&amp;lt; &quot; (&quot; &amp;lt;&amp;lt; data &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
    }
};

//옵저버: 선 그래프
class LineChart : public Observer {
public:
    void update(int data) override {
        cout &amp;lt;&amp;lt; &quot;LineChart: Plotting data as a line: &quot;;
        for (int i = 0; i &amp;lt; data; ++i) {
            cout &amp;lt;&amp;lt; &quot;-&quot;;
        }
        cout &amp;lt;&amp;lt; &quot; (&quot; &amp;lt;&amp;lt; data &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
    }
};

//옵저버: 파이 차트
class PieChart : public Observer {
public:
    void update(int data) override {
        cout &amp;lt;&amp;lt; &quot;PieChart: Displaying data as a pie chart slice: &quot;;
        cout &amp;lt;&amp;lt; &quot;Pie [&quot; &amp;lt;&amp;lt; data &amp;lt;&amp;lt; &quot;%]&quot; &amp;lt;&amp;lt; endl;
    }
};

int main() {
    ExcelSheet excelSheet;

    BarChart* barChart = new BarChart();
    LineChart* lineChart = new LineChart();
    PieChart* pieChart = new PieChart();

    excelSheet.attach(barChart);
    excelSheet.attach(lineChart);
    excelSheet.attach(pieChart);

    excelSheet.setData(5);
    excelSheet.setData(10);

    delete barChart;
    delete lineChart;
    delete pieChart;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1) &lt;b&gt;Observer&lt;/b&gt; 클래스 : 모든 차트 객체들의 부모인 추상 클래스. ExcelSheet 라는 관찰 대상으로부터 변화를 전달받기 위해 update 함수 정의.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;vector&amp;lt;Observer*&amp;gt; observers; 를 선언해서 옵저버 명단을 담을 수 있는 공간을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;2) ExcelSheet 클래스 : 변화의 주체(&lt;b&gt;Subject&lt;/b&gt;). 내부적으로 데이터를 보관하고, 데이터가 변할 때마다 옵저버들에게 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;attach(), detach() 함수를 통해서 관찰 대상에 알림을 받을 객체(Observer)를 추가(push_back)하거나 제거(erase)할 수 있다. 이후 &lt;b&gt;notify()&lt;/b&gt; 함수를 통해 등록된 모든 옵저버의 update() 함수를 순차적으로 호출(for문을 사용해서 Observers 명단을 확인)하고, 업데이트 하라고 신호만 보낸다. setdata() 함수는 데이터를 실제로 변경하는 함수로, 값이 바뀌면 자동으로 notify() 함수를 호출하게 했다(데이터가 변경되면 Observer에게 알림을 보내도록).&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;3) 각각의 Chart 클래스는 Observer를 상속받아 실제로 화면에 데이터를 그리는 구체적인 객체들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;4) main함수에서 new 객체들을 생성 후, attach() 함수를 통해 서로 연결한다. 마지막에는 동적 할당한 메모리를 해제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;*옵저버 명단 제거 시 쓰는 함수가 어려워서 적었음*&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;// observers.erase(remove(observers.begin(), observers.end(), observer), observers.end());&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;remove 함수&lt;/b&gt;는 내가 삭제할 데이터를 제외한 &lt;b&gt;'살려야 할 데이터를 앞으로 밀어내고', '삭제할 데이터를 맨 뒤로 보낸 다음', '삭제하고 싶은 데이터가 시작되는 위치를 가리키고 반환'&lt;/b&gt;하는 함수이다. 따라서 &lt;b&gt;erase와 함께 자주 사용&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;erase 함수&lt;/b&gt;는 시작지점부터 끝지점까지를 잘라내는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 둘이 결합해서 옵저버 명단을 제거하는 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이런 머리 아픈 건 안쪽에 있는 것부터 확인하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;1) remove(observers.begin(), observers.end(), observer) : observers 명단의 처음부터 끝까지 훑어서, 우리가 삭제하고 싶은 observer 찾기. 찾으면 해당 observer를 &lt;b&gt;맨 뒤로 밀어내고&lt;/b&gt;, &lt;b&gt;삭제할 observer가 시작되는 지점의 주소값을 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;2) observers.erase(remove(), observers.end()); : remove()를 통해 삭제할 observer가 시작되는 지점의 주소값이 반환되었고, erase()를 실행할 시작점이 정해졌다. 당연히 삭제할 녀석이 맨 뒤로 간 상태이므로 observers.end()를 통해 끝부분까지 삭제하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;3) 공식처럼 정리하면&amp;nbsp;&lt;b&gt;observers.erase(remove(시작, 끝, 삭제하고 싶은 대상), observers.end());&amp;nbsp;&lt;/b&gt;꼴로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이렇게 사용하면 중간에 있는 걸 지우면 뒤의 데이터를 다 옮겨와야 되는 불상사를 미리 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 오늘의 코드카타 *&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자연수 N이 주어지면, N의 각 자릿수의 합을 구해서 return 하는 solution 함수를 만드세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N의 범위는 100,000,000 이하의 자연수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778195195420&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int solution(int n) {
   int sum = 0;
    for (sum = 0; n &amp;gt; 0; n /= 10) {
        sum += n % 10;
    }
    return sum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자연수 N은 10진법이니까 10으로 나눴을 때의 나머지가 소수점 한자리가 나올 거라고 생각해보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 그 나머지 값은 1의 자리의 자릿수 값이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 어차피 n은 int 자료형이니까, 소수점은 강제로 제거되고 맨 뒷자리 숫자만 빠진 10진법 숫자만 남게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 10으로 나누면 10의 자리의 자릿수 값이 소수점 한자리로 나오게 되고, 그 값이 10의 자리의 자릿수 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 for문을 활용한 반복문으로 구현해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int sum = 0; 은, for문에서 어차피 sum = 0;으로 초기화를 하니까 int sum; 으로 선언만 해도 충분할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문이니까 while문으로도 작성해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1778195783915&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int solution(int n) {
	int answer = 0;
    
    while (n&amp;gt;0) {
    	answer += n % 10;
   		n /= 10;
    }
    
	return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/게임 개발을 위한 C++</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/18</guid>
      <comments>https://moonwalk0515.tistory.com/18#entry18comment</comments>
      <pubDate>Fri, 8 May 2026 11:58:44 +0900</pubDate>
    </item>
    <item>
      <title>[C++ 코딩테스트 연습] 1. 필수 문법</title>
      <link>https://moonwalk0515.tistory.com/17</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 변수&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 변수?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;프로그래밍에서 변수는 데이터를 저장하고 처리하는 데 사용되는 구성 요소이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 스칼라&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;스칼라 타입은 하나의 데이터를 저장하는 타입을 의미한다. 대표적으로 정수형(int), 부동 소수형(float or double), 논리형(bool), 문자형(char)가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 배열&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;배열은 하나의 이름으로 &lt;b&gt;동일한 타입의 변수를 묶어서 관리&lt;/b&gt;할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;배열의 각 원소는 메모리상에 연속적인 공간에 저장되므로, &lt;b&gt;인덱스를 사용해 원하는 위치의 원소에 바로 접근할 수 있다&lt;/b&gt;. 인덱스는 0부터 시작하므로 예를들어 score[1]은 배열의 2번째 원소를 의미하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;배열은 타입 특성상 전체 배열을 직접 대입하는 것이 불가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1778149755863&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

int main(void) {
	int score[5] = { 70, 80, 90, 85, 75 };
	int sum = 0;

	for (int i = 0; i &amp;lt; 5; i++) {
		sum += score[i];
	}
	double average = sum / 5.0;
	cout &amp;lt;&amp;lt; &quot;평균: &quot; &amp;lt;&amp;lt; average &amp;lt;&amp;lt; endl;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;ex) 배열의 합&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 문자열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문자열은 문자들이 연속적으로 나열된 데이터다. C++에서는 문자열을 사용하기 위해 #include&amp;lt;string&amp;gt; 헤더를 포함해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 문자열 찾기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문자열 안에서 특정 문자 또는 문자열을 찾을 수 있는데 이때 find() 메서드를 사용한다. &lt;b&gt;값을 찾았다면 시작 위치를 인덱스로 반환&lt;/b&gt;하고, &lt;b&gt;찾지 못했다면 string::npos라는 이미 정의되어있는 값을 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778150700435&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

int main(void) {
	string text = &quot;Winner Chicken Dinner!&quot;;

	size_t pos1 = text.find(&quot;Chicken&quot;);
	size_t pos2 = text.find(&quot;!&quot;);
	size_t pos3 = text.find(&quot;Pizza&quot;);
	
	cout &amp;lt;&amp;lt; &quot;Chicken 위치: &quot; &amp;lt;&amp;lt; pos1 &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;! 위치: &quot; &amp;lt;&amp;lt; pos2 &amp;lt;&amp;lt; endl;
	if (pos3 = string::npos) {
		cout &amp;lt;&amp;lt; &quot;No Pizza.&quot; &amp;lt;&amp;lt; endl;
		}
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 문자열 추가, 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문자열을 추가/결합하기 위해서 &lt;b&gt;+ 연산자&lt;/b&gt; 혹은 &lt;b&gt;+= 연산자&lt;/b&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 문자열 내의 특정 문자를 수정하고 싶으면 &lt;b&gt;[ ] 연산자&lt;/b&gt; 혹은&lt;b&gt; replace()&lt;/b&gt; 메서드를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1778151259306&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

int main(void) {
	string text = &quot;Winner Chicken Dinner!&quot;;

	string text2 = &quot;Winner &quot; + text;
	cout &amp;lt;&amp;lt; text2 &amp;lt;&amp;lt; endl;

	text2 += &quot;!!&quot;;
	cout &amp;lt;&amp;lt; text2 &amp;lt;&amp;lt; endl;

	text2.replace(text2.find(&quot;Chicken&quot;), 7, &quot;PIZZA&quot;);
	cout &amp;lt;&amp;lt; text2 &amp;lt;&amp;lt; endl;

	text2[text2.find(&quot;!&quot;)] = '?';	//&quot;&quot; 가 아니라 ''에 주의
	cout &amp;lt;&amp;lt; text2 &amp;lt;&amp;lt; endl;

	return 0;
}

//결과값 : 
Winner Winner Chicken Dinner!
Winner Winner Chicken Dinner!!!
Winner Winner PIZZA Dinner!!!
Winner Winner PIZZA Dinner?!!&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 그 외 중요 문자열 메서드&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;length() : 문자열의 길이를 반환. str.length()&lt;/li&gt;
&lt;li&gt;size() : 문자열의 길이를 반환. str.size()&lt;/li&gt;
&lt;li&gt;empty() : 문자열이 비어있으면 true, 아니면 false. str.empty()&lt;/li&gt;
&lt;li&gt;clear() : 문자열을 비움. str.clear()&lt;/li&gt;
&lt;li&gt;insert() : 삽입 위치에 문자열을 삽입. str.insert(삽입위치, 문자열)&lt;/li&gt;
&lt;li&gt;erase() : 시작 위치부터 삭제할 길이만큼 삭제. str.erase(시작위치, 삭제할 길이)&lt;/li&gt;
&lt;li&gt;substr() : 시작 위치부터 길이만큼 문자열 반환. str.substr(시작위치, 길이)&lt;/li&gt;
&lt;li&gt;compare() : 사전 순 비교해서 &amp;lt; 0 , = 0 , &amp;gt; 0 반환. str.compare()&lt;/li&gt;
&lt;li&gt;push_back : 문자열의 끝에 문자를 추가. str.push_back()&lt;/li&gt;
&lt;li&gt;pop_back : 문자열의 마지막 문자 제거. str.pop_back()&lt;/li&gt;
&lt;li&gt;toupper() : 변환할 문자의 소문자를 대문자로 변환 (대문자를 소문자로는 tolower()&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1778152220568&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

int main(void) {
	string text = &quot;hello, world!&quot;;

	for (int i = 0; i &amp;lt; text.length(); i++) {
		text[i] = toupper(text[i]);
		
	}
	cout &amp;lt;&amp;lt; text;
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;대표적으로 문자열 전체를 대문자로 바꾸는 코드를 작성해보았다. toupper()는 문자 하나에 사용하기 때문에 반복문을 활용해서 전체 문자열을 대문자로 바꾸어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;마찬가지로 특정 조건을 만족했을 때에만 대문자로 바꿀 수 있게 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1778154321948&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
using namespace std;

int main() {
	string ID = &quot;2025_KIM&quot;;

	string year = ID.substr(0, 4);	//4번째 인덱스 이전까지.
	string name = ID.substr(5);	//길이는 생략해도 됨.

	cout &amp;lt;&amp;lt; &quot;년도: &quot; &amp;lt;&amp;lt; year &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;이름: &quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; endl;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;substr() 을 이용해서 ID를 입력했을 때 년도와 이름을 구분할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 코딩테스트 할 때 필수 체크 사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;And &amp;amp;&amp;amp; 연산자와 Or || 연산자를 사용할 때 잘 구분하자(단락평가 주의).&lt;/li&gt;
&lt;li&gt;&amp;amp;&amp;amp; 연산자와 || 연산자 + 조건문의 형식에서는 true (=1), false (=0) 값을 잘 구분하자.&lt;/li&gt;
&lt;li&gt;배열 접근 시 인덱스가 경계를 벗어나지 않도록 주의하자. 인덱스 i는 0부터 시작하고 end()는 마지막 원소의 다음을 지칭한다.&lt;/li&gt;
&lt;li&gt;나누기, 나머지 연산자 사용시 0으로 나누는 상황이 발생하지 않도록 조심하자. 만약 0으로 나눌 수 있는 상황이 있다면 조건문으로 분모가 0인지 검사하자.&lt;/li&gt;
&lt;li&gt;자료형에 맞는 크기의 값을 사용하는지 확인하자(오버플로우 발생에 주의).&lt;/li&gt;
&lt;li&gt;중간 계산 과정에서 오버플로우가 발생하지 않도록 주의하자(나누기를 곱셈보다 먼저 하는 게 좋은 이유).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ex) 단락평가&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;논리 연산자 &amp;amp;&amp;amp; 와 || 는 조건식에서 &lt;b&gt;첫 번째 피연산자의 평과 결과만으로 전체 표현식의 값을 결정할 수 있는 경우&lt;/b&gt; &lt;b&gt;두 번째 피연산자를 평가하지 않는다&lt;/b&gt;. 이를 단락 평가라고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A &amp;amp;&amp;amp; B에서 A가 false인 경우, B는 평가되지 않는다. 어차피 전체 조건식이 무조건 false이기 때문.&lt;/li&gt;
&lt;li&gt;A || B 에서 A가 true인 경우, B는 평가되지 않는다. 어차피 전체 조건식이 무조건 true이기 때문.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 편한거 아니냐, 할 수 있는데 사실은 주의해야 할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나는 두 조건을 모두 평가한 뒤에 결과값을 도출한다고 생각하고 코드를 작성했지만, 실제로는 하나만 평가되어 예상치 못한 동작이 발생하게 되는 경우&lt;/b&gt;가 생길 수 있다. 이러한 오류를 최소화하려면 조건식에서는 함수 호출 결과를 직접 사용하지 않는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1778155261685&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bool is_ready = true;
if (is_ready || update_system()) { ..... }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;예를 들어 위에서 볼 수 있듯, is_ready가 true니까 뒤의 update_system() 함수는 아예 쳐다도 안보게 된다. 결국 시스템은 업데이트를 하지 않은 채 다음 로직으로 넘어가게 되는 것이다.&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 코딩테스트</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/17</guid>
      <comments>https://moonwalk0515.tistory.com/17#entry17comment</comments>
      <pubDate>Thu, 7 May 2026 23:41:46 +0900</pubDate>
    </item>
    <item>
      <title>[Text RPG 만들기] 3. setPotion 함수, 레벨 &amp;amp; 경험치 시스템, 직업별 공격 오버라이딩(도전 STEP1~ 도전  STEP3)</title>
      <link>https://moonwalk0515.tistory.com/16</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. main.cpp 수정 부분&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778138779466&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	//step3.
	int hpPotion = 0;
	int mpPotion = 0;
	bool isGameStart = false;
	//포인터를 사용해서 변수의 메모리 주소를 넘겨줘야 해당 주소로 직접 찾아가서 조작할 수 있음.
	setPotion(5, &amp;amp;hpPotion, &amp;amp;mpPotion);	//변수의 메모리 주소를 넘기기 위해 &amp;amp; 연산자를 사용하였음

	cout &amp;lt;&amp;lt; &quot;\n* HP 포션 5개, MP 포션 5개가 기본 지급되었습니다.&quot; &amp;lt;&amp;lt; endl;
	system(&quot;pause &amp;gt; nul&quot;);

	while (!isGameStart) {
		system(&quot;cls&quot;);
   ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1&quot;&gt;포인터를 활용한 포션 개수 초기화 함수 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;main 함수 위쪽에 메모리 주소를 받아 포션 개수를 설정해 주는 setPotion 함수를 새로 추가. 이에 따라 기존에는 int hpPotion = 5; 처럼 바로 숫자를 넣어서 초기화했지만, 이제는 0으로 초기화한 뒤 &lt;b&gt;setPotion(5, &amp;amp;hpPotion, &amp;amp;mpPotion);을 호출&lt;/b&gt;해서 포인터의 주소값을 통해 값을 변경하도록 수정.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778140804134&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;		cin &amp;gt;&amp;gt; MenuChoice;

		switch (MenuChoice) {

			//던전에 입장하면 랜덤 몬스터(MonsterSpawn)가 소환되고, 전투 루프가 시작 + 루프가 끝나면 인벤토리에 아이템 더하기
		case 1: {
			//물약을 먹은 후 던전 입장하면 버프가 1 차감 되어야 함. Player.h에 정의해놓음.
			player-&amp;gt;consumeBuff();
			//MonsterSpawn 클래스의 정적함수인 RandomNormalMonster()를 호출
			  Monster currentMonster = MonsterSpawn::RandomNormalMonster();
			//Battle 클래스의 startBattle함수 실행. 전투가 끝난 후 droppedItem 변수에 아이템 정보 받음.
			  Item droppedItem = Battle::startBattle(player, currentMonster);

			  //전투루프 종료 후 플레이어가 살아있다면 경험치를 획득하게
			  if (player-&amp;gt;getHP() &amp;gt; 0) {
				  player-&amp;gt;earnExp(currentMonster.getExpReward());
			  }

			  //인벤토리에 이미 같은 아이템이 있는지 확인
			  if (droppedItem.name != &quot;&quot;) {
				  bool isInventoryFound = false;
				  //같은 아이템이 있는 경우
				  //inventory는 vector를 사용하기에 auto&amp;amp; 으로 처음부터 끝까지 스캔.
				  for (auto&amp;amp; item : inventory) {	
					  if (item.name == droppedItem.name) {
						  item.count += droppedItem.count;	//아이템 카운트를 1 늘림.
						  isInventoryFound = true;
						  break;
					  }
				  }
				  //같은 아이템이 없는 경우
				  if (!isInventoryFound) {
					  if (inventory.size() &amp;lt; inventory_MAX) {
						  inventory.push_back(droppedItem);	//인벤토리에 새 칸을 추가하기 위해 push_back 사용.
					  }
					  else {
						  cout &amp;lt;&amp;lt; &quot;   -&amp;gt; 인벤토리가 가득 차서 [&quot; &amp;lt;&amp;lt; droppedItem.name &amp;lt;&amp;lt; &quot;]을(를) 버렸습니다.&quot; &amp;lt;&amp;lt; endl;
					  }
				  }
			  }
			  break;
		}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;던전 입장 시 버프 턴 차감 로직 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메인 메뉴에서 1번 던전 입장을 선택했을 때, 몬스터가 소환되기 직전에 player-&amp;gt;consumeBuff(); 코드 추가. 주석에 적어둔 것처럼, 포션 제작소에서 만든 포션을 사용하고 버프를 얻은 상태로 던전에 입장하면 버프 지속 횟수를 1씩 차감하기 위한 기능. (아직 포션 사용 기능은 만들지 않았음)&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;전투 종료 후 몬스터 경험치 획득 로직 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;던전에서 전투(Battle::startBattle)가 끝난 직후에 플레이어가 살아있는지(player-&amp;gt;getHP() &amp;gt; 0) 확인하는 조건. 플레이어가 전투에서 생존했다면 player-&amp;gt;earnExp(currentMonster.getExpReward()); 코드가 실행되면서 잡은 몬스터의 경험치를 보상으로 획득하도록 로직을 업데이트.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2-1. Player.h 변경 부분&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778140972502&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef PLAYER_H_
#define PLAYER_H_

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

class Monster; //Monster 클래스 전방 선언

class Player {

protected:
	string name;
	string job;
	int level;
	int hp, mp, power, defence;
	int exp;
	int maxExp;
	int maxHp;
	int maxMp;

	int staminaBuffTurnsLeft = 0;
	int powBuffTurnsLeft = 0;

public:
	Player(string name, int hp, int mp, int power, int defence);

	virtual ~Player() {}	//상속 목적의 부모 클래스의 가상 소멸자

	//virtual void attack() = 0; 기존의 순수 가상 함수
	virtual void attack(Monster* monster) = 0;	//-&amp;gt; 각 직업별 함수 오버라이드(몬스터 클래스 전방 선언 이후 변경)

	void printPlayerStatus();	//전직 이후의 상태창

	//경험치 획득 및 레벨업
	void earnExp(int amount);
	void levelUp();

	//포션 사용 함수
	void useHpPotion();
	void useMpPotion();
	//void useStaminaPotion();
	//void useStrengthPotion();

	//getter와 setter 함수. name, job, hp, mp, power, defence
	string getName() { return name; }

	string getJob() { return job; }

	int getHP() { return hp; }	//hp값을 반환해줘야 하니까 int.
	void setHP(int value) { hp = value; }	//반환할 필요는 없으니까 void. value는 공간을 만들어놓음.

	int getMP() { return mp; }
	void setMP(int value) { mp = value; }

	int getPower() { return power; }
	int getDefence() { return defence; }

	//경험치 관련 getter setter
	int getExp() { return exp; }
	int getMaxExp() { return maxExp; }

	//던전 3회 동안 공격력 2배 -&amp;gt; 버프 상태, 남은 횟수, 공격력에서 조건 추가
	int getBuffPower() { return (powBuffTurnsLeft &amp;gt; 0) ? power * 2 : power; }

	void useStaminaPotion() { staminaBuffTurnsLeft = 3; }
	void useStrengthPotion() { powBuffTurnsLeft = 3; }

	void consumeBuff() {
		if (staminaBuffTurnsLeft &amp;gt; 0) {
			staminaBuffTurnsLeft--;
		}
		if (powBuffTurnsLeft &amp;gt; 0) {
			powBuffTurnsLeft--;
		}
	}
};
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1&quot;&gt;몬스터 클래스 전방 선언 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;파일 위쪽에 class Monster;라는 전방 선언 추가. Player 클래스 안에서 Monster 객체를 사용할 떄, 서로 헤더 파일을 참조하면서 생길 수 있는 꼬임 현상을 방지하기 위해 미리 알려주는 역할.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;새로운 상태와 버프 관련 멤버 변수 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;protected 영역에 캐릭터의 성장을 위한 경험치 관련 변수(exp, maxExp)와 최대 체력/마나를 기억할 변수(maxHp, maxMp)추가. 그리고 포션 제작소와 연동될 버프 지속 턴 수를 계산하기 위해 staminaBuffTurnsLeft와 powBuffTurnsLeft 변수를 0으로 초기화된 상태로 추가.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;공격(attack) 함수 구조 변경&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원래는 virtual void attack() = 0;으로 허공에 공격하는 형태였다면, 업데이트 후 virtual void attack(Monster* monster) = 0;으로 바꿈. 전방선언을 한 이유가 이 포인터를 쓰기 위함이었음. 플레이어가 특정 몬스터를 타겟으로 지정해서 공격할 수 있도록 몬스터 매개변수를 받게 됨.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;경험치 및 레벨업 관련 함수 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메인 코드에서 전투가 끝난 뒤 경험치를 얻던 로직을 처리할 수 있게 void earnExp(int amount);와 void levelUp(); 함수가 선언되었고, 이 값을 외부에서 확인할 수 있도록 하단에 getExp()와 getMaxExp() 함수도 함께 추가.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;포션 및 버프 시스템 관련 함수 대거 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기본 포션을 사용하기 위한 useHpPotion(), useMpPotion() 선언을 추가. 특정 포션을 먹으면 남은 턴 수를 3으로 늘려주는 useStaminaPotion()과 useStrengthPotion(), 던전에 입장할 때마다 버프 턴을 1씩 깎는 consumeBuff(), 그리고 공격력 버프가 남아있을 때 실시간으로 공격력을 2배로 뻥튀기해서 반환해 주는 getBuffPower()추가.(버프 시스템 때문에 선언한 함수가 많아졌음)&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2-2. Player.cpp 변경 부분&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778141086227&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Player.h&quot;

//Player:: Player클래스 안에 있는,
//Player(...) 생성자. 캐릭터가 생성될 때 한번 생성.
// : name(), hp()~ 멤버 초기화 리스트. 생성자 생성한 후에 외부에서 받아온 매개변수로 초기화해라.
Player::Player(string name, int hp, int mp, int power, int defence)
	: name(name), hp(hp), mp(mp), maxHp(hp), maxMp(mp), power(power), defence(defence), level(1), job(&quot;초보자&quot;), exp(0), maxExp(100) {
}

//경험치 획득
void Player::earnExp(int amount) {
	//던전 3회 동안 경험치 2배 -&amp;gt; 버프 상태, 남은 횟수, 경험치 로직에 조건 추가. 물약 상태면 amount를 2배로 세팅.
	if (staminaBuffTurnsLeft &amp;gt; 0) {
		amount *= 2;
	}
	
	exp += amount;
	cout &amp;lt;&amp;lt; &quot;★ 전투 승리!&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;  -&amp;gt; 경험치 +&quot; &amp;lt;&amp;lt; amount &amp;lt;&amp;lt; &quot; 획득! (현재 경험치: &quot; &amp;lt;&amp;lt; exp &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; maxExp &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; endl;
	if (staminaBuffTurnsLeft &amp;gt; 0) {
		cout &amp;lt;&amp;lt; &quot;현재 스태미나포션의 효과로 경험치를 2배 획득했습니다. (&quot; &amp;lt;&amp;lt; staminaBuffTurnsLeft &amp;lt;&amp;lt; &quot;)회 남음&quot; &amp;lt;&amp;lt; endl;
	}

	//if대신 while을 써서 조건값이 true면 반복되도록 했음.
	while (exp &amp;gt;= maxExp) {
		levelUp();
	}
	//수정 이전. 레벨이 1씩밖에 오르지 않았음. 레벨 1 오르면 exp = 0 으로 초기화.
	//if (exp &amp;gt;= maxExp) {
	//	levelUp();
	//}
}

//레벨업 + 경험치 초기화
void Player::levelUp() {
	int previousLevel = level;
	level++;

	maxHp += 10;
	maxMp += 5;
	power += 5;

	hp = maxHp;
	mp = maxMp;

	//레벨업 했을 때 경험치를 0으로 초기화하는 게 아니라 레벨업 요구치만큼 차감하면 됨.
	exp -= maxExp;
	//exp = 0; (이전 버전)
	maxExp += 50;	//요구 경험치 50씩 증가.

	cout &amp;lt;&amp;lt; &quot;★ 레벨 업! Lv.&quot; &amp;lt;&amp;lt; previousLevel &amp;lt;&amp;lt; &quot; -&amp;gt; Lv.&quot; &amp;lt;&amp;lt; level &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;  -&amp;gt; HP + 10, MP +5, 공격력 +5 증가!&quot; &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; endl;
	system(&quot;pause&quot;);
}

//exp, lecel 추가해서 새롭게 상태창 함수 작성
void Player::printPlayerStatus() {
	cout &amp;lt;&amp;lt; &quot;------------------------------------&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;닉네임: &quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &quot; | 직업: &quot; &amp;lt;&amp;lt; job &amp;lt;&amp;lt; &quot; | Lv.&quot; &amp;lt;&amp;lt; level &amp;lt;&amp;lt; &quot; (&quot; &amp;lt;&amp;lt; exp &amp;lt;&amp;lt; &quot; / &quot; &amp;lt;&amp;lt; maxExp &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;HP: &quot; &amp;lt;&amp;lt; hp &amp;lt;&amp;lt; &quot; / &quot; &amp;lt;&amp;lt; maxHp &amp;lt;&amp;lt; &quot; | MP: &quot; &amp;lt;&amp;lt; mp &amp;lt;&amp;lt; &quot; / &quot; &amp;lt;&amp;lt; maxMp &amp;lt;&amp;lt; &quot; | 공격력: &quot; &amp;lt;&amp;lt; power &amp;lt;&amp;lt; &quot; | 방어력: &quot; &amp;lt;&amp;lt; defence &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;------------------------------------&quot; &amp;lt;&amp;lt; endl;
}

//포션때문에 useHp, Mp 포션 만들어야 했음
void Player::useHpPotion() {

	hp += 20;

	if (hp &amp;gt; maxHp) {
		hp = maxHp;
	}
}

void Player::useMpPotion() {

	mp += 20;

	if (mp &amp;gt; maxMp) {
		mp = maxMp;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;1&quot;&gt;생성자 초기화 리스트 확장&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐릭터가 처음 만들어질 때 실행되는 생성자의 초기화 리스트에 항목들을 추가. 기존의 기본 스탯뿐만 아니라 최대 체력maxHp, maxMp, exp = 0(초기 경험치), maxExp = 100 를 한 번에 세팅하도록 업데이트.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;경험치 획득 및 버프 연동 로직 구현&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;earnExp 함수를 추가. 전투 승리 시 경험치를 올려주도록 간단하게 작성할 수 있으나, 스태미나 버프(staminaBuffTurnsLeft &amp;gt; 0)가 켜져 있으면 추가 경험치를 획득하는 로직까지 포함하였음. 또한 레벨업 판정을 if문 대신 while문으로 변경해서 한 번에 많은 양의 경험치를 받았는데도 레벨업이 하나만 되지 않도록, 연속으로 레벨업할 수 있도록 변경.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;레벨업 능력치 상승 및 잔여 경험치 이월&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;levelUp 함수의 경우. 레벨이 오르면 최대 체력이 10, 최대 마나가 5, 공격력이 5 상승하고, 현재 체력과 마나를 최대치까지 채워주는 기능을 포함. 레벨업 시 경험치를 0으로 초기화하지 않고 exp -= maxExp;를 사용해서 레벨업에 쓰인 만큼만 차감하고 남은 경험치는 이월시키도록 변경.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;상태창 출력 정보 디테일 추가&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;printPlayerStatus 함수의 변경. 레벨 옆에 현재 경험치와 요구 경험치 비율을 띄워주고, 체력과 마나도 현재 수치와 최대 수치를 슬래시(/)로 구분하였음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;포션 사용 시 최대치 초과 방지 기능&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;마지막으로 useHpPotion과 useMpPotion 함수를 추가. 체력과 마나를 20씩 올려주고, 만약 포션을 먹고 회복된 수치가 최대치(maxHp, maxMp)를 넘어가게 되면 최대치까지만 맞춰지도록 제한하였음.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. MonsterSpawn.cpp&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778141334951&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//MonsterSpawn.cpp
//Monster(string name, int hp, int power, int defence, string item, int price, int expReward)
	case 0:
		return Monster(&quot;슬라임&quot;, 30, 20, 10, &quot;슬라임의 끈적한 젤리&quot;, 30, 20);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;경험치 획득 로직이 추가되어서, 몬스터에게 각각 경험치를 부여했다. (expReward. Monster.h에 생성자가 존재)&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;4. Monster.h&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778141567963&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Monster {
private:
	string name;
	int hp, power, defence;
	int expReward;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; private 영역에 int expReward; 변수를 추가 정의. 몬스터가 가지고 있는 경험치 보상 수치를 저장할 목적.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778141589896&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public:	//생성자
	Monster(string name, int hp, int power, int defence, string item, int price, int expReward)
		: name(name), hp(hp), power(power), defence(defence), dropItemName(item), dropItemPrice(price), expReward(expReward) {
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 새로운 변수가 추가됨에 따라 생성자의 매개변수를 하나 늘렸음. 마지막 인자로 int expReward를 받으며, 초기화 리스트를 통해 멤버 변수를 초기화하도록 수정. 따라서 MonsterSpawn에서 경험치 값을 추가로 입력했음.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778141635223&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	//몬스터가 가지고 있는 expReward getter함수
	int getExpReward() { return expReward; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;getter함수를 추가해서 몬스터의 경험치 보상 값을 가져올 수 있도록 했음.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;5. Battle.cpp&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778142730292&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Battle.h&quot;
#include &quot;Item.h&quot;
#include &amp;lt;iostream&amp;gt;

using namespace std;

Item Battle::startBattle(Player* player, Monster&amp;amp; monster) {
	cout &amp;lt;&amp;lt; &quot;\n===========================================&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;[ 전투 시작! ] &quot; &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot;(&quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;) vs &quot; &amp;lt;&amp;lt; monster.getName() &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;===========================================&quot; &amp;lt;&amp;lt; endl;

	while (player-&amp;gt;getHP() &amp;gt; 0 &amp;amp;&amp;amp; monster.getHP() &amp;gt; 0) {	//둘다 살아있을 때 전투루프

		cout &amp;lt;&amp;lt; &quot;\n--- 플레이어 턴 ---&quot; &amp;lt;&amp;lt; endl;				//플레이어 턴, 플레이어 공격
		player-&amp;gt;attack(&amp;amp;monster);

		//몬스터가 사망했을 경우 문자열을 추가로 출력 -&amp;gt; Win 방향으로
		if (monster.getHP() &amp;lt;= 0) {
			cout &amp;lt;&amp;lt; &quot; (사망)&quot; &amp;lt;&amp;lt; endl;
			break;	//몬스터가 사망했으므로 전투루프 종료
		}

		//몬스터의 공격
		cout &amp;lt;&amp;lt; &quot;\n--- 몬스터 턴 ---&quot; &amp;lt;&amp;lt; endl;
		monster.attack(player);
		cout &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot; HP: &quot; &amp;lt;&amp;lt; player-&amp;gt;getHP() &amp;lt;&amp;lt; endl;
	
		//플레이어가 패배했을 경우 문자열을 추가로 출력 -&amp;gt; Lose 방향으로
		if (player-&amp;gt;getHP() &amp;lt;= 0) {
			cout &amp;lt;&amp;lt; &quot;\n[ 패배 ] &quot; &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot;님은 패배했습니다.&quot; &amp;lt;&amp;lt; endl;
			break;
		}
	}

	//이제 전투 루프 탈출했으니까 승리 or 패배 텍스트 출력
	if (monster.getHP() &amp;lt;= 0) {
		cout &amp;lt;&amp;lt; &quot;\n★ 전투 승리!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; &quot; &amp;lt;&amp;lt; monster.getDropItemName() &amp;lt;&amp;lt; &quot; 획득!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; 인벤토리에 저장되었습니다.&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;\n계속하려면 아무 키나 누르세요.&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause &amp;gt; nul&quot;);

		return Item(monster.getDropItemName(), 50, 1);	//생성자를 활용해서 가독성을 높였음.
	}

	if (player-&amp;gt;getHP() &amp;lt; 0) {
		cout &amp;lt;&amp;lt; &quot;..전투에서 패배하였습니다..&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;..메인 메뉴로 돌아가세요..&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause &amp;gt; nul&quot;);
		return Item();	//함수를 종료하고 startBattle 시작 전으로 돌아감.
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가장 큰 변화는 책임 분산, 그리고 코드가 하나의 클래스에 집중되는 것을 막고자 했음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;데미지 계산 및 HP 갱신 로직의 이동&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원래는 Battle::startBattle 함수 내부에서 직접 플레이어의 공격력과 몬스터의 방어력을 계산하고, setHP를 통해 몬스터의 체력을 깎았으나, &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;player-&amp;gt;attack(&amp;amp;monster); 으로 대체. 공격 시 발생하는 데미지 계산과 상대방의 HP 감소 처리를 Player 클래스의 attack 메서드가 전담하도록 변경하였음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;함수 인자 전달 방식의 변화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전에는 player-&amp;gt;attack()을 인자 없이 호출했으나, &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;player-&amp;gt;attack(&amp;amp;monster)와 같이 공격 대상인 monster의 주소값을 인자로 넘기게 변경. 플레이어가 누구를 공격하는지 확인하고 대상 객체에 직접 접근해서 상태를 변화시킬 수 있게 하기 위함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;코드의 간결성 및 가독성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;불필요한 지역 변수(playerDamage, beforeMonsterHP) 선언과 조건문(데미지가 0 이하일 때 1로 고정하는 로직 등)을 제거했음. startBattle 함수의 가독성을 올리기 위함이었음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;텍스트 출력의 변화&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 버전에서는 monster.getHP() 변화 과정을 &quot;HP: 100 -&amp;gt; 80&quot;과 같이 Battle 클래스에서 직접 출력했지만, 변경 이후에는 상세한 전투 수치 출력을 Player::attack 메서드 내부로 옮겼음.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;6. 직업 헤더파일, 소스 파일(ex)Thief.h, Thief.cpp)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;h4 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1) Thief.h&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778143021301&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef THIEF_H_
#define THIEF_H_

#include &quot;Player.h&quot;

//전방 선언은 Monster가 있다는 것만 알리기(Player.h가 있으니까)
class Monster;
//자식 클래스 Thief가 부모 클래스 Player(public만)를 상속하도록 만듬.
class Thief : public Player {

public:
	Thief(string name, int hp, int mp, int power, int defence);
	void attack(Monster* monster) override;
};

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;선언과 구현의 분리&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생성자(Thief(...))와 attack() 함수의 구체적인 동작이 헤더 파일(.h) 안에 모두 포함되어 있는 인라인(Inline) 형태였었으나, &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이제는 헤더에 함수를 선언만 함.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Monster 클래스와의 상호작용 추가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;attack(Monster* monster)와 같이&lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;4,1,0&quot;&gt; Monster 객체의 포인터&lt;/b&gt;를 인자로 받아서 도적(플레이어)이 단순히 공격 모션을 취하는 것을 넘어 특정 몬스터에게 대미지를 입히는 등의 직접적인 상호작용을 가능하게 했음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;전방 선언(Forward Declaration) 활용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;class Monster;라는 전방 선언이 추가되었고, 이를 통해 헤더 간의 상호 참조 문제를 방지했음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;함수 시그니처 변경 (오버라이딩 조건)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;부모 클래스인 Player의 attack 함수도 Monster*를 인자로 받도록 함께 업데이트하였음.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;2) Thief.cpp&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1778143052364&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Thief.h&quot;
#include &quot;Monster.h&quot;
#include &amp;lt;iostream&amp;gt;

using namespace std;

 Thief::Thief(string name, int hp, int mp, int power, int defence) : Player(name, hp, mp, power, defence) {
	job = &quot;도적&quot;;	//직업명
	power += 10;		//직업 특화 스탯
}

void Thief::attack(Monster* monster) {
	int damage = (getPower() - monster-&amp;gt;getDefence()) / 5;
	if (damage &amp;lt;= 0) {
		damage = 1;
	}

	int beforeMonsterHP = monster-&amp;gt;getHP();

	for (int i = 0; i &amp;lt; 5; i++) {
		monster-&amp;gt;setHP(monster-&amp;gt;getHP() - damage);
	}

	cout &amp;lt;&amp;lt; &quot;[도적] 단검을 찌른다! -&amp;gt; &quot; &amp;lt;&amp;lt; monster-&amp;gt;getName() &amp;lt;&amp;lt; &quot;에게 &quot; &amp;lt;&amp;lt; damage &amp;lt;&amp;lt; &quot; 데미지! (x5)&quot; &amp;lt;&amp;lt; endl;	//직업 특수 문자열 출력
	cout &amp;lt;&amp;lt; monster-&amp;gt;getName() &amp;lt;&amp;lt; &quot; HP: &quot; &amp;lt;&amp;lt; beforeMonsterHP &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; monster-&amp;gt;getHP();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;범위 지정 연산자(::) 사용:&lt;/b&gt; Thief::Thief나 Thief::attack처럼 함수 이름 앞에 Thief::를 붙여, 이 함수들이 Thief 클래스에 소속된 것임을 명시.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;생성자(Constructor)의 역할 수행&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Thief.h에서 선언했던 생성자를 실제로 구현하며 초기화를 진행. 부모 클래스(Player)의 생성자를 먼저 호출한 뒤, 내부에서 job = &quot;도적&quot;이나 power += 10과 같이 도적 클래스만의 고유 스탯을 설정하는 로직을 실행하도록 했음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;attack(Monster* monster) 내부의 상세한 전투 공식 작성&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;monster-&amp;gt;getDefence()를 통해 상대의 방어력을 가져와 대미지를 계산하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;for문을 사용하여 &quot;단검으로 여러 번 공격한다&quot;는 도적의 컨셉을 monster-&amp;gt;setHP()를 5회 반복 호출하는 방식을 구현했음. 궁수의 경우에는 같은 방식으로 3번 공격하도록 변경. power에서 defence를 먼저 빼고 그 뒤에 횟수로 나눠줘야 함. 그렇지 않으면 공격력이 5분의 1이 되어버려서 몬스터의 방어구를 뚫지 못하는 망한 캐릭터가 되어버리기 때문.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이후에는 공격 전후의 HP 변화를 계산하여 정보를 출력하도록 포인터를 활용해서 변경하였음.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;*오늘의 코드카타*&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 n을 입력받아 n의 약수를 모두 더한 값을 리턴하는 함수, solution을 완성해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n은 0 이상 3,000이하인 정수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778463047208&quot; class=&quot;autoit&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int solution(int n) {
    int answer = 0;
    
    for (int i = 1; i * i &amp;lt;= n; ++i) {
        if ( n % i == 0 ) {
            answer += i;
            
            if ( i * i != n ) {
                answer += ( n / i );
            }
        }
    }
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는, 모든 정수 n을 a * b 꼴로 나타낼 수 있다는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 a가 결정되면 자동적으로 b가 결정된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄청 큰 수가 아니라면 a를 찾기는 쉽다. 이 경우에는 i = 1부터 n의 제곱근까지만 스캔하면서 a를 찾고 자동으로 b가 찾아지게 한다. 다만, 예를 들어 16의 약수를 찾는 과정에서 16 = 4*4 같은 경우에 4가 중복되므로 이를 피하기 위해 두 숫자가 다를 때만 더하라는 조건을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n의 제곱근보다 큰 수를 스캔할 필요가 없는게, 이미 n제곱근보다 작거나 같은 수 범주에서 스캔하면서 짝꿍을 다 찾았기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 중복해서 찾을 필요가 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약 n % i == 0 이라면, 적절한 수로 잘 나누어서 약수를 찾아낸 것이므로 그 수 i를 더한다. 그리고, i^2 이 n보다 작거나 같은 수일 때까지 i를 증가시키면서 조건을 충족하는 i 값을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 같은 조건에서 약수의 개수를 세는 방법은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하다. 이미 작성한 코드에 if 조건문을 넣기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍적으로는 반복문을 이용해서 숫자를 세는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778463047208&quot; class=&quot;autoit&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int solution(int n) {
    int count = 0; //개수 세기
   
    for (int i = 1; i * i &amp;lt;= n; ++i) {
        if ( n % i == 0 ) { 
            if ( i * i == n ) { count += 1; }
            else { count += 2; }
        }
    } 
    return count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까와 같은 논리로 i * i = n 형태가 나오면 i가 중복되므로 개수를 셀 때 하나만 세도록 한다. n 제곱근보다 작거나 같은 경우는 자동적으로 짝꿍이 정해지므로 2개를 세도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수학적으로 생각해보면 소인수분해를 활용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 작은 소수인 2부터 시작해서 숫자를 나누고 특정 소수로 더 이상 나누어지지 않을 때까지 계속 나누면서 그 숫자로 몇번 나누었는지 누적하면서 센다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 (지수 + 1) 값 끼리 곱해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 12 = 2^2 * 3 이니까, (2+1) * (1+1) = 6개가 약수의 개수가 된다. 반복문을 활용하는 것과 비교해보자면, 12의 제곱근이 대략 3과 4 사이의 값이므로, i = 1, 2, 3 일 때 갯수를 셀 것이고 짝꿍도 덩달아서 정해지니까 6개의 값이 출력 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 소인수분해를 어떻게 코드로 구현할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n이 0보다 큰 정수라는 조건에서의 소인수분해 코드는 다음과 같다. 핵심 아이디어는 가장 작은 소수인 2부터 n의 제곱근까지 i로 나누기 시작하고, 나누어 떨어지는 i값을 찾으면 지수를 카운트하는 식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778463047209&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

long long count(int n) {
	if ( n == 1 ) return 1;
    
    long long totalCount = 1;
    int tempN = n;
    
    for (int i = 2; i * i &amp;lt;= tempN; ++i) {
    	if (tempN % i == 0) {
        	int exponent = 0;
            while (tempN % i == 0) {
            	exponent++;
                tempN /= i;
            }
            totalCount *= (exponent + 1);
	}
}

	if (tempN &amp;gt; 1) {
    	totalcount *= 2;
    }
    
    return totalCount;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롱롱은 엄청 큰 수까지 감안해서 써보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n값으로 직접 나누기보다 tempN을 선언해서 계속 나눌 수를 임시로 만들어 주는 게 좋다. 그리고 tempN을 i로 나눈 값을 다시 tempN으로 만들어서 while문으로 계속 나누면서 나머지 값이 0 이 될때마다 exponent에 숫자를 하나씩 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 소인수분해처럼 지수의 개수를 누적해서 셀 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 (지수 + 1) 값들을 곱한 값이 totalCount가 되어 약수의 개수를 셀 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은, 루프가 끝난 후에 tempN이 1보다 크다면 남은 수는 소수이기 때문에, 소수의 지수는 1이므로 1+1인 2를 추가로 곱해줘야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 텍스트게임 만들기</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/16</guid>
      <comments>https://moonwalk0515.tistory.com/16#entry16comment</comments>
      <pubDate>Thu, 7 May 2026 17:43:04 +0900</pubDate>
    </item>
    <item>
      <title>[C++학습] 7. STL</title>
      <link>https://moonwalk0515.tistory.com/15</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. STL (Standard Template Library)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 표준 라이브러리의 일부로, 컨테이너, 알고리즘, 반복자 등의 템플릿 기반 구성 요소를 포함한다. STL을 사용하면 다양한 자료구조와 알고리즘을 직접 구현하지 않고도 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;[1. 자주 사용되는 컨테이너]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너란 데이터를 담는 자료구조를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 컨테이너는 템플릿으로 구현되어 있어 다양한 타입의 데이터를 저장할 수 있고, 메모리 관리를 내부적으로 하고 있어 사용 시 메모리 해제를 직접 고려하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 컨테이너는 반복자를 제공하기 때문에 내부 구현을 몰라도 동일한 방식으로 컨테이너를 순회할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 벡터(vector)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터는 배열과 매우 유사한 컨테이너이다. 특징은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1. 템플릿 클래스로 구현되어 특정 타입에 종속되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2. 삽입되는 원소 개수에 따라 내부 배열의 크기가 자동으로 조정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-3. 인덱스를 통해 임의 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 벡터를 선언해야 한다. 타입만 명시해서 선언하거나, 초기값까지 같이 선언해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(초기값 선언하는 경우) : vector&amp;lt;int&amp;gt; vec(5,10); --&amp;gt; 크기는 5. 모든 원소가 10으로 초기화.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열처럼 벡터를 사용하려면, &lt;b&gt;벡터의 타입을 벡터로 하면 된다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, vector&amp;lt;int&amp;gt; vec( 3, vector&amp;lt;int&amp;gt; ( 4, 7 )); (선언만 한 경우) --&amp;gt; 가로줄이 3개이고, 세로줄이 4개. 모든 원소는 7 초기화.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 3 x 4 2차원 배열을 만든다. 가로줄은 행(row), 세로줄은(colomn). row번호를 먼저 만들고 그 오른쪽으로 채워나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음으로는 벡터에서 제공하는 메서드 중 자주 사용하게 될 메서드를 알아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1.&lt;b&gt; push_back&lt;/b&gt; : 벡터의 맨 끝에 원소를 &lt;b&gt;추가&lt;/b&gt;하는 메서드. 원소의 개수가 늘어남에 따라 크기는 자동으로 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex1) vec.push_back(10) : 맨 끝에 10이라는 원소를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex2) 2차원 벡터에 데이터를 추가하고 싶을 때는?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ex2-1) 2차원 벡터에 새로운 1차원 벡터(행)을 통째로 집어넣고 싶다면 : vector&amp;lt;T&amp;gt; 타입의 객체를 push_back&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vector&amp;lt;int&amp;gt; vec2 = {1, 2, 3};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vec2.push_back(vec1);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ex2-2) 이미 존재하는 특정 행에 개별 데이터를 넣고 싶다면 : 인덱스를 이용해서 해당 행을 찾고 그 행에서 push_back&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vec[1].push_back(10); vec 벡터의 인덱스 1 (두번째 행)의 마지막에 숫자 10을 추가.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2.&lt;b&gt; pop_back :&amp;nbsp;&lt;/b&gt;벡터의 맨 끝 원소를 &lt;b&gt;제거&lt;/b&gt;하는 메서드. 원소가 제거되면 벡터 크기가 자동으로 줄어든다. push_back 과 반대 개념으로 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 2차원 벡터에서 만약 2번째 (vec[1])에서 맨 마지막 원소만 pop_back한다고 하면 2번째 행의 크기만 감소하고 나머지 행의 크기는 그대로이다. 이때, vec.size()를 해보면 행의 개수는 유지하고 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점으로는 빈 행에서 pop_back을 하는 경우다. size가 0인 경우 pop_back을 하면 에러가 난다. 그래서 일반적으로는 다음과 같이 확인부터 하고 진행하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;if (&lt;b&gt;!vec[1].empty()&lt;/b&gt;) { vec[1].pop_back(); }&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-3. &lt;b&gt;size&lt;/b&gt; : 현재 벡터의 크기(원소 개수)를 확인할 때 사용하는 메서드. 보통 &lt;b&gt;벡터의 전체 원소를 대상으로 반복문을 돌릴 때&lt;/b&gt; 유용하게 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vec.size() 로 사용하면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-4.&amp;nbsp;&lt;b&gt;erase :&amp;nbsp;&lt;/b&gt;특정 위치(또는 구간)의 원소를 제거하는 함수이다. 자주 사용하지 않는 것이 좋다(시간 복잡도가 커짐).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 vec.begin()과 함께 많이 사용된다. &lt;b&gt;vec.begin()은 벡터의 첫 번째 원소를 가리키는 반복자를 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;b&gt;vec.end()는 벡터의 마지막 원소 다음을 가리키는 반복자를 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ex1) 예를 들어 벡터의 두 번째 요소를 제거하고 싶다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// vec.erase(vec.begin() + 1); --&amp;gt; index 1 제거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ex2) 벡터의 두 번째 부터 네 번째 요소(구간)를 제거하고 싶다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// vec.erase(vec.begin() + 1, vec.begin() + 3); --&amp;gt; index 1~3 제거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 사용한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 맵&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 키를 사용하여 값을 검색하는 기능&lt;/b&gt;을 제공하는 컨테이너이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵은 키를 활용해서 &lt;b&gt;키와 값을 쌍으로 저장하고 검색&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키-값 쌍은 &lt;b&gt;pair&amp;lt;const Key, Value&amp;gt;&lt;/b&gt; 형태로 저장되고, &lt;b&gt;키값을 기준으로 내부 데이터가 자동으로 정렬&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 키 값은 허용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵을 선언하기 위해서는 우선 &lt;b&gt;#include &amp;lt;map&amp;gt;&lt;/b&gt;하고, 키-값 쌍을 저장하기 위해 &lt;b&gt;키 타입과 값 타입 두 가지를 지정&lt;/b&gt;해야 한다. &lt;b&gt;키 타입은 비교 연산이 가능해야&lt;/b&gt; 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;lt;사용법&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째, map을 선언한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// map&amp;lt;string, string&amp;gt; contryMap; (map의 키값쌍, map별명)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째, 요소를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//countryMap[&quot;KR&quot;] = &quot;Korea&quot;; (키값은 KR(string이니까 &quot;&quot;), 값은 Korea(&quot;&quot;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세번째, 요소를 출력해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//for (const auto&amp;amp; pair : countryMap) { cout &amp;lt;&amp;lt; pair.first &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; pair.second &amp;lt;&amp;lt; endl; }&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//출력 결과 : KR, Korea&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵에서 사용하는 대표적인 함수를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1. map은 &lt;b&gt;key 순으로 오름차순 정렬(자동)&lt;/b&gt;된다. 그래서 key값이 비교 연산이 가능해야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 내림차순 정렬하게 만들고 싶으면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1.1) map &lt;b&gt;선언할 때 세번째 인자에 greater&amp;lt;int&amp;gt;를 추가&lt;/b&gt;한다. 선언 시점부터 내림차순으로 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;//map&amp;lt;int, string, greater&amp;lt;int&amp;gt;&amp;gt; myMap; (선언시 key값 자료형 int 기준으로 내림차순)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1.2) map은 그대로 오름차순으로 두고 &lt;b&gt;출력할 때만 거꾸로 훑어가면서 출력&lt;/b&gt;하기. &lt;b&gt;역방향 반복자를 사용&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//for (auto it = myMap.&lt;b&gt;rbegin()&lt;/b&gt;; it != myMap.&lt;b&gt;rend()&lt;/b&gt;; ++ it) {...};&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//(auto랑 같이 관습적으로 반복자(it라고 이름을 붙이고) 사용. 역방향이면&amp;nbsp;&lt;b&gt;rbegin, rend()&lt;/b&gt; 를 사용한다&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2. insert()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;make_pair()&lt;/b&gt;를 이용해서 pair 객체를 생성한 후 &lt;b&gt;insert 함수를 사용&lt;/b&gt;할 수 있다.&lt;b&gt; {} 또는 []를 사용&lt;/b&gt;해서 값을 추가할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2.1 make_pair() + insert() 사용해서 추가하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//map&amp;lt;int, string&amp;gt; myMap;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap.&lt;b&gt;insert(make_pair(1, &quot;Apple&quot;)&lt;/b&gt;);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2.2 {} or [] + insert() 사용해서 추가하기.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap.insert(&lt;b&gt;{2, &quot;Banana&quot;}&lt;/b&gt;);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap&lt;b&gt;[3]&lt;/b&gt; = &quot;Grape&quot;;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-3. find()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find 메서드를 사용하면 특정 키가 map에 존재하는 지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find는 &lt;b&gt;키가 존재하면 해당 키의 반복자(iterator)를 반환&lt;/b&gt;하고, &lt;b&gt;존재하지 않으면 map.end()를 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//int key = 2;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//auto it = myMap.&lt;b&gt;find(key)&lt;/b&gt;; (키 2를 찾기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//if (&lt;b&gt;it != myMap.end()&lt;/b&gt;) {...} (키가 존재하면, 이라는 조건식)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-4. size()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵에 키-값 &lt;b&gt;쌍의 개수를 반환&lt;/b&gt;하는 함수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap.size();&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-5. erase(key)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵의 &lt;b&gt;특정 key&lt;/b&gt;를 가진 요소만 삭제하는 함수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap.erase(2); (key값이 2인 요소를 삭제한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//존재하지 않는 키값을 삭제해도 문제는 없으나, 삭제되었는지 여부를 알기 위해서 다음과 같이 함께 사용해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//int removed = myMap.erase(2);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//if (removed == 0) { cout &amp;lt;&amp;lt;... &amp;lt;&amp;lt; endl; } (키값이 없어서 삭제가 안된 경우 문자열이 별도로 출력되게 함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-6. clear()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵에 있는 &lt;b&gt;모든 원소를 삭제&lt;/b&gt;하는 함수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//myMap.clear();&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;[2. 자주 사용되는 알고리즘]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STL은 다양한 컨테이너와 독립적으로 동작하는 범용 알고리즘을 제공한다. 이 범용 알고리즘을 적용하기 위해서는 반복자를 활용해야 한다. &lt;b&gt;반복자는 컨테이너의 요소를 추상화하여 일관된 방식으로 접근할 수 있도록&lt;/b&gt; 도와준다. 반복자를 함께 활용하는 대표적인 범용 알고리즘을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-1. sort()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#include &amp;lt;algorithm&amp;gt;&lt;/b&gt; 필요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부의 데이터를 정렬하는 함수. int/double과 같이 기본 타입의 경우, 사용자 정렬 함수가 없다면 오름차순 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//int arr[] : ~ , //int size = sizeof(arr) / sizeof(arr[0]);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//sort(arr, arr + size); (sort (시작 위치, 끝나는 위치). 즉 &lt;b&gt;arr은 배열의 첫번째 주소이고 arr + size 는 시작 주소에 크기를 더한 값. 다시말해 마지막 데이터 바로 다음의 메모리 주소를 가리킨다&lt;/b&gt;. 이렇게 하는 이유는 C++ 표준 알고리즘 함수 대부분은 시작점은 포함하고 끝점은 포함하지 않기 때문이다. 기본적으로 i &amp;lt; n 이 포함되어 있다고 생각하면 될 것 같다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 사용자 정렬 함수를 이용해서 정렬하고 싶으면 대표적으로 comp(a, b)를 구현하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;comp(a, b) : 첫 번째 인자 &lt;b&gt;a가 앞에 있는 원소를 의미&lt;/b&gt;하고, &lt;b&gt;true 면 a와 b의 순서가 유지되고 false면 순서를 바꾼다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오름차순 구현하는 방법은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//bool comp(int a, int b) { return a &amp;lt; b; } (오름차순)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//sort(arr, arr + size, comp); (세번째 인자로 comp 함수를 전달하여 정렬 규칙을 지정한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-2. find()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서 특정 원소를 찾아 해당 원소의 반복자를 반환하는 함수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;find(first last, 찾을&lt;/b&gt;&amp;nbsp;&lt;b&gt;값)&amp;nbsp;&lt;/b&gt;의 형식으로 사용한다. first last 가 탐색 대상이고, &lt;b&gt;원소를 찾은 경우 해당 원소의 반복자를 반환&lt;/b&gt;한다. &lt;b&gt;원소를 찾지 못한 경우 last 반복자를 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//auto it = find(arr, arr + size, 4); (특정 값 4를 찾음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;//값이 있다면 특정 값 4의 반복자를 반환. 값이 없다면 last 반복자 반환.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;[3. 반복자]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘은 반복자를 기반으로 동작하기 때문에 반복자가 무엇인지 알 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복자는 컨테이너의 요소에 대한 일관된 접근 방식을 제공하므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘이 특정 컨테이너의 내부 구현과 무관하게 동작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 순방향 반복자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;begin() : 컨테이너의 첫 번째 원소를 가리키는 반복자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;end() : 컨테이너의 &lt;b&gt;마지막 원소 다음&lt;/b&gt;을 가리키는 반복자. 대부분 find() 메서드 사용 시 찾지 못한 경우 end() 반복자를 반환.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 역방향 반복자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rbegin() : 컨테이너의 마지막 원소를 가리키는 역방향 반복자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rend() : 컨테이너의 첫 번째 원소의 &lt;b&gt;이전&lt;/b&gt;을 가리키는 역방향 반복자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;*오늘의 코드카타*&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;평균 구하기&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수를 담고 있는 배열 arr의 평균값을 return하는 함수, solution을 완성해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr은 길이 1 이상, 100 이하인 배열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr의 원소는 -10,000 이상 10,000 이하인 정수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 주어진 조건 안에서 해결&lt;/p&gt;
&lt;pre id=&quot;code_1778108826409&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

// arr_len은 배열 arr의 길이입니다.
double solution(int arr[], size_t arr_len) {
    double answer = 0;
    double sum = 0;
    
    for (size_t i = 0; i &amp;lt; arr_len; i++) {
        sum += arr[i];
    }
    
    answer = sum / arr_len;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sum을 선언 후 초기화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;size_t i =0 부터, arr_len보다 작으면 i를 증가시키고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sum에 arr[] i번째 인덱스에 있는 값을 더해라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) vector 사용&lt;/p&gt;
&lt;pre id=&quot;code_1778108381518&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;numeric&amp;gt;

using namespace std;

double solution(vector&amp;lt;int&amp;gt; arr) {
    double sum = accumulate(arr.begin(), arr.end(), 0.0);
    return sum / arr.size();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#include &amp;lt;vector&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;accumulate(); 를 사용하려면 #include &amp;lt;numeric&amp;gt;이 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vector와 accumulate(); 를 사용해서&amp;nbsp; arr.begin() 부터 arr.end()-1 까지 더할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0.0을 쓰면 자료형 double을 굳이 쓸 필요 없이 알아서 자료형을 인식한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/게임 개발을 위한 C++</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/15</guid>
      <comments>https://moonwalk0515.tistory.com/15#entry15comment</comments>
      <pubDate>Thu, 7 May 2026 11:28:54 +0900</pubDate>
    </item>
    <item>
      <title>[Text RPG 만들기] 2. Monster 클래스 + 1:1전투, 인벤토리 + 아이템 드롭, 포션 제작소 기본, 게임 완성 (step5~step8)</title>
      <link>https://moonwalk0515.tistory.com/14</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. main.cpp&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069891438&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Status.h&quot;
#include &quot;Player.h&quot;
#include &quot;Warrior.h&quot;
#include &quot;Magician.h&quot;
#include &quot;Thief.h&quot;
#include &quot;Archer.h.&quot;
#include &quot;Item.h&quot;
#include &quot;Monster.h&quot;	//step7
#include &quot;MonsterSpawn.h&quot;	//step7
#include &quot;Battle.h&quot;	//step7
#include &quot;AlchemyWorkshop.h&quot; //step7
#include &amp;lt;cstdlib&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;	//인벤토리 관리

using namespace std;

//step1 ~ step2
int main() {

	string name;
	const int SIZE = 4;
	int stat[SIZE] = { 0 };
	//stat[0] = hp, stat[1] = mp, stat[2] = attack, stat[3] = defense
	
	cout &amp;lt;&amp;lt; &quot;===========================================&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;          [ 던전 탈출 텍스트 RPG ]           &quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;===========================================&quot; &amp;lt;&amp;lt; endl;

	cout &amp;lt;&amp;lt; &quot;용사의 이름을 입력해주세요: &quot;;
	cin &amp;gt;&amp;gt; name;

	while (true) {
		cout &amp;lt;&amp;lt; &quot;\nHP와 MP를 입력해주세요: &quot;;
		cin &amp;gt;&amp;gt; stat[0] &amp;gt;&amp;gt; stat[1];

		if (stat[0] &amp;gt; 50 &amp;amp;&amp;amp; stat[1] &amp;gt; 50) {
			break;
		}
		else {
			cout &amp;lt;&amp;lt; &quot;HP나 MP의 값이 너무 작습니다. 다시 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
		}
	}

	while (true) {
		cout &amp;lt;&amp;lt; &quot;공격력과 방어력을 입력해주세요: &quot;;
		cin &amp;gt;&amp;gt; stat[2] &amp;gt;&amp;gt; stat[3];

		if (stat[2] &amp;gt; 50 &amp;amp;&amp;amp; stat[3] &amp;gt; 50) {
			break;
		}
		else {
			cout &amp;lt;&amp;lt; &quot;공격력이나 방어력이 너무 작습니다. 다시 입력해주세요.&quot; &amp;lt;&amp;lt; endl;
		}
	}

	printStatus(name, stat);	//Status.h, Status.cpp
	
	//step3.
	int hpPotion = 5;
	int mpPotion = 5;
	bool isGameStart = false;

	cout &amp;lt;&amp;lt; &quot;\n* HP 포션 5개, MP 포션 5개가 기본 지급되었습니다.&quot; &amp;lt;&amp;lt; endl;
	system(&quot;pause &amp;gt; nul&quot;);

	while (!isGameStart) {
		system(&quot;cls&quot;);
		
		cout &amp;lt;&amp;lt; &quot;\n============================================&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;&amp;lt; 캐릭터 강화 &amp;gt;&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1. HP UP    2. MP UP    3. 공격력 2배&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;4. 방어력 2배  5. 현재 능력치  0. 게임 시작&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;============================================&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;번호를 선택해주세요: &quot;;
		int choice;
		cin &amp;gt;&amp;gt; choice;

		switch (choice) {
		case 1:	//hp포션 사용
			if (hpPotion &amp;gt; 0) {
				stat[0] += 20;
				hpPotion--;
				cout &amp;lt;&amp;lt; &quot;* HP가 20 증가했습니다. (HP 포션 차감: 남은 포션 &quot; &amp;lt;&amp;lt; hpPotion &amp;lt;&amp;lt; &quot;개)&quot; &amp;lt;&amp;lt; endl;
				system(&quot;pause &amp;gt; nul&quot;);
			}
			else {
				cout &amp;lt;&amp;lt; &quot;포션 부족! HP 포션이 없습니다.&quot; &amp;lt;&amp;lt; endl;
				system(&quot;pause &amp;gt; nul&quot;);
			}
			break;
		
		case 2:	//mp포션 사용
			if (mpPotion &amp;gt; 0) {
				stat[1] += 20;
				mpPotion--;
				cout &amp;lt;&amp;lt; &quot;* MP가 20 증가했습니다. (MP 포션 차감: 남은 포션 &quot; &amp;lt;&amp;lt; mpPotion &amp;lt;&amp;lt; &quot;개)&quot; &amp;lt;&amp;lt; endl;
				system(&quot;pause &amp;gt; nul&quot;);
			}
			else {
				cout &amp;lt;&amp;lt; &quot;포션 부족!MP 포션이 없습니다.&quot; &amp;lt;&amp;lt; endl;
				system(&quot;pause &amp;gt; nul&quot;);
			}
			break;

		case 3:	//공격력 2배
			stat[2] *= 2;
			cout &amp;lt;&amp;lt; &quot;* 공격력이 2배가 되었습니다. (현재 공격력: &quot; &amp;lt;&amp;lt; stat[2] &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause &amp;gt; nul&quot;);
			break;

		case 4:	//방어력 2배
			stat[3] *= 3;
			cout &amp;lt;&amp;lt; &quot;* 방어력이 2배가 되었습니다. (현재 방어력: &quot; &amp;lt;&amp;lt; stat[3] &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause &amp;gt; nul&quot;);
			break;

		case 5:	//상태창 출력
			printStatus(name, stat);
			system(&quot;pause &amp;gt; nul&quot;);
			break;

		case 0:	//게임 시작 -&amp;gt; isGameStart = true
			cout &amp;lt;&amp;lt; &quot;게임을 시작합니다!&quot; &amp;lt;&amp;lt; endl;
			isGameStart = true;
			system(&quot;pause &amp;gt; nul&quot;);
			break;

		default:	//번호 입력 잘못했을 때 다시 while문 처음으로
			cout &amp;lt;&amp;lt; &quot;잘못된 번호입니다. 다시 선택해주세요.&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause &amp;gt; nul&quot;);
			break;
		}
	}

	//step4. 직업 선택
	Player* player = nullptr;	//Player* Player타입의 객체를 가리키는 포인터(player)
	//= nullptr; 로 일단 포인터를 비워둠. 나중에 직업을 가리키기 위함
	//new를 이용해서 각자 직업을 케이스별로 할당
	bool isjConfirmed = false;
	
	while (!isjConfirmed) {
		cout &amp;lt;&amp;lt; &quot;\n&amp;lt; 전직 시스템 &amp;gt;&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &quot;님, 직업을 선택해주세요!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1. 전사   2. 마법사   3. 도적   4. 궁수&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;선택: &quot;;
		int jChoice;
		cin &amp;gt;&amp;gt; jChoice;
		
		//혹시 모르니까 한번 더 메모리를 비워줌
		if (player != nullptr) {
			delete player;
			player = nullptr;
		}

		switch (jChoice) {

		case 1:	//전사
			player = new Warrior(name, stat[0], stat[1], stat[2], stat[3]);
			cout &amp;lt;&amp;lt; &quot;* &quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;로 전직하였습니다. (HP + 30)&quot; &amp;lt;&amp;lt; endl;
			player-&amp;gt;attack();
			break;

		case 2:	//마법사
			player = new Magician(name, stat[0], stat[1], stat[2], stat[3]);
			cout &amp;lt;&amp;lt; &quot;* &quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;로 전직하였습니다. (MP + 30)&quot; &amp;lt;&amp;lt; endl;
			player-&amp;gt;attack();
			break;

		case 3:	//도적
			player = new Thief(name, stat[0], stat[1], stat[2], stat[3]);
			cout &amp;lt;&amp;lt; &quot;* &quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;로 전직하였습니다. (Power + 10)&quot; &amp;lt;&amp;lt; endl;
			player-&amp;gt;attack();
			break;

		case 4:	//궁수
			player = new Archer(name, stat[0], stat[1], stat[2], stat[3]);
			cout &amp;lt;&amp;lt; &quot;* &quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;로 전직하였습니다. (Power + 5, Defence + 5)&quot; &amp;lt;&amp;lt; endl;
			player-&amp;gt;attack();
			break;

		default:
			cout &amp;lt;&amp;lt; &quot;다시 선택해주세요. 1~4 사이의 숫자를 입력해야 합니다.&quot; &amp;lt;&amp;lt; endl;
			break;
		}

		//직업 선택 이후 확정 or 다시 선택
		if (player != nullptr) {
			int QChoice;
			cout &amp;lt;&amp;lt; &quot;\n[&quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;] 을 선택하셨습니다. 이대로 확정하시겠습니까?&quot; &amp;lt;&amp;lt; endl;
			cout &amp;lt;&amp;lt; &quot;1. 확정    2. 다시 선택\n1 또는 2를 입력하세요: &quot;;
			cin &amp;gt;&amp;gt; QChoice;

			if (QChoice == 1) {	//확정
				isjConfirmed = true;
				cout &amp;lt;&amp;lt; &quot;전직이 완료되었습니다.&quot; &amp;lt;&amp;lt; endl;
				player-&amp;gt;printPlayerStatus();
				system(&quot;pause &amp;gt; nul&quot;);
			}
			else {				//다시 선택
				delete player;
				player = nullptr;
				cout &amp;lt;&amp;lt; &quot;\n직업을 다시 선택하겠습니다.&quot; &amp;lt;&amp;lt; endl;
			}
		}
	}
	
	//인벤토리 관련
	vector&amp;lt;Item&amp;gt; inventory;
	const int inventory_MAX = 10;

	//메인메뉴 관련 (While + switch case)
	bool isMenuRunning = true;
	int MenuChoice;
	AlchemyWorkshop Workshop;	//true면 이제 함수가 실행

	while (isMenuRunning) {
		system(&quot;cls&quot;);

		cout &amp;lt;&amp;lt; &quot;\n=== 메인 메뉴 ===&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;[현재 상태] HP: &quot; &amp;lt;&amp;lt; player-&amp;gt;getHP() &amp;lt;&amp;lt; &quot; | MP: &quot; &amp;lt;&amp;lt; player-&amp;gt;getMP() &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1. 던전 입장&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;2. 인벤토리 확인&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;3. 포션 제작소&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;0. 게임 종료&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;==================&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;선택: &quot;;
		cin &amp;gt;&amp;gt; MenuChoice;

		switch (MenuChoice) {

			//던전에 입장하면 랜덤 몬스터(MonsterSpawn)가 소환되고, 전투 루프가 시작 + 루프가 끝나면 인벤토리에 아이템 더하기
		case 1: {
			//MonsterSpawn 클래스의 정적함수인 RandomNormalMonster()를 호출
			  Monster currentMonster = MonsterSpawn::RandomNormalMonster();
			//Battle 클래스의 startBattle함수 실행. 전투가 끝난 후 droppedItem 변수에 아이템 정보 받음.
			  Item droppedItem = Battle::startBattle(player, currentMonster);

			  //인벤토리에 이미 같은 아이템이 있는지 확인
			  if (droppedItem.name != &quot;&quot;) {
				  bool isInventoryFound = false;
				  //같은 아이템이 있는 경우
				  //inventory는 vector를 사용하기에 auto&amp;amp; 으로 처음부터 끝까지 스캔.
				  for (auto&amp;amp; item : inventory) {	
					  if (item.name == droppedItem.name) {
						  item.count += droppedItem.count;	//아이템 카운트를 1 늘림.
						  isInventoryFound = true;
						  break;
					  }
				  }
				  //같은 아이템이 없는 경우
				  if (!isInventoryFound) {
					  if (inventory.size() &amp;lt; inventory_MAX) {
						  inventory.push_back(droppedItem);	//인벤토리에 새 칸을 추가하기 위해 push_back 사용.
					  }
					  else {
						  cout &amp;lt;&amp;lt; &quot;   -&amp;gt; 인벤토리가 가득 차서 [&quot; &amp;lt;&amp;lt; droppedItem.name &amp;lt;&amp;lt; &quot;]을(를) 버렸습니다.&quot; &amp;lt;&amp;lt; endl;
					  }
				  }
			  }
			  break;
		}

		//인벤토리 확인, 아이템 리스트
		case 2: {
			system(&quot;cls&quot;);
			cout &amp;lt;&amp;lt; &quot;[ 인벤토리 (&quot; &amp;lt;&amp;lt; inventory.size() &amp;lt;&amp;lt; &quot;/&quot; &amp;lt;&amp;lt; inventory_MAX &amp;lt;&amp;lt; &quot;) ]&quot; &amp;lt;&amp;lt; endl;

			if (inventory.empty()) {
				cout &amp;lt;&amp;lt; &quot;비어 있음&quot; &amp;lt;&amp;lt; endl;
			}

			//인벤토리의 아이템 출력하기. range-for --&amp;gt; for (요소데이터타입 변수이름 : 컨테이너이름) {}
			else {
				int i = 1;
				for (const auto&amp;amp; item : inventory) {
					cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &quot;. &quot;;	//번호 매기기
					item.PrintItemInfo();
					cout &amp;lt;&amp;lt; endl;
					i++;
				}
			}
			cout &amp;lt;&amp;lt; &quot;\n0. 돌아가기&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause &amp;gt; nul&quot;);
			break;
			
		}
		//포션 제작소.
		case 3:
			Workshop.RunWorkshopMenu(inventory);
			break;
		
		//게임 종료. 
		case 0: 
			isMenuRunning = false;
			cout &amp;lt;&amp;lt; &quot;게임을 종료합니다.&quot; &amp;lt;&amp;lt; endl;
			break;
		
		//잘못 입력했을 때 크래시 방지
		default: 
			cout &amp;lt;&amp;lt; &quot;잘못된 선택입니다.&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause &amp;gt; nul&quot;);
			break;
		}
	}

	//메모리 해제 후 게임종료
	if (player != nullptr) {
		delete player;
		player = nullptr;
	}

	return 0;	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Monster.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069905652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef MONSTER_H_
#define MONSTER_H_

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
#include &quot;Player.h&quot;

using namespace std;

class Monster {
private:
	string name;
	int hp, power, defence;

	string dropItemName;
	int dropItemPrice;



public:	//생성자
	Monster(string name, int hp, int power, int defence, string item, int price)
		: name(name), hp(hp), power(power), defence(defence), dropItemName(item), dropItemPrice(price) {
	}

	//getter, setter
	string getName() { return name; }
	int getHP() { return hp; }
	void setHP(int value) { hp = value; }

	int getPower() { return power;}
	int getDefence() { return defence; }

	string getDropItemName() { return dropItemName; }

	//몬스터의 공격을 정의하는 메서드(멤버함수)
	void attack(Player* player) {
		int damage = power - player-&amp;gt;getDefence();	//몬스터의 공격력 - 플레이어의 방어력

		if (damage &amp;lt;= 0) {	//데미지가 0보다 작을 때는 데미지 1.
			damage = 1;
		}
		
		player-&amp;gt;setHP(player-&amp;gt;getHP() - damage);	//데미지만큼 플레이어의 체력을 깎음
		
		cout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &quot;의 공격!&quot; &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot;에게 &quot; &amp;lt;&amp;lt; damage &amp;lt;&amp;lt; &quot; 데미지!&quot; &amp;lt;&amp;lt; endl;
	}

};

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3-1. MonsterSpawn.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069913452&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef MONSTERSPAWN_H_
#define MONSTERSPAWN_H_

#include &quot;Monster.h&quot;

class MonsterSpawn {
public:

	//일반 몬스터
	static Monster RandomNormalMonster();

	//보스 몬스터는 나중에 작성
};

#endif;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3-2. MonsterSpawn.cpp&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069921069&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;MonsterSpawn.h&quot;
#include &amp;lt;cstdlib&amp;gt; //rand 사용

//현재 등록된 몬스터
//0슬라임, 1허브자라버섯, 2베리스무디, 3고스트, 4에비안

//일반 몬스터 랜덤 소환
Monster MonsterSpawn::RandomNormalMonster() {
	int randNum = rand() % 5;	//일단 5마리.

	switch (randNum) {

		//randNum을 돌려서 Monster.h의 생성자에 값을 채운다.
	case 0:
		return Monster(&quot;슬라임&quot;, 30, 20, 10, &quot;슬라임의 끈적한 젤리&quot;, 30);
	case 1:
		return Monster(&quot;허브자라버섯&quot;, 50, 30, 15, &quot;허브&quot;, 20);
	case 2:
		return Monster(&quot;베리스무디&quot;, 40, 25, 20, &quot;베리&quot;, 20);
	case 3:
		return Monster(&quot;고스트&quot;, 200, 20, 10, &quot;마나 결정&quot;, 30);
	case 4:
		return Monster(&quot;에비안&quot;, 120, 10, 20, &quot;맑은물&quot;, 20);
	default:
		return Monster(&quot;미싱노&quot;, 1000, 1000, 1000, &quot;에러 아이템&quot;, 1000);
	}
}

//보스 몬스터 소환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4-1. Battle.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069928361&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef BATTLE_MANAGER_H_
#define BATTLE_MANAGER_H_

#include &quot;Player.h&quot;
#include &quot;Monster.h&quot;
#include &quot;Item.h&quot;

class Battle {

public:
	static Item startBattle(Player* player, Monster&amp;amp; monster);

};

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4-2. Battle.cpp&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069937337&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Battle.h&quot;
#include &quot;Item.h&quot;
#include &amp;lt;iostream&amp;gt;

using namespace std;

Item Battle::startBattle(Player* player, Monster&amp;amp; monster) {
	cout &amp;lt;&amp;lt; &quot;\n===========================================&quot; &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;[ 전투 시작! ] &quot; &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot;(&quot; &amp;lt;&amp;lt; player-&amp;gt;getJob() &amp;lt;&amp;lt; &quot;) vs &quot; &amp;lt;&amp;lt; monster.getName() &amp;lt;&amp;lt; endl;
	cout &amp;lt;&amp;lt; &quot;===========================================&quot; &amp;lt;&amp;lt; endl;

	while (player-&amp;gt;getHP() &amp;gt; 0 &amp;amp;&amp;amp; monster.getHP() &amp;gt; 0) {	//둘다 살아있을 때 전투루프

		cout &amp;lt;&amp;lt; &quot;\n--- 플레이어 턴 ---&quot; &amp;lt;&amp;lt; endl;				//플레이어 턴, 플레이어 공격
		player-&amp;gt;attack();
	
		int playerDamage = player-&amp;gt;getPower() - monster.getDefence();	//플레이어의 공격 데미지
		if (playerDamage &amp;lt;= 0) {
		playerDamage = 1;
		}

		int beforeMonsterHP = monster.getHP();						//몬스터의 현재 체력
		monster.setHP(beforeMonsterHP - playerDamage);				//몬스터 이전 체력 - 플레이어 공격 데미지

		cout &amp;lt;&amp;lt; monster.getName() &amp;lt;&amp;lt; &quot;에게 &quot; &amp;lt;&amp;lt; playerDamage &amp;lt;&amp;lt; &quot; 데미지!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; monster.getName() &amp;lt;&amp;lt; &quot; HP: &quot; &amp;lt;&amp;lt; beforeMonsterHP &amp;lt;&amp;lt; &quot; -&amp;gt; &quot; &amp;lt;&amp;lt; monster.getHP();
	
		//몬스터가 사망했을 경우 문자열을 추가로 출력 -&amp;gt; Win 방향으로
		if (monster.getHP() &amp;lt;= 0) {
			cout &amp;lt;&amp;lt; &quot; (사망)&quot; &amp;lt;&amp;lt; endl;
			break;	//몬스터가 사망했으므로 전투루프 종료
		}

		//몬스터의 공격
		cout &amp;lt;&amp;lt; &quot;\n--- 몬스터 턴 ---&quot; &amp;lt;&amp;lt; endl;
		monster.attack(player);
		cout &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot; HP: &quot; &amp;lt;&amp;lt; player-&amp;gt;getHP() &amp;lt;&amp;lt; endl;
	
		//플레이어가 패배했을 경우 문자열을 추가로 출력 -&amp;gt; Lose 방향으로
		if (player-&amp;gt;getHP() &amp;lt;= 0) {
			cout &amp;lt;&amp;lt; &quot;\n[ 패배 ] &quot; &amp;lt;&amp;lt; player-&amp;gt;getName() &amp;lt;&amp;lt; &quot;님은 패배했습니다.&quot; &amp;lt;&amp;lt; endl;
			break;
		}
	}

	//이제 전투 루프 탈출했으니까 승리 or 패배 텍스트 출력
	if (monster.getHP() &amp;lt;= 0) {
		cout &amp;lt;&amp;lt; &quot;\n★ 전투 승리!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; &quot; &amp;lt;&amp;lt; monster.getDropItemName() &amp;lt;&amp;lt; &quot; 획득!&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;  -&amp;gt; 인벤토리에 저장되었습니다.&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;\n계속하려면 아무 키나 누르세요.&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause &amp;gt; nul&quot;);

		return Item(monster.getDropItemName(), 50, 1);	//생성자를 활용해서 가독성을 높였음.
	}

	if (player-&amp;gt;getHP() &amp;lt; 0) {
		cout &amp;lt;&amp;lt; &quot;..전투에서 패배하였습니다..&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;..메인 메뉴로 돌아가세요..&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause &amp;gt; nul&quot;);
		return Item();	//함수를 종료하고 startBattle 시작 전으로 돌아감.
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5-1. Item.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069944219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef ITEM_H_
#define ITEM_H_

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

//struct. Item 구조체 작성. 나만의 데이터 타입(자료형) 생성. 여러 자료형을 하나로 묶는 형태.
struct Item {
	string name;
	int price;
	int count;

	//아이템 생성자
	Item(string name = &quot;&quot;, int price = 0, int count = 1)
		: name(name), price(price), count(count) {
	}
	
	//인벤토리에서 아이템 정보 출력하는 멤버 함수
	void PrintItemInfo() const {
		if (name == &quot;&quot;) {
			cout &amp;lt;&amp;lt; &quot;(비어 있음)&quot;;
		}
		else {
			cout &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &quot; x&quot; &amp;lt;&amp;lt; count &amp;lt;&amp;lt; &quot; (&quot; &amp;lt;&amp;lt; price &amp;lt;&amp;lt; &quot;G)&quot; &amp;lt;&amp;lt; endl;
		}
	}
};

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6-1. PotionRecipe.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069953409&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef POTIONRECIPE_H_
#define POTIONRECIPE_H_

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

using namespace std;

struct PotionRecipe {
	string potionName;
	string ingredient1;
	string ingredient2;

	PotionRecipe(string potionName, string ingredient1, string ingredient2)
		: potionName(potionName), ingredient1(ingredient1), ingredient2(ingredient2) {
	}

	void PrintRecipe() const {
		cout &amp;lt;&amp;lt; &quot;-&amp;gt; &quot; &amp;lt;&amp;lt; potionName &amp;lt;&amp;lt; &quot;: &quot; &amp;lt;&amp;lt; ingredient1 &amp;lt;&amp;lt; &quot; x1, &quot; &amp;lt;&amp;lt; ingredient2 &amp;lt;&amp;lt; &quot; x1&quot; &amp;lt;&amp;lt; endl;
	}
};

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6-2. AlchemyWorkshop.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069960950&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef ALCHEMYWORKSHOP_H_
#define ALCHEMYWORKSHOP_H_

#include &quot;PotionRecipe.h&quot;
#include &quot;Item.h&quot;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

class AlchemyWorkshop {

private:
	vector&amp;lt;PotionRecipe&amp;gt; recipes;	//레시피 저장소

public:
	AlchemyWorkshop();

	// 포션 제작소에서 사용할 메뉴 메서드.
	void RunWorkshopMenu(vector&amp;lt;Item&amp;gt;&amp;amp; inventory);
	//포션 제작 메서드. 참조자로 받아서 원본 인벤토리에서 재료가 빠져나가게 함.
	void CraftPotion(vector&amp;lt;Item&amp;gt;&amp;amp; inventory);

private:
	void ShowAllRecipes() const;
	void SearchByName(string potionName) const;
	void SearchByIngredient(string ingredient) const;

	//포션 제작 담당 helper함수 (재료가 있는지 체크하고, 제작하면 재료를 제거)
	bool HasIngredient(const vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName) const;
	void RemoveIngredient(vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName);
};



#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6-3. AlchemyWorkshop.cpp&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778069971776&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AlchemyWorkshop.h&quot;
#include &amp;lt;iostream&amp;gt;

AlchemyWorkshop::AlchemyWorkshop() {
	//데이터 삽입은 맨 마지막에 되도록 push_back. vector니까 가능하다.
	//PotionRecipe(string potionName, string ingredient1, string ingredient2) //PotionRecipe.h
	recipes.push_back(PotionRecipe(&quot;HP포션&quot;, &quot;허브&quot;, &quot;맑은물&quot;));
	recipes.push_back(PotionRecipe(&quot;MP포션&quot;, &quot;마나 결정&quot;, &quot;맑은물&quot;));
	recipes.push_back(PotionRecipe(&quot;스태미나포션&quot;, &quot;허브&quot;, &quot;베리&quot;));
	recipes.push_back(PotionRecipe(&quot;강화포션&quot;, &quot;슬라임의 끈적한 젤리&quot;, &quot;베리&quot;));
}

//포션 제작(레시피 검색, 재료 확인, 재료 소모, 인벤토리에 추가)
void AlchemyWorkshop::CraftPotion(vector&amp;lt;Item&amp;gt;&amp;amp; inventory) {
	string potionName;
	cout &amp;lt;&amp;lt; &quot;\n제작할 포션 이름을 입력하세요: &quot;;
	cin &amp;gt;&amp;gt; potionName;
	//potionName 입력받고 쭉 훑어봐서 같은게 있으면 그걸 출력. 같은게 없으면 별도의 문자열 출력
	
	//레시피 검색해서 같은 거 찾기.
	PotionRecipe* targetRecipe = nullptr; //포인터 targetRecipe를 생성하고 nullptr로 초기화
	for (auto&amp;amp; r : recipes) {
		if (r.potionName == potionName) {	//1부터 주소값에 있는 potionName을 찾아서 검색하고.
			targetRecipe = &amp;amp;r;	//값을 가져와서 가리키시오.
			break;
		}
	}
	// 포션 이름 검색 시 존재하지 않는 경우.
	if (targetRecipe == nullptr) {
		cout &amp;lt;&amp;lt; &quot;-&amp;gt; [&quot; &amp;lt;&amp;lt; potionName &amp;lt;&amp;lt; &quot;] 레시피가 존재하지 않습니다.&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause&quot;);
		return;
	}
	
	//재료 확인
	bool hasIngredient1 = AlchemyWorkshop::HasIngredient(inventory, targetRecipe-&amp;gt;ingredient1);
	bool hasIngredient2 = AlchemyWorkshop::HasIngredient(inventory, targetRecipe-&amp;gt;ingredient2);

	if (!hasIngredient1 || !hasIngredient2) {
		cout &amp;lt;&amp;lt; &quot;-&amp;gt; 재료가 부족합니다! (필요: &quot; &amp;lt;&amp;lt; targetRecipe-&amp;gt;ingredient1 &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; targetRecipe-&amp;gt;ingredient2 &amp;lt;&amp;lt; &quot;)&quot; &amp;lt;&amp;lt; endl;
		system(&quot;pause&quot;);
		return;
	}

	//재료 소모 즉, hasIngredient 1, 2가 모두 true값일때.
	//void RemoveIngredient(vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName);
	AlchemyWorkshop::RemoveIngredient(inventory, targetRecipe-&amp;gt;ingredient1);
	AlchemyWorkshop::RemoveIngredient(inventory, targetRecipe-&amp;gt;ingredient2);

	//만든 포션이 인벤토리에 있는 것과 중복되는 지 체크하고 중복된다면 item.count만 늘리면 되고,
	bool isinventoryFound = false;
	for (auto&amp;amp; item : inventory) {
		if (item.name == targetRecipe-&amp;gt;potionName) {
			item.count++;
			isinventoryFound = true;
			break;
		}
	}
	//중복되지 않으면 인벤토리에 1개 추가.
	//아이템 생성자. Item(string name = &quot;&quot;, int price = 0, int count = 1)
	if (!isinventoryFound) {
		inventory.push_back(Item(targetRecipe-&amp;gt;potionName, 100, 1));
	}
	
	cout &amp;lt;&amp;lt; &quot; [&quot; &amp;lt;&amp;lt; targetRecipe-&amp;gt;potionName &amp;lt;&amp;lt; &quot;] 제작을 완료했습니다! 인벤토리를 확인해보세요.&quot; &amp;lt;&amp;lt; endl;
	system(&quot;pause&quot;);
}

//인벤토리에 해당 재료가 1개 이상 있는지 확인하는 멤버함수
//bool HasIngredient(const vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName) const;
bool AlchemyWorkshop::HasIngredient(const vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName) const {
	for (const auto&amp;amp; item : inventory) {
		if (item.name == ingredientName &amp;amp;&amp;amp; item.count &amp;gt; 0) {
			return true;
		}
	}
	return false;
}

//인벤토리에서 재료 1개를 차감하는 멤버함수
//void RemoveIngredient(vector&amp;lt;Item&amp;gt;&amp;amp;inventory, string ingredientName);
void AlchemyWorkshop::RemoveIngredient(vector&amp;lt;Item&amp;gt;&amp;amp; inventory, string ingredientName) {
	//인벤토리를 처음부터 끝까지 훑어보고 이름이 같은거를 count 하나 빼기.
	for (auto item = inventory.begin(); item != inventory.end(); ++item) {
		if (item-&amp;gt;name == ingredientName) {
			item-&amp;gt;count--;
			if (item-&amp;gt;count == 0) {	//개수가 0이되면 인벤토리에서 지운다.
				inventory.erase(item);
			}
			return;
		}
	}
}

// 전체 레시피 검색(제일 쉬움)
//void ShowAllRecipes() const;
void AlchemyWorkshop::ShowAllRecipes() const {
	cout &amp;lt;&amp;lt; &quot;\n[ 전체 레시피 목록 ]: &quot; &amp;lt;&amp;lt; endl;
	for (const auto&amp;amp; r : recipes) {
		r.PrintRecipe();
	}
}

//포션 이름 일치하는 거 검색
//void SearchByName(string potionName) const;
void AlchemyWorkshop::SearchByName(string potionName) const {
	bool found = false;
	for (const auto&amp;amp; r : recipes) {
		if (r.potionName == potionName) {
			r.PrintRecipe();
			found = true;
			break;
		}
	}
	if (!found) {
		cout &amp;lt;&amp;lt; &quot;-&amp;gt; '&quot; &amp;lt;&amp;lt; potionName &amp;lt;&amp;lt; &quot;' 레시피를 찾을 수 없습니다.&quot; &amp;lt;&amp;lt; endl;
	}
}

//재료 이름 하나라도 일치하면 검색(제일 어려움)
//void SearchByIngredient(string ingredient) const;
void AlchemyWorkshop::SearchByIngredient(string ingredient) const {
	int count = 0;	//레시피 개수 출력하려고.
	for (const auto&amp;amp; r : recipes) {
		if (r.ingredient1 == ingredient || r.ingredient2 == ingredient) {
			r.PrintRecipe();
			count++;
		}
	}
	//재료 검색 실패시
	if (count == 0) {
		cout &amp;lt;&amp;lt; &quot;-&amp;gt; '&quot; &amp;lt;&amp;lt; ingredient &amp;lt;&amp;lt; &quot;'(이)가 포함된 레시피를 찾을 수 없습니다.&quot; &amp;lt;&amp;lt; endl;
	}
	//재료 검색 성공시 (함수 반환)
	else {
		cout &amp;lt;&amp;lt; &quot;총 &quot; &amp;lt;&amp;lt; count &amp;lt;&amp;lt; &quot;개의 레시피를 찾았습니다.&quot; &amp;lt;&amp;lt; endl;
	}

}

//포션 제작소 메뉴 만들기
void AlchemyWorkshop::RunWorkshopMenu(vector&amp;lt;Item&amp;gt;&amp;amp; inventory) {
	bool inWorkshop = true;
	int WorkshopChoice;

	while (inWorkshop) {
		system(&quot;cls&quot;);
		cout &amp;lt;&amp;lt; &quot;\n=== 포션 제작소 ===&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;1. 전체 레시피 보기\n2. 포션 이름으로 검색\n3. 재료로 검색\n4. 포션 제작\n0. 돌아가기&quot; &amp;lt;&amp;lt; endl;
		cout &amp;lt;&amp;lt; &quot;\n선택: &quot;;
		cin &amp;gt;&amp;gt; WorkshopChoice;

		if (WorkshopChoice == 1) {
			ShowAllRecipes();	
			system(&quot;pause&quot;);
		}
		else if (WorkshopChoice == 2) {
			string potionName;
			cout &amp;lt;&amp;lt; &quot;검색할 포션 이름: &quot;;
			cin &amp;gt;&amp;gt; potionName;
			SearchByName(potionName);
			system(&quot;pause&quot;);
		}
		else if (WorkshopChoice == 3) {
			string ingredientName;
			cout &amp;lt;&amp;lt; &quot;검색할 재료: &quot;;
			cin &amp;gt;&amp;gt; ingredientName;
			SearchByIngredient(ingredientName);
			system(&quot;pause&quot;);
		}
		else if (WorkshopChoice == 4) {
			CraftPotion(inventory);
		}
		else if (WorkshopChoice == 0) {
			inWorkshop = false;
		}
		else {
			cout &amp;lt;&amp;lt; &quot;잘못된 선택입니다.&quot; &amp;lt;&amp;lt; endl;
			system(&quot;pause&quot;);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 작성하느라 꽤 고생했다. 필수 과제를 처음에 시작하려고 했을 때 막막한 기분만 들었는데, 기본적인 C++을 활용해서 로직을 작성해보는 연습도 하고 실제로 실행도 해보니까 재미를 느끼고 있었다. 뭐든지 결과물을 잘 보는게 중요한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 나아가서는 오류도 잘 점검하고, 원하는 결과값이 잘 나오는지 꼼꼼히 체크하고, 코드 작성을 시작하기에 앞서 무엇을 먼저 작성할 것인지를 잘 판단하는 실력도 필요하다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>게임 프로그래밍 공부/C++ 텍스트게임 만들기</category>
      <author>samooyang</author>
      <guid isPermaLink="true">https://moonwalk0515.tistory.com/14</guid>
      <comments>https://moonwalk0515.tistory.com/14#entry14comment</comments>
      <pubDate>Wed, 6 May 2026 21:19:41 +0900</pubDate>
    </item>
  </channel>
</rss>