<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Wonder_Land.log</title>
        <link>https://velog.io/</link>
        <description>아무것도 모르는 컴공 학생의 Wonder_Land</description>
        <lastBuildDate>Fri, 03 Mar 2023 08:37:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Wonder_Land.log</title>
            <url>https://velog.velcdn.com/images/wonder_land/profile/3b5aaacd-1d85-4c09-a603-bf6ee2f335cd/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Wonder_Land.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wonder_land" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[알고리즘] 11. 이분탐색]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-11.-%EC%9D%B4%EB%B6%84%ED%83%90%EC%83%89</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-11.-%EC%9D%B4%EB%B6%84%ED%83%90%EC%83%89</guid>
            <pubDate>Fri, 03 Mar 2023 08:37:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/985">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x13강 - 이분탐색&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>이분탐색</li>
<li>Parametric Search</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-이분탐색">1. 이분탐색</h1>
<blockquote>
<ul>
<li><em><strong>이분탐색</strong></em>
: _<strong>정렬되어 있는 배열</strong>_에서 특정 데이터를 찾기 위해, 모든 데이터를 순차적으로 확인하는 대신, _<strong>탐색 범위를 절반</strong>_으로 줄여가며 찾는 탐색 방법. O(lg N).</li>
</ul>
</blockquote>
<h2 id="1-구현">1) 구현</h2>
<p>이분탐색을 구현할 때 주의할 점은</p>
<ol>
<li><p>주어진 배열은 _<strong>정렬</strong>_되어 있어야 한다.</p>
</li>
<li><p>무한 루프에 빠지지 않게 _<strong><code>mid</code>값을 결정</strong>_해야 한다.</p>
</li>
</ol>
<pre><code class="language-cpp">
int binarysearch(int target){
    int st = 0, en = n - 1;
    while(st &lt;= en){
        int mid = (st + en) / 2;
        if(a[mid] &lt; target) st = mid + 1;
        else if(a[mid] &gt; target) en = mid - 1;
        else return 1;
    }
    return 0;
}</code></pre>
<p>직접 구현 대신 <code>binary_search(a, a + n, t)</code>로 대체 가능.</p>
<pre><code class="language-cpp">int lower_idx(int target, int len){
    int st = 0, en = len;
    while(st &lt; en){
        int mid = (st + en) / 2;
        if(a[mid] &gt;= target) en = mid;
        else st = mid + 1;
    }
    return st;
}

int upper_idx(int target, int len){
    int st = 0, en = len;
    while(st &lt; en){
        int mid = (st + en) / 2;
        if(a[mid] &gt; target) en = mid;
        else st = mid + 1;
    }
    return st;
}</code></pre>
<p>직접 구현 대신 <code>lower_bound, upper_bound</code>로 대체 가능</p>
<hr>
<h1 id="2-parametric-search">2. Parametric Search</h1>
<blockquote>
<ul>
<li><em><strong>Parametric Search</strong></em>
: 조건을 만족하는 최소/최대값을 구하는 문제(<em><strong>최적화 문제</strong></em>)를 _<strong>결정 문제</strong>_로 변환해 이분탐색을 수행하는 방법</li>
</ul>
</blockquote>
<p>문제에서 _<strong>최소/최대 얘기</strong>_가 있고 _<strong>범위가 엄청 크다</strong>_거나 _<strong>시간복잡도에서 값 하나를 로그</strong>_로 떨굴 수 있을 때, 고려할 수 있는 방법.</p>
<ol>
<li><p>최적화 문제를 결정 문제로 바꿀 수 있는가?</p>
</li>
<li><p>결정 문제로 얻어낸 함수가 감소 혹은 증가함수인가?</p>
</li>
</ol>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/985">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x13강 - 이분탐색&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 10. 수학]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-10.-%EC%88%98%ED%95%99</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-10.-%EC%88%98%ED%95%99</guid>
            <pubDate>Sun, 26 Feb 2023 07:24:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/983">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x12강 - 수학&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>소수</li>
<li>최대공약수</li>
<li>연립합동방정식</li>
<li>이항계수</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-소수">1. 소수</h1>
<blockquote>
<ul>
<li><em><strong>소수</strong></em>
: <strong>1과 자기 자신</strong>으로만 나누어지는 수
: 약수가 2개인 수</li>
</ul>
</blockquote>
<ul>
<li><em><strong>합성수</strong></em>
: 1과 자기 자신을 제외한 다른 약수를 가지고 있는 수</li>
</ul>
<h2 id="1-소수-판정법">1) 소수 판정법</h2>
<h3 id="1-가장-기본적인-방법-on">(1) 가장 기본적인 방법. O(N)</h3>
<p>정의 그대로 1과 자기 자신으로만 나누어지는지,
2부터 N-1까지의 수 중에서 그 수를 나누는 다른 수가 없는지 확인!</p>
<p>다음 코드는, <code>n</code>을 <code>i</code>로 나누어보는 연산이 최대 <code>n-2</code>번이므로,
시간복잡도는 O(N).</p>
<pre><code class="language-cpp">bool isprime(int n){
    if(n == 1) return false;
    for(int i = 2; i &lt; n; i++)
        if(n % i == 0) return false;
    return true;
}</code></pre>
<h3 id="2-발전된-방법-o√n">(2) 발전된 방법. O(√N)</h3>
<p>합성수 N에서 1을 제외한 가장 작은 약수는 √N이하이다.</p>
<p>예를 들어, N = 18일 때,
가장 작은 약수는 1을 제외한 &#39;2&#39;이다.
√18 = 4.xxxx이고, 2는 √18이하이다.</p>
<p>Pf)
합성수 N에서 1을 제외한 가장 작은 약수를 x라고 하면,
N / x 또한 1이 아닌 N의 약수가 될 것이다.
따라서 x &lt;= (N / x)이다.
위의 식을 정리하면 x^2 &lt;= N이므로, x &lt;= √N이다.</p>
<p>따라서, 1번 방법에서는 2부터 N까지 나누었다면,
2번에서는 2부터 √N까지만 나눈다.</p>
<p>그렇다면 √N은 어떻게 구현을 하나.
<code>sqrt</code>함수는 실수의 연산/저장 과정에서 반드시 오차가 발생하므로 사용하면 안된다.</p>
<p>따라서 for문의 조건을, <code>i * i &lt;= n</code>으로 하면 된다.</p>
<pre><code class="language-cpp">bool isprime(int n){
    if(n == 1) return false;
    for(int i = 2; i * i &lt;= n; i++)
        if(n % i == 0) return false;
    return true;
}</code></pre>
<hr>
<h2 id="2-범위-내의-소수-판정법">2) 범위 내의 소수 판정법</h2>
<h3 id="1-가장-기본적인-방법">(1) 가장 기본적인 방법</h3>
<p>2부터 N까지 소수 판별법을 적용.</p>
<p>소수 판별법은 O(√N), 이걸 N개에 대해 적용하므로
시간 복잡도는 O(N√N).</p>
<h3 id="2-조금-발전된-방법">(2) 조금 발전된 방법</h3>
<p>1번 방법에서 소수 판별법을 진행할 때,
<code>2 ~ √N</code> 사이의 모든 수를 나눔.
이 때, 모든 수가 아닌, <code>2 ~ √N</code> 사이의 <strong>소수로만 나눔!!</strong></p>
<h3 id="3-에라토스테네스의-체-onlglgn">(3) 에라토스테네스의 체. O(NlglgN)</h3>
<p>2부터 시작해, 각 소수의 배수들을 지워나가 N까지 탐색하는 방식.
(= 합성수를 제외하는 방식)</p>
<p>예를 들어, 2가 소수로 판별되면 2의 배수인 4, 6, 8, ...을 모두 <code>false</code>로 만듦.
다음으로 3이 소수로 판별되면 3의 배수인 6, 9, 12, ...을 모두 <code>false</code>로 만듦.
이 때, 이미 <code>false</code>로 판별된 수는 소수가 아니므로 제외하고 다음 수를 탐색한다.</p>
<p>이 때, 조금 더 발전시키면,
2를 통해, <code>4, 6, 8, 10, ...</code>
3을 통해, <code>6, 9, 12, 15, ...</code>
4를 통해, <code>8, 12, 16, 20, ...</code>이 소수로 판별되었음.
5에서 탐색할 때, <code>10, 15, 20, 25, ...</code>에서 <code>25</code>의 앞 <code>10, 15, 20</code>은 이미 앞의 과정을 통해 소수로 판별되었음.
이는 이 수들이 5보다 작은 소인수를 가지고 있음을 의미함.
따라서, 5에서 탐색할 때, 5^2 = 25부터 false로 바꿔가면 됨.
(이는 5뿐만 아니라, 모든 수에서 적용가능함. 2, 3,  4, ...)</p>
<pre><code class="language-cpp">vector&lt;int&gt; sieve(int n){
    vector&lt;int&gt; primes;
    vector&lt;bool&gt; state(n + 1, true);
    state[1] = fasle;
    for(int i = 2; i * i &lt;= n; i++){
        if(!state[i]) continue;
        for(int j = i * i; ij &lt;= n; j += i)
            state[j] = false;
    }
    for(int i = 2; i &lt;= n; i++)
        if(state[i]) primes.push_back(i);
    return primes;
}</code></pre>
<p>참고로 <code>state</code>변수를 <code>vector&lt;bool</code> 자료형임.
왜 bool배열로 하지 않은 이유는, 배열로 잡으면 시간이 꽤 느려짐.
bool은 1byte를 차지하는데, 만약 <code>vector&lt;bool&gt;</code>로 두면 한 칸이 1byte가 아닌 1bit만 차지하도록 최적화됨.
덕분에 공간도 8배 적게 쓰고, cache hit rate가 올라가서 시간도 빨라짐.</p>
<p>이런 식의 최적화를 거치게 되면 시간복잡도는 O(NlglgN)인데, 거의 O(N)과 비슷함.</p>
<hr>
<h2 id="3-소인수분해factorization">3) 소인수분해(Factorization)</h2>
<blockquote>
<ul>
<li><em><strong>소인수분해(Factorization)</strong></em>
: 정수를 소수의 곱으로 나타내는 것</li>
</ul>
</blockquote>
<h3 id="1-에라토스테네스의-채를-이용">(1) 에라토스테네스의 채를 이용</h3>
<p>에라토스테네스의 채를 이용해,
N이하의 모든 소수를 N으로 나누어보는 방법</p>
<h3 id="2-발전된-방법">(2) 발전된 방법</h3>
<p>N을 나누는 수를, 2부터 시작해 1씩 증가시켜, 나누어질 수 없을 때까지 나누는 방법.</p>
<h4 id="21-방법의-정당성">(2.1) 방법의 정당성</h4>
<p>이 방법이 소인수 목록을 잘 구하는가?</p>
<ol>
<li><p>소인수 목록에 적힌 수들을 모두 곱했을 때 N이 되는가?</p>
</li>
<li><p>목록에 있는 수들이 전부 소수인가?</p>
</li>
</ol>
<p>우선 1번은 당연한 것.
이 방법은 N에서 각각의 수를 나누었기 때문.</p>
<p>2번은 만족하는가?
나누는 수를 2에서부터 1씩 증가시키지만, 따로 소수인지 판별하지 않았음.</p>
<p>하지만 결론적으로 말하면 _<strong>반드시 다 소수</strong>_임!!
만약 소인수목록에 소수가 아닌 수가 있다고 가정을 해보자.
그 수는 소수가 아닌 반드시 _<strong>합성수</strong>_가 될 것.
합성수는 소수인 약수를 반드시 가짐.
만약 15라는 소인수목록이 있다면, 15는 3과 5라는 소수인 약수를 가짐.
하지만 15까지 가기 이전에 이미 3인 순간에서, N은 더 이상 3으로 나누어질 수 없을 때까지 나누어짐.
따라서 소인수목록으로 3의 배수의 15는 나올 수 없음.</p>
<p>위의 귀류법과 같은 논리로, 만약 소인수목록으로 합성수가 있다면, 그 합성수에는 소인수 <code>p</code>가 있을 것이지만, 이미 그 합성수로 가기 전에 N에는 <code>p</code>가 남아있지 않은 것이므로 모순임.</p>
<h4 id="22-시간복잡도의-개선">(2.2) 시간복잡도의 개선</h4>
<p>일단, 시간복잡도는 N이 소수라면 중간에 나눠지는 과정 없이 i가 N까지 올라가야 하므로 O(N)의 시간복잡도를 가짐.</p>
<p>하지만 이를 O(√n)으로 만들 수 있음.
소수 판정 알고리즘과 비슷함.
N을 i로 나누는 이유는 소인수 i를 빼내기 위함.
그렇다면 i * i가 N보다 커지게 되면 더 이상 나누는 작업을 할 필요가 없음.
(이미 N에는 √n이하의 소인수가 없기 때문
 = N은 소인수임)</p>
<p> 예를 들어, <code>1100</code>을 소인수분해 한다고 했을 때,</p>
<table>
<thead>
<tr>
<th>N</th>
<th>i</th>
<th align="left">소인수목록</th>
</tr>
</thead>
<tbody><tr>
<td>1100</td>
<td>2</td>
<td align="left"></td>
</tr>
<tr>
<td>550</td>
<td>2</td>
<td align="left">2</td>
</tr>
<tr>
<td>275</td>
<td>2</td>
<td align="left">2, 2</td>
</tr>
<tr>
<td>275</td>
<td>3</td>
<td align="left">(변화 X) 2, 2</td>
</tr>
<tr>
<td>275</td>
<td>4</td>
<td align="left">(변화 X) 2, 2</td>
</tr>
<tr>
<td>275</td>
<td>5</td>
<td align="left">2, 2</td>
</tr>
<tr>
<td>55</td>
<td>5</td>
<td align="left">2, 2, 5</td>
</tr>
<tr>
<td>11</td>
<td>5</td>
<td align="left">2, 2, 5, 5</td>
</tr>
<tr>
<td>11</td>
<td>6 ~ 10</td>
<td align="left">(변화 X) 2, 2, 5, 5</td>
</tr>
<tr>
<td>1</td>
<td>11</td>
<td align="left">2, 2, 5, 5, 11</td>
</tr>
</tbody></table>
<p>위와 같은 과정을 거친다.</p>
<p>이 때, 밑에서 3번째 과정을 보게 되면,
i가 5이고, N은 11이다.
i * i = 25 &gt; N = 11을 만족하므로,
N은 √N이하의 소인수가 없으므로(N은 소인수이므로),
소인수목록에 N(=11)을 뒤에 추가하면 된다.
(3번째 과정의 뒤의 과정은 필요가 없게 된다.)</p>
<hr>
<p>정리하면,
_<strong>소수판정과 소수판별은 모두 O(√n)</strong>_임.
그리고 1부터 N까지 수 중에서 소수 목록을 구하는 것은 _<strong>에라토스테네스의 체</strong>_를 쓰면 됨.</p>
<hr>
<h1 id="2-최대공약수greatest-common-divisor-gcd">2. 최대공약수(Greatest Common Divisor, GCD)</h1>
<h2 id="1-약수">1) 약수</h2>
<blockquote>
<ul>
<li><em><strong>약수</strong></em>
: 어떤 수를 나누어떨어지게 하는 수
: 1부터 N까지 for문을 돌면서 다 나눠보면 구할 수 있음. O(N).</li>
</ul>
</blockquote>
<p>하지만 이 역시 마찬가지로 O(√n)에 가능.
18의 약수는 1, 2, 3, 6, 9, 18.
<code>1 * 18</code>, <code>2 * 9</code>, <code>3 * 6</code>로 표현 가능한데,
1, 2, 3만 구하면 나머지 약수는 자동으로 구해짐.
곱해서 N이 되는 두 수를 생각하면, 작은 수는 √n이하이므로, √n까지만 반복하면 됨.
(단 N이 제곱수일 때, 예를 들어 N이 16일 때는 √16 = 4는 약수에 1번만 포함시켜야 함.)</p>
<pre><code class="language-cpp">vector&lt;int&gt; divisor(int n){
    vector&lt;int&gt; div;
    for(int i = 1; i * i &lt;= n; i++)
        if(n % i == 0) div.push_back(i);
    for(int j = (int)div.size() - 1; j &gt;= 0; j--){
        if(div[j] * div[j] == n) continue;
        div.push_back(n / div[j]);
    }
    return div;
}</code></pre>
<p>두번째 for문에서 int로 형변환을 시켜줘야한다.
왜냐하면 <code>size()</code>함수는 unsigned를 반환하는데, 만약 size가 0이라면 <code>0 - 1</code>로 <code>-1</code>이 아닌 <code>(unsigned int의 최댓값 - 1)</code>을 값으로 가짐.
(하지만, 이 상황에서 1은 반드시 포함되므로 size - 1을 해도 최소 <code>0</code>이 되므로, overflow가 발생하지만, 이를 <code>int j</code>에 넣을 때 다시 형변환이 일어나 j에 <code>-1</code>이 정상적으로 담김. 하지만 이상적이지는 않은 과정임.)</p>
<hr>
<h2 id="2-최대공약수greatest-common-divisor-gcd-1">2) 최대공약수(Greatest Common Divisor, GCD)</h2>
<blockquote>
<ul>
<li><em><strong>약수</strong></em>
: 두 자연수의 공통된 약수 중 가장 큰 약수</li>
</ul>
</blockquote>
<h3 id="1-기본적인-방법">1) 기본적인 방법</h3>
<p>두 수의 약수 목록을 다 찾아서, 정렬을 하거나 또는 초기 상태에서 겹치는 약수를 찾고, 그 중 최댓값을 찾으면 됨.</p>
<h3 id="2-유클리드-호제법">2) 유클리드 호제법</h3>
<blockquote>
<ul>
<li><em><strong>유클리드 호제법</strong></em>
: 두 수 A, B에 대해 A를 B로 나눈 나머지를 r이라고 하면, _<strong>GCD(A, B) = GCD(B, r)</strong>_이다.</li>
</ul>
</blockquote>
<p>예를 들어, A를 20, B를 12로 하면,
A를 B로 나눈 나머지는 8(= 20 % 12).
GCD(20, 12) = GCD(12, 8) = GCD(8, 4) = GCD(4, 0)이다. 이 때, 0은 모든 수이 배수이므로, 최대공약수는 4가 된다.</p>
<pre><code class="language-cpp">int gcd(int a, int b){
    if(a == 0) return b;
    return gcd(b % a, a);
}</code></pre>
<p>위에서는 재귀를 사용했지만, whlie을 이용해서 구현할 수도 있고, 시간복잡도도 O(log(max(a,b)))라서 굉장히 빠름.
그리고 C++17에서는 gcd함수가 numeric헤더 파일 안에 내장되어 있음.</p>
<h3 id="3-최소공배수least-common-multiple-lcm">3) 최소공배수(Least Common Multiple, LCM)</h3>
<blockquote>
<ul>
<li><em><strong>최소공배수</strong></em>
: A * B = GCD(A, B) * LCM(A, B)</li>
</ul>
</blockquote>
<pre><code class="language-cpp">int lcm(int a, int b){
    return a / gcd(a, b) * b;
}</code></pre>
<p>위에서 overflow 방지를 위해 <code>a</code>를 먼저 <code>gcd</code>로 나누어줌.</p>
<hr>
<h1 id="3-연립합동방정식">3. 연립합동방정식</h1>
<p>-</p>
<hr>
<h1 id="4-이항계수">4. 이항계수</h1>
<h2 id="1-기본적인-방법-1">1) 기본적인 방법</h2>
<pre><code class="language-cpp">int n, k; cin &gt;&gt; n &gt;&gt; k;
int ret = 1;
for(int i = 1; i &lt;= n; i++) ret *= i;
for(int i = 1; i &lt;= k; i++) ret /= i;
for(int i = 1; i &lt;= n-k; i++) ret /= i;
cout &lt;&lt; ret;</code></pre>
<h2 id="2-발전된-방법-1">2) 발전된 방법</h2>
<p>nCk = n-1Ck + n-1Ck-1</p>
<p>만약 5개 중에서 3개를 뽑는다고 하면 5C3.</p>
<ol>
<li>내가 찜한 상자 1개 + 나머지 4개 중에서 2개 픽</li>
<li>내가 찜한 상자 0개 + 나머지 4개 중에서 3개 픽</li>
</ol>
<p>이는 dp로 해결 가능.
d[i][j] = iCj라 하면, d[i][j] = d[i-1][j-1] + d[i-1][j]로 표현 가능.</p>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
using namespace std;

int comb[1002][1002];
int mod = 10007;

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    for(int i = 1; i &lt;= 1000; i++){
        comb[i][0] = comb[i][i] = 1;
        for(int j = 1; j &lt; i; j++)
            comb[i][j] = (comb[i-1][j] + comb[i-1][j-1]) % mod;
    }
    int n, m; cin &gt;&gt; n &gt;&gt; m;
    cout &lt;&lt; comb[n][m];
}</code></pre>
<hr>
<h1 id="5-qa">5. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="6-마치며">6. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/983">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x12강 - 수학&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 9. 그리디 알고리즘]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-9.-%EA%B7%B8%EB%A6%AC%EB%94%94</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-9.-%EA%B7%B8%EB%A6%AC%EB%94%94</guid>
            <pubDate>Sat, 25 Feb 2023 08:05:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/975">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x11강 - 그리디&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222609762108&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 10. 그리디 - (1)&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>그리디 알고리즘</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-그리디greedy-알고리즘">1. 그리디(Greedy) 알고리즘</h1>
<blockquote>
<ul>
<li><em><strong>그리디(Greedy) 알고리즘</strong></em>
: 지금 가장 최적인 답을 근시안적으로 택하는 알고리즘
: 관찰을 통해 탐색 범위를 줄이는 알고리즘</li>
</ul>
</blockquote>
<h2 id="1-그리디-알고리즘을-푸는-과정">1) 그리디 알고리즘을 푸는 과정</h2>
<h3 id="1-관찰을-통해-탐색-범위를-줄이는-방법을-고안">(1) 관찰을 통해 탐색 범위를 줄이는 방법을 고안</h3>
<p>직관적으로 보이는 풀이의 시간복잡도로, 문제에서 제시한 시간 제한 안에 들어올 수 없을 때,
시간복잡도를 더 낮출 수 있는 방법을 관찰함.</p>
<h3 id="2-탐색-범위를-줄여도-올바른-결과를-낼-수-있음을-수학적으로-증명">(2) 탐색 범위를 줄여도 올바른 결과를 낼 수 있음을 수학적으로 증명</h3>
<h3 id="3-구현-후-해결">(3) 구현 후 해결!</h3>
<hr>
<p>하지만, 실제 코테에서는 시간이 촉박하므로, 수학적으로 꼼꼼히 증명하기가 힘듦.
따라서, 방법을 고안하고 대충 주어진 예제에서 잘 돌아가는지 확인 후 구현함.</p>
<p>이런 과정에서 절망적인 풀이 흐름은,
1번에서 잘못된 방법을 고안하고, 2번에서 잘못되었음을 증명하지 못하고, 3번에서 계속 틀리는 경우임.
(잘못된 방법이라서 틀린건지, 구현이 잘못되었는지 알 수 없음.)</p>
<hr>
<h2 id="2-코테-추천-전략">2) 코테 추천 전략</h2>
<p>따라서 코테 추천 전략은!</p>
<h3 id="1-거의-똑같은-문제를-풀었거나-정말-간단한-문제여서-풀이를-100-확신할-때">(1) 거의 똑같은 문제를 풀었거나, 정말 간단한 문제여서, 풀이를 100% 확신할 때</h3>
<p>답을 제출해 보고, 틀리면 빨리 손절!!!</p>
<h3 id="2-100-확신은-없지만-맞는-것-같은-그리디-풀이를-찾았을-때">(2) 100% 확신은 없지만, 맞는 것 같은 그리디 풀이를 찾았을 때</h3>
<p>일단은 넘어가고, 다른 문제를 풀게 없거나 종료가 20 ~ 40분 정도 남은 시점에 코딩 시작!!!</p>
<hr>
<h1 id="2-qa">2. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="3-마치며">3. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/975">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x11강 - 그리디&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222609762108&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 10. 그리디 - (1)&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 8. 다이나믹 프로그래밍]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-8.-%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-8.-%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Fri, 03 Feb 2023 07:44:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/974">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x10강 - 다이나믹 프로그래밍&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222603789811&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 06. 다이나믹 프로그래밍 - 기초&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>다이나믹 프로그래밍</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-다이나믹-프로그래밍dynamic-programming-dp">1. 다이나믹 프로그래밍(Dynamic Programming, DP)</h1>
<blockquote>
<ul>
<li><em><strong>다이나믹 프로그래밍(Dynamic Programming, DP)</strong></em>
: 여러 개의 하위 문제를 먼저 푼 후, 그 결과를 쌓아올려 주어진 문제를 해결하는 알고리즘</li>
</ul>
</blockquote>
<p>예를 들어, 피보나치 수열의 경우</p>
<ol>
<li>재귀적으로 해결</li>
<li>DP로 해결</li>
</ol>
<p>하는 방법이 있음.</p>
<pre><code class="language-cpp">// 1. 재귀적으로 해결
if(n &lt;= 1) return 1;
return fibo(n-1) + fibo(n-2);

// 2. DP로 해결
f[0] = f[1] = 1;
for(int i = 2; i &lt;= n; i++)
    f[i] = f[i-1] + f[i-2];
return f[n];</code></pre>
<p>1번 방법은  O(1.618^N)의 시간복잡도를 가짐
반면 2번 방법은 O(N)의 시간복잡도를 가지므로 꽤나 극단적인 차이가 발생.</p>
<h2 id="1-dp를-푸는-과정">1) DP를 푸는 과정</h2>
<p>DP 문제는 어렵게 하고자 하면 끝도 없이 어려워지지만, 코테 수준에서는 다음 3가지 과정만 지키면 크게 어렵지 않음.</p>
<h3 id="1-테이블-정의하기">(1) 테이블 정의하기</h3>
<p>특별한 방법이 있는 것이 아닌, 경험적으로 익힘.</p>
<h3 id="2-점화식-찾기">(2) 점화식 찾기</h3>
<h3 id="3-초기값-정하기">(3) 초기값 정하기</h3>
<hr>
<h1 id="2-qa">2. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="3-마치며">3. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/974">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x10강 - 다이나믹 프로그래밍&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222603789811&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 06. 다이나믹 프로그래밍 - 기초&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 7. 정렬]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-7.-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-7.-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Tue, 31 Jan 2023 04:59:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/955">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0E강 - 정렬 I&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222547222163&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 03. STL 알고리즘 _ sort&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>기본 정렬</li>
<li>병합 정렬(Merge Sort)</li>
<li>퀵 정렬(Quick Sort)</li>
<li>Counting Sort</li>
<li>Radix Sort</li>
<li>STL Sort</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-기본-정렬">1. 기본 정렬</h1>
<h2 id="1-선택-정렬on²">1) 선택 정렬(O(N²))</h2>
<p>아이디어 / 구현이 가장 기초적인 정렬</p>
<p>원소 중 최댓값을 제일 앞 / 뒤로 순차적으로 이동시키는 정렬</p>
<pre><code class="language-cpp">int arr[5] = {3, 2, 7, 116, 62};
int n = 5;
for(int i = n - 1; i &gt; 0; i--){
    int mxidx = 0;
    for(int j = 1; j &lt;= i; j++){
        if(arr[mxidx] &lt; arr[j]) mxidx = j;
    }
    swap(arr[mxidx], arr[i]);
}</code></pre>
<pre><code class="language-cpp">int arr[5] = {3, 2, 7, 116, 62};
int n = 5;
for(int i = n - 1; i &gt; 0; i--){
    swap(*max_element(arr, arr + i + 1), arr[i]);
}</code></pre>
<h2 id="2-버블-정렬on²">2) 버블 정렬(O(N²))</h2>
<p>구현이 가장 편한 정렬</p>
<p>앞에서부터 인접한 두 원소를 보면서, 앞의 원소가 더 클 때 자리를 바꾸는 정렬</p>
<pre><code class="language-cpp">int arr[5] = {-2, 2, 4, 6, 13};
int n = 5;
for(int i = 0; i &lt; n; i++){
    for(int j = 0; j &lt; n - 1 - i; j++){
        if(arr[j] &gt; arr[j+1]) swap(arr[j], arr[j+1]);
    }
}</code></pre>
<h2 id="3-삽입-정렬on²">3) 삽입 정렬(O(N²))</h2>
<hr>
<h1 id="2-병합-정렬merge-sort-onlgn">2. 병합 정렬(Merge Sort) (O(NlgN))</h1>
<p>(N이 10만정도일 때, O(N²)은 10초 ~ 1분, O(NlgN)은 겁나 빠름. N이 커지면 커질수록 격차는 더 커짐)</p>
<blockquote>
<ul>
<li><em><strong>병합 정렬(Merge Sort)</strong></em>
: 재귀적으로 수열을 나눠 정렬한 후 합치는 정렬</li>
</ul>
</blockquote>
<pre><code class="language-cpp">int idx1 = 0, idx2 = 0;
for(int i = 0; i &lt; n + m; i++){
    if(idx1 &gt;= n){c[i] = b[idx2++]; continue;}
    if(idx2 &gt;= m){c[i] = a[idx1++]; continue;}

    if(a[idx1] &gt; b[idx2]) c[i] = b[idx2++];
    else c[i] = a[idx1++];
}</code></pre>
<p>병합 정렬의 단계는 3단계로 나눌 수 있음.</p>
<ol>
<li>주어진 리스트를 2개로 나눈다. O(N)</li>
<li>나눈 리스트 2개를 정렬한다.</li>
<li>정렬된 2개의 리스트를 합친다. O(Nk) = O(NlgN)</li>
</ol>
<h2 id="1-stable-sort">1) Stable Sort</h2>
<p>만약 다음을 정렬한다고 해보자</p>
<p><code>20, 10(빨), 10(파), 10(초)</code></p>
<ol>
<li><code>10(빨) 10(파) 10(초) 20</code></li>
<li><code>10(파) 10(빨) 10(초) 20</code></li>
<li><code>10(초) 10(빨) 10(파) 20</code></li>
</ol>
<p>위의 예시에서, 우선 순위가 같은 원소가 여러 개가 있음.
이 때, <em><strong>우선 순위가 같은 원소들은 원래의 순서를 따라가게 하는 성질이 Stable Sort</strong></em>.</p>
<p>즉, <code>10(빨) 10(파) 10(초) 20</code>이면, Stable Sort 성질을 가짐. 나머지 정렬은 Unstable Sort.</p>
<p>따라서, Merge Sort에서 Stable Sort를 만족시키려면, 
앞의 리스트와 뒤의 리스트의 우선 순위가 같을 때, <em><strong>앞의 리스트의 원소를 먼저 보내줘야 함.</strong></em></p>
<hr>
<h1 id="3-퀵-정렬quick-sort-onlgn--on²">3) 퀵 정렬(Quick Sort) (O(NlgN) ~ O(N²))</h1>
<p>이름에서 알 수 있듯이, 거의 대부분의 정렬 알고리즘보다 빨라서 각종 라이브러리에서는 퀵 정렬을 바탕으로 만들어짐</p>
<p>하지만, 코테에서 STL을 못 쓴다면, 퀵 정렬이 아닌 <em><strong>반드시 Merge Sort를 사용해야 함.</strong></em>
(만약, 힙을 알고 있다면 Heap Sort, 아니면 다른 O(NlgN) 정렬 사용)</p>
<blockquote>
<ul>
<li><em><strong>퀵 정렬(Quick Sort)</strong></em>
: pivot을 기준으로 왼쪽에는 pivot보다 작은 수, 오른쪽에는 pivot보다 큰 수가 오게끔 재귀적으로 정렬</li>
</ul>
</blockquote>
<pre><code class="language-cpp">int arr[8] = {6, -8, 1, 12, 8, 3, 7, -7};
int tmp[8];
int tidx = 0;
int pivot = arr[0];
for(int i = 1; i &lt; 8; i++)
    if(arr[i] &lt;= pivot) tmp[tidx++] = arr[i];
tmp[tidx++] = pivot;
for(int i = 1; i &lt; 8; i++)
    if(arr[i] &gt; pivot) tmp[tidx++] = arr[i];
for(int i = 0; i &lt; 8; i++) arr[i] = tmp[i];</code></pre>
<p>단, 위의 방식대로 코드를 작성하면 <code>tmp</code>라는 추가적인 공간이 필요함.
퀵 소트는 추가적인 공간을 사용하지 않고, pivot을 제자리에 보낼 방법을 고안해야함.
(이렇게 추가적인 공간을 사용하지 않는 정렬을 &#39;In-Place Sort&#39;라고 함)</p>
<pre><code class="language-cpp">int arr[8] = {6, -8, 1, 12, 8, 3, 7, -7};
int pivot = arr[0];
int l = 1, r = 7;
while(1){
    while(l &lt;= r &amp;&amp; arr[l] &lt;= pivot) l++;
    while(l &lt;= r &amp;&amp; arr[r] &gt; pivot) r--;
    if(l &gt; r) break;
    swap(arr[l], arr[r]);
}
swap(arr[0], arr[r]);</code></pre>
<hr>
<h1 id="4-counting-sort">4. Counting Sort</h1>
<blockquote>
<ul>
<li><em><strong>Counting Sort</strong></em>
: 수열에서 등장한, 원소의 개수를 세어, 원소의 개수만큼 나열한 정렬</li>
</ul>
</blockquote>
<p><code>1 5 4 2 3 1 4 3</code>
<code>1 : 2번</code>
<code>2 : 1번</code>
<code>3 : 2번</code>
<code>4 : 2번</code>
<code>5 : 1번</code>
-&gt; <code>1 1 2 3 3 4 4 5</code></p>
<p>정말 쉬운 정렬이지만, 명확한 단점이 있음.</p>
<p>만약, 원소가 <code>999,999,999</code>라면 크기가 10억인 배열이 필요함.</p>
<p>이론적으로는 수의 범위가 0 ~ 1억까지라도 사용가능하지만, 실제적으로는 1000만 이하일 때에는 COunting Sort를 사용함.</p>
<hr>
<h1 id="5-radix-sort">5. Radix Sort</h1>
<blockquote>
<ul>
<li><em><strong>Radix Sort</strong></em>
수열에서 등장한, 원소의 자리수를 세어, 원소의 자리수를 기준으로 정렬</li>
</ul>
</blockquote>
<p><code>012 421 046 674 103 502 007 100 021 545</code></p>
<p>각 수의 1의 자리를 기준으로 0 ~ 9의 리스트에 구분하여 넣습니다.</p>
<p><code>0 : 100</code>
<code>1 : 421 021</code>
<code>2 : 012 502</code>
<code>3 : 103</code>
<code>4 : 674</code>
<code>5 : 545</code>
<code>6 : 046</code>
<code>7 : 007</code>
<code>8 :</code>
<code>9 :</code>
-&gt; <code>100 421 021 012 502 103 674 545 046 007</code></p>
<p>이후에는 0번 리스트부터 수를 보면서, 수열을 재배치함.</p>
<p><code>0 : 100</code>
<code>1 : 421 021</code>
<code>2 : 502 012</code>
<code>3 : 103</code>
<code>4 : 674</code>
<code>5 : 545</code>
<code>6 : 046</code>
<code>7 : 007</code>
<code>8 :</code>
<code>9 :</code>
-&gt; <code>100 421 021 502 012 103 674 545 046 007</code></p>
<p>이후에는, 10의 자리를 기준으로 넣고 재배치.</p>
<p><code>0 : 100 502 103 007</code>
<code>1 : 012</code>
<code>2 : 421 021</code>
<code>3 :</code>
<code>4 : 545 046</code>
<code>5 :</code>
<code>6 :</code>
<code>7 : 674</code>
<code>8 :</code>
<code>9 :</code>
-&gt; <code>100 502 103 007 012 421 021 545 046 674</code></p>
<p>마지막으로 100의 자리도 마찬가지.</p>
<p><code>0 : 007 012 021 046</code>
<code>1 : 100 103</code>
<code>2 :</code>
<code>3 :</code>
<code>4 : 421</code>
<code>5 : 502 545</code>
<code>6 : 674</code>
<code>7 :</code>
<code>8 :</code>
<code>9 :</code>
-&gt; <code>007 012 021 046 100 103 421 502 545 674</code></p>
<h2 id="1-comparison-vs-non-comparision">1) Comparison Vs Non-Comparision</h2>
<h3 id="1-comparision-sort">(1) Comparision Sort</h3>
<p>Merge, Quick, Bubble Sort같이 원소들끼리 크기를 비교하는 정렬</p>
<h3 id="2-non-comparision-sort">(2) Non-Comparision Sort</h3>
<p>Counting, Radix Sort같이 원소들끼리 크기를 비교하지 않는 정렬</p>
<hr>
<h1 id="6-stl-sort">6. STL sort</h1>
<p>STL sort는 기본적으로 퀵 소트를 기반으로 하지만, 리스트가 불균등하게 나눠져 재귀의 깊이가 너무 깊어지면 O(NlgN)이 보장되는 정렬 알고리즘으로 나머지 처리.</p>
<p>STL sort는 Stable Sort가 아니기 때문에, Stable Sort가 필요하다면 <code>stable_sort</code>함수를 사용!(사용법 동일)</p>
<p>pair, tuple은 제일 앞의 원소의 대소를 비교하고, 만약 같다면 다음 원소의 대소를 비교함.
<code>pair1 : {2, 5}, pair2{3, -2}</code>
-&gt; <code>pair1 &lt; pair2</code>
<code>tuple1 : {2, 1, 0}, tuple2{2, -2, 6}</code>
-&gt; <code>tuple1 &gt; tuple2</code>
따라서, 좌표를 정렬하거나 기타 속성이 있는 원소를 정렬할 때, 구조체보다는 pair나 tuple이 용이함.</p>
<h2 id="1-사용법">1) 사용법</h2>
<p><code>1. sort(시작, 끝)</code>
<code>2. sort(시작, 끝, 비교 함수)</code></p>
<p>2번에서 보듯이, 비교 함수를 사용자가 지정할 수 있음.</p>
<p>만약 배열을 정렬하고자 한다면 다음과 같이 하고</p>
<pre><code class="language-cpp">int a[5]  = {1, 4, 5, 2, 7};
sort(a, a+5);</code></pre>
<p>주어진 배열에서,
5로 나눈 나머지 순으로, 나머지가 같다면 크기 순으로 정렬하고자 한다면</p>
<pre><code class="language-cpp">bool Cmp(int a, int b){
    if(a % 5 != b % 5) return a % 5 &lt; b % 5;
    return a &lt; b;
}

int a[5]  = {1, 4, 5, 2, 7};
sort(a, a+5);</code></pre>
<h2 id="2-비교-함수의-주의-사항">2) 비교 함수의 주의 사항</h2>
<h3 id="1-두-값이-같거나-우선-순위가-같을-때-false를-반환">(1) 두 값이 같거나 우선 순위가 같을 때, false를 반환</h3>
<p>이 때, 비교함수에서는, a가 b의 앞에 와야할 때만 true를 반환해야하니,
_<strong>두 값이 같거나 우선 순위가 같다면 반드시 false를 반환</strong>_해야 함.</p>
<p>-&gt; 런타임에러를 발생시키기도 함.</p>
<h3 id="2-인자로-stl-혹은-클래스-객체-전달시-reference를-사용">(2) 인자로, STL 혹은 클래스 객체 전달시 reference를 사용</h3>
<p>만약 reference로 전달하지 않으면, 값의 복사가 일어나게 되는데 불필요함.
그냥 reference로 전달하면 복사 과정이 일어나지 않음.</p>
<p>따라서
<code>bool cmp(string a, string b)</code>보다는
<code>bool cmp(const string&amp; a, const string&amp; b)</code>가 바람직함.</p>
<p>-&gt; 수행 시간에서 약간의 차이는 있을 수 있겠지만, 에러는 발생하지 않음</p>
<p>따라서, <em><strong>두 값이 같을 때는 반드시 false를 반환하자!!</strong></em></p>
<hr>
<h1 id="7-qa">7. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="8-마치며">8. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/955">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0E강 - 정렬 I&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222547222163&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 03. STL 알고리즘 _ sort&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 6. 시뮬레이션]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-6.-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-6.-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%EC%85%98</guid>
            <pubDate>Tue, 10 Jan 2023 09:09:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/948">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0D강 - 시뮬레이션&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222605154765&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 08. 구현 + 시뮬레이션&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>시뮬레이션</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-시뮬레이션">1. 시뮬레이션</h1>
<p>이 유형은, BFS나 재귀와 같은 특정 자료구조, 알고리즘에 종속되어 있지 않고,
<em><strong>주어진 문제 상황을 구현하면 되지만, 이 때 구현이 빡세게 필요한 유형의 문제.</strong></em></p>
<p>따라서, 배경 지식보다는, 구현력이 많이 필요함.</p>
<h2 id="1-연습문제-1--boj15683감시">1) 연습문제 1 : BOJ15683(감시)</h2>
<h3 id="1-내-풀이">(1) 내 풀이</h3>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

int n, m;
int board[10][10]; // 원본 배열
vector&lt;pair&lt;int,int&gt;&gt; V; // cctv 장소를 담는 큐
int mn = 100;

void Up(int cur_x, int cur_y){ // ↑
    for(int i = cur_x - 1; i &gt;= 0; i--){
        if(board[i][cur_y] == 6) return;
        if(board[i][cur_y] == 0) board[i][cur_y] = 7;
    }
}
void Down(int cur_x, int cur_y){ // ↓
    for(int i = cur_x + 1; i &lt; n; i++){
        if(board[i][cur_y] == 6) return;
        if(board[i][cur_y] == 0) board[i][cur_y] = 7;
    }
}
void Left(int cur_x, int cur_y){ // ←
    for(int i = cur_y - 1; i &gt;= 0; i--){
        if(board[cur_x][i] == 6) break;
        if(board[cur_x][i] == 0) board[cur_x][i] = 7;
    }
}
void Right(int cur_x, int cur_y){ // →
    for(int i = cur_y + 1; i &lt; m; i++){
        if(board[cur_x][i] == 6) break;
        else if(board[cur_x][i] == 0) board[cur_x][i] = 7;
    }
}

void Solve(int k){
    if(k == V.size()){
        int cnt = 0;
        for(int i = 0; i &lt; n; i++)
            for(int j = 0; j &lt; m; j++)
                if(board[i][j] == 0) cnt++;
        mn = min(mn, cnt);
        return;
    }

    int cur_x = V[k].first, cur_y = V[k].second;
    int temp[10][10];
    for(int i = 0; i &lt; n; i++)
        for(int j = 0; j &lt; m; j++) 
            temp[i][j] = board[i][j];

    if(board[cur_x][cur_y] == 1){
        for(int dir = 0; dir &lt; 4; dir++){
            if(dir == 0) Up(cur_x, cur_y);
            else if(dir == 1) Down(cur_x, cur_y);
            else if(dir == 2) Left(cur_x, cur_y);
            else Right(cur_x, cur_y);
            Solve(k + 1);
            for(int i = 0; i &lt; n; i++)
                for(int j = 0; j &lt; m; j++)
                    board[i][j] = temp[i][j];
        }
    }
    else if(board[cur_x][cur_y] == 2){
        for(int dir = 0; dir &lt; 2; dir++){
            if(dir == 0){
                Up(cur_x, cur_y);
                Down(cur_x, cur_y);
            }
            else{
                Left(cur_x, cur_y);
                Right(cur_x, cur_y);
            }
            Solve(k + 1);
            for(int i = 0; i &lt; n; i++)
                for(int j = 0; j &lt; m; j++)
                    board[i][j] = temp[i][j];
        }
    }
    else if(board[cur_x][cur_y] == 3){
        for(int dir = 0; dir &lt; 4; dir++){
            if(dir == 0){
                Left(cur_x, cur_y);
                Up(cur_x, cur_y);
            }
            else if(dir == 1){
                Up(cur_x, cur_y);
                Right(cur_x, cur_y);
            }
            else if(dir == 2){
                Right(cur_x, cur_y);
                Down(cur_x, cur_y);
            }
            else{
                Down(cur_x, cur_y);
                Left(cur_x, cur_y);
            }
            Solve(k + 1);
            for(int i = 0; i &lt; n; i++)
                for(int j = 0; j &lt; m; j++)
                    board[i][j] = temp[i][j];
        }
    }
    else if(board[cur_x][cur_y] == 4){
        for(int dir = 0; dir &lt; 4; dir++){
            if(dir == 0){
                Up(cur_x, cur_y);
                Down(cur_x, cur_y);
                Left(cur_x, cur_y);
            }
            else if(dir == 1){
                Up(cur_x, cur_y);
                Down(cur_x, cur_y);
                Right(cur_x, cur_y);
            }
            else if(dir == 2){
                Left(cur_x, cur_y);
                Right(cur_x, cur_y);
                Up(cur_x, cur_y);
            }
            else{
                Left(cur_x, cur_y);
                Right(cur_x, cur_y);
                Down(cur_x, cur_y);
            }
            Solve(k + 1);
            for(int i = 0; i &lt; n; i++)
                for(int j = 0; j &lt; m; j++)
                    board[i][j] = temp[i][j];
        }
    }
    else if(board[cur_x][cur_y] == 5){
        Up(cur_x, cur_y);
        Down(cur_x, cur_y);
        Left(cur_x, cur_y);
        Right(cur_x, cur_y);
        Solve(k + 1);
        for(int i = 0; i &lt; n; i++)
            for(int j = 0; j &lt; m; j++)
                board[i][j] = temp[i][j];
    }
}

int main(){
    fastio;

    cin &gt;&gt; n &gt;&gt; m;
    for(int i = 0; i &lt; n; i++){
        for(int j = 0; j &lt; m; j++){
            cin &gt;&gt; board[i][j];
            if(board[i][j] &gt; 0 &amp;&amp; board[i][j] &lt; 6)
                V.push_back({i,j});
        }
    }
    Solve(0);
    cout &lt;&lt; mn &lt;&lt; &#39;\n&#39;;
}</code></pre>
<ol>
<li><p>입력을 받을 때, CCTV의 경우 <code>V</code>벡터에 좌표 저장</p>
</li>
<li><p>백트래킹으로 해결
: CCTV를 만날 때 마다, 각각의 CCTV 경로를 탐색
: 이 때, 한 경로를 끝내면 이전 상태로 <code>board</code>를 초기화 해야함. 그래서 이전 상태를 <code>temp</code>에 저장하고, 경로 탐색을 마치면 <code>board</code>에 이전 상태가 저장된 <code>temp</code>를 복사해주었다.
: 각각의 탐색이 끝나면 <code>cnt</code>에 사각지대를 저장하고, 최솟값을 구해주었다.</p>
</li>
<li><p>CCTV의 경로는 <code>Up, Down, Left, Right</code> 함수로 구현
(처음에는 <code>Solve</code>함수에 일일이 다 구현해서 코드가 300줄 가까이 되었다. 이를 함수로 구현)</p>
</li>
</ol>
<h3 id="2-바킹독님의-풀이">(2) <a href="https://blog.encrypted.gg/948">바킹독님의 풀이</a></h3>
<p><a href="https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x0D/15683.cpp">(바킹독님의 코드 - 바킹독님의 깃헙)</a></p>
<ol>
<li>변수들이 가질 수 있는 값이 여러개이고</li>
<li>모든 조합을 다 확인하고 싶은데</li>
<li>변수들끼리는 서로 독립적일 때</li>
<li>백트래킹보다 나은 방법은?</li>
</ol>
<p>-&gt; <strong>진법</strong>을 이용</p>
<p>각각의 CCTV는 1 / 2 / 4가지의 방법이 있음.</p>
<p>따라서, 가능한 방향의 종류가 최대 4개이므로,** 4진법에 대응**시킬 것.</p>
<p>이 때, 지수를 표현해주어야 함.</p>
<ol>
<li><p><code>^</code>연산자? -&gt; C에서는 XOR 비트연산이므로 사용 X</p>
</li>
<li><p><code>pow</code> 함수 -&gt; 인자로 실수를 받고, return도 실수이므로 바람직하지 않음.
(double의 유효숫자는 15, long long은 19자리이므로, 정수의 정수 승에서 오차 발생할 수 있음)</p>
</li>
<li><p>for문 중첩 사용 -&gt; 4중 for문 사용하면 됨.</p>
</li>
<li><p>Left Shift 사용</p>
</li>
</ol>
<p>-&gt; <code>1 &lt;&lt; (2 * cctv.size())</code> == <code>4^(cctv.size())</code>
-&gt; 밑이 1이면, Left Shift한 만큼 2의 제곱수가 됨.
--&gt; <code>1(2) == 1(10)</code> -&gt; <code>100(2) == 4(10)</code></p>
<h4 id="21-4진법으로-변환-후-자리수-얻기">(2.1) 4진법으로 변환 후, 자리수 얻기</h4>
<p>만약, 1번 CCTV가 3개일 경우, 경우의 수는 64(4 * 4 * 4).
0 ~ 63을 4진법으로 나타내면, 000 001 ~ 332 333.
그리고 각각의 자리수를 방향 3개에 대응시킴.
예를 들어, 39(10) = 213(4)이므로, 방향을 (2,1,3)으로 둘 수 있음.
이렇게 하면 64가지의 조합을 모두 확인할 수 있음.</p>
<p>그렇다면 10진법을 4진법으로 바꾸고 각 자리수를 얻어야 함.</p>
<ul>
<li>10진법에서 각 자리수 얻기</li>
</ul>
<ol>
<li>마지막 자리수 : 10으로 나눈 나머지</li>
<li>나머지 자리수 : 10으로 나눈 후, 10으로 나눈 나머지</li>
</ol>
<p>-&gt; 4진법도 마찬가지로, <code>10</code>대신 <code>4</code>를 사용하면 됨.</p>
<p>따라서, <em><strong>CCTV의 개수가 <code>k</code>개 일 때는, <code>0 ~ 4^k - 1</code>까지에 대해 자리수를 얻으면 됨.</strong></em></p>
<h4 id="22-사각지대의-개수-세기">(2.2) 사각지대의 개수 세기</h4>
<p>CCTV가 감시할 수 있는 칸마다 <code>7</code>이라는 수로 마킹해 둠.
이 후에 <code>0</code>인 칸만 세면 됨.</p>
<p>이건 나랑 아이디어는 똑같은데 코드가 살짝다름.</p>
<p>나는 if문을 사용했고</p>
<pre><code class="language-cpp">for(int i = 0; i &lt; n; i++)
    for(int j = 0; j &lt; n; j++)
        if(board[i][j] == 0) cnt++;</code></pre>
<p>바킹독님은 비교 연산자를 사용함.</p>
<pre><code class="language-cpp">for(int i = 0; i &lt; n; i++)
    for(int j = 0; j &lt; n; j++)
        val += (board2[i][j] == 0);</code></pre>
<p>사실 결과는 똑같지만 이런 방법도 있구나...</p>
<h4 id="23-시간복잡도-계산">(2.3) 시간복잡도 계산</h4>
<ol>
<li><p><code>board2</code>에 <code>board1</code>을 복사할 때 <code>NM(최대 64칸)</code></p>
</li>
<li><p><code>upd</code>함수의 호출 횟수 : 5번 CCTV가 8개일 때, <code>4 * 8</code></p>
</li>
<li><p><code>upd</code>함수의 연산량 : 최대 <code>8번</code></p>
</li>
<li><p>빈 칸 확인 : <code>NM(최대 64칸)</code></p>
</li>
<li><p>방향의 개수 : 4^8</p>
</li>
</ol>
<p>-&gt; (64 + 4<em>8</em>8 + 64) * 4^8 = 25,165,824</p>
<p>약 2500만이므로 1초 안에 넉넉히 통과.</p>
<p>만약 계산결과가 5억 정도로 1초가 간당간당하다면,
cctv가 2번 일 때, dir = 2, 3이라면 continue하고,
cctv가 5번 일 때, dir != 0이면 continue하고,
upd함수에서 7로 바꿀 때 cnt를 증가시켜, 빈칸을 확인하는 연산량을 줄이던가 해야함.</p>
<hr>
<h2 id="2-연습문제-2--boj18808스티커-붙이기">2) 연습문제 2 : BOJ18808(스티커 붙이기)</h2>
<p>이 문제를 푸는 데 있어 핵심은 &quot;도형의 회전&quot;이었다.</p>
<p>이 회전을 구현 못해서 다른 블로그를 참고했다.</p>
<h3 id="1-바킹독님의-풀이">(1) <a href="https://blog.encrypted.gg/948">바킹독님의 풀이</a></h3>
<p><a href="https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x0D/18808.cpp">(바킹독님의 코드 - 바킹독님의 깃헙)</a></p>
<h4 id="11-스티커를-특정-영역에-붙일-수-있는지-확인하고-붙이기">(1.1) 스티커를 특정 영역에 붙일 수 있는지 확인하고 붙이기</h4>
<p>노트북의 (x,y)에 스티커의 (0,0)을 기준으로 붙이기.
노트북에 올라가는 스티커의 모든 칸이 겹치는 칸이 있는지 확인하고 노트북에 값 갱신하기.</p>
<h4 id="12-스티커-회전하기">(1.2) 스티커 회전하기</h4>
<p><code>B[x][y] = A[n - 1 - y][x]</code>
여기서 <code>n</code>은 원래 배열(<code>A</code>)의 행의 개수.</p>
<p>그리고 행과 열이 바뀌기 때문에
<code>swap(r,c)</code>를 빼먹으면 안됨.</p>
<h4 id="13-시간복잡도-계산">(1.3) 시간복잡도 계산</h4>
<ol>
<li><p>노트북에 놓을 수 있는지 확인하는 위치의 개수 : 4 * 40 * 40
: 4개의 회전 방향과, 최대 칸의 개수 : 40 * 40</p>
</li>
<li><p>모눈종이를 특정 위치에 놓을 수 있는지 확인하는 연산 : 10 * 10</p>
</li>
<li><p>스티커의 개수 : 최대 100개</p>
</li>
</ol>
<p>-&gt; 4 * 40 * 40 * 10 * 10 = 64,000,000</p>
<p>6400만이므로 2초 안에 넉넉히 통과.
(위의 경우는 최악의 경우를 계산한 것이므로, 일반적으로는 훨씬 더 빠르게 가능)</p>
<hr>
<h2 id="3-연습문제-3--boj121002048easy">3) 연습문제 3 : BOJ12100(2048(Easy))</h2>
<p>하...
이거 너무 어려워...</p>
<h3 id="1-바킹독님의-풀이-1">(1) <a href="https://blog.encrypted.gg/948">바킹독님의 풀이</a></h3>
<h4 id="11-게임판을-상하좌우로-기울이기">(1.1) 게임판을 상하좌우로 기울이기</h4>
<p>사실 난 처음에 숫자들을 각 방향으로 이동시키려고 했다.
그런데 풀이를 보니 한 방향으로 이동만 구현하고, 보드판 전체를 회전시켜 각 방향으로의 이동&quot;처럼&quot; 구현했다.
(이런 걸 어떻게 생각할까....)</p>
<pre><code class="language-cpp">if(board2[i][j] == 0) continue;

if(temp[idx] == 0) temp[idx] = board2[i][j];
else if(temp[idx] == board2[i][j]) temp[idx++] *= 2;
else temp[++idx] = board2[i][j];</code></pre>
<p>우선 보드의 값이 <code>0</code>이면 패스.
만약 값을 이동시킬 배열이 비어있으면, 값을 넣음.
만약 배열에 같은 값이 있고, 더한 적이 없으면, 값을 2배로 증가.
만약 다른 값이 있거나, 이전에 더한적이 있으면 새로운 인덱스에 추가</p>
<p>보드를 90도씩 회전하는 것은 앞선 문제와 동일.</p>
<h4 id="12-5번-기울이는-방향-정하기">(1.2) 5번 기울이는 방향 정하기</h4>
<p>문제에서 최대 5번만 이동한다고 했으므로,
5번 각각의 경우에 어떻게 기울일지 정해야 함.</p>
<p>그렇다면 5번 각각의 경우마다 4번(상하좌우)로 기울일 수 있기 때문에 경우의 수는 <code>4^5 == 1024</code>.</p>
<p>즉, 1024개의 경우를 탐색해줘야 한다.
<code>tmp</code>변수에 각 경우의 수를 저장하고, <code>brute</code> 변수에 진법을 이용해 표시한다.</p>
<pre><code class="language-cpp">for(int tmp = 0; tmp &lt; 1024; tmp++){
        for(int i = 0; i &lt; n; i++)
            for(int j = 0; j &lt; n; j++)
                board2[i][j] = board1[i][j];

        int brute = tmp;
        for(int i = 0; i &lt; 5; i++){
            int dir = brute % 4;
            brute /= 4;
            Move(dir);
        }
        for(int i = 0; i &lt; n; i++)
            for(int j = 0; j &lt; n; j++)
                mx = max(mx, board2[i][j]);
    }</code></pre>
<h4 id="13-시간복잡도-계산-1">(1.3) 시간복잡도 계산</h4>
<ol>
<li><p>기울임 처리를 위한 연산 횟수 : 20 * 20
: 행과 열의 최대 길이가 <code>20</code>씩임.</p>
</li>
<li><p>각각의 경우마다 기울이는 횟수는 5번</p>
</li>
<li><p>모든 경우의 수는 1024개</p>
</li>
</ol>
<p>-&gt; (20 * 20 * 5) * 1024 = 2,048,000</p>
<hr>
<h2 id="3-연습문제-4--boj15686치킨-배달">3) 연습문제 4 : BOJ15686(치킨 배달)</h2>
<p>사실 이 문제가 가장 쉬웠고, 바킹독님의 풀이와 거의 똑같았다.
그래서 내가 풀었던 방법대로 기술함.
(엉엉 너무 좋아)</p>
<h3 id="1-풀이">(1) 풀이</h3>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

#define X first
#define Y second
int n, m;
int city[51][51];  // 입력받을 원본 배열
vector&lt;pair&lt;int,int&gt;&gt; house; // 집의 좌표
vector&lt;pair&lt;int,int&gt;&gt; chicken; // 원본 치킨집 좌표

int main(){
    fastio;

    cin &gt;&gt; n &gt;&gt; m;
    for(int i = 0; i &lt; n; i++){
        for(int j = 0; j &lt; n; j++){ 
            cin &gt;&gt; city[i][j];
            if(city[i][j] == 1) house.push_back({i,j});
            if(city[i][j] == 2) chicken.push_back({i,j});
        }
    }

    // 서로 다른 치킨집 개수(chicken1.size()) 중 m개를 골라 살아남김
    vector&lt;int&gt; v;
    for(int i = 0; i &lt; chicken.size(); i++) v.push_back(i &lt; m ? 0 : 1);

    int ans = INT_MAX;
    do{
        // 폐업이 안된 치킨집 좌표
        vector&lt;pair&lt;int,int&gt;&gt; alive;
        for(int i = 0; i &lt; v.size(); i++)
            if(v[i] == 0)
                alive.push_back({chicken[i].X, chicken[i].Y});

        int sum_mn = 0;
        for(auto h : house){
            int dist_mn = 100;
            for(auto a : alive){
                int dist = abs(h.X - a.X) + abs(h.Y - a.Y);
                dist_mn = min(dist_mn, dist);
            }
            sum_mn += dist_mn;
        }
        ans = min(ans, sum_mn);
    }while(next_permutation(v.begin(), v.end()));

    cout &lt;&lt; ans &lt;&lt; &#39;\n&#39;;
}</code></pre>
<h4 id="11-폐업시키지-않을-치킨집-m개-고르기">(1.1) 폐업시키지 않을 치킨집 M개 고르기</h4>
<p>사실 이 부분이 핵심이었다.
각각의 경우마다 다 계산해주어야 하나?
백트래킹? 진법을 활용한 경우의 수 계산?</p>
<p>사실 이럴바엔 치킨집 N개 중에서 M개를 고르기만 하면 된다.
-&gt; 조합!!</p>
<p>백트래킹을 통한 조합의 구현보다는, next_permutation을 통한 구현이 훨씬 간단하기 때문에, 이를 통해 구현했다.</p>
<p><code>v</code>라는 벡터를 이용해 조합을 구현했다.</p>
<pre><code class="language-cpp">vector&lt;int&gt; v;
for(int i = 0; i &lt; chicken.size(); i++) v.push_back(i &lt; m ? 0 : 1);

do{
    ...
}while(next_permutation(v.begin(), v.end()));</code></pre>
<h4 id="12-도시의-치킨-거리-구하기">(1.2) 도시의 치킨 거리 구하기</h4>
<p>나같은 경우는, 맨 처음에 입력을 받을 때,
집의 좌표와, 치킨집의 좌표를 각각의 벡터에 저장했다.</p>
<p>그렇게 해야, 치킨 거리를 구할 때,
집의 좌표나 치킨집의 좌표를 구하려고 전체 배열을 순회하는 것을 피할 수 있기 때문
(만약 전체 배열을 순회해야 한다면, 집의 좌표를 구하기 위해 O(N²), 각 집마다 최소 치킨 거리를 구하기 위해 또 치킨집을 찾아야하므로 전체 배열을 다시 순회해야한다 O(N²). 그러면 O(N⁴)이 된다...)
(따라서, 각 경우를 벡터에 저장해주면, 집을 찾기 위해 전체 배열을 순회하지 않아도 된다. 집을 저장한 벡터만 순회하면 되므로 최대 O(N). 각각의 집마다 최소 치킨 거리를 찾아야 하므로, 치킨집이 저장된 벡터를 순회하면 되므로 O(N). 즉, O(N²)이 된다.)</p>
<pre><code class="language-cpp">for(int i = 0; i &lt; n; i++){
for(int j = 0; j &lt; n; j++){ 
        cin &gt;&gt; city[i][j];
        if(city[i][j] == 1) house.push_back({i,j});
        if(city[i][j] == 2) chicken.push_back({i,j});
    }
}</code></pre>
<p>이후에, 폐업되지 않은 치킨집을 저장할 벡터를 선언해, 폐업되지 않은 치킨집의 좌표를 벡터에 저장한다.
그리고 이 벡터와 집이 저장된 벡터를 이용해 최소 거리를 구한다.</p>
<pre><code class="language-cpp">int ans = INT_MAX;
do{
    // 폐업이 안된 치킨집 좌표
    vector&lt;pair&lt;int,int&gt;&gt; alive;
    for(int i = 0; i &lt; v.size(); i++)
        if(v[i] == 0)
            alive.push_back({chicken[i].X, chicken[i].Y});

    int sum_mn = 0;
    for(auto h : house){
        int dist_mn = 100;
        for(auto a : alive){
            int dist = abs(h.X - a.X) + abs(h.Y - a.Y);
            dist_mn = min(dist_mn, dist);
        }
        sum_mn += dist_mn;
    }
    ans = min(ans, sum_mn);
}while(next_permutation(v.begin(), v.end()));</code></pre>
<h4 id="13-시간-복잡도-계산">(1.3) 시간 복잡도 계산</h4>
<ol>
<li>각 집에 대해 모든 치킨집과의 거리를 다 계산해야 함 : 100 * 13</li>
<li>폐업시키지 않을 치킨집을 뽑는 &quot;최대&quot; 경우의 수 : ₁₃C₆
: 조합에서 가장 큰 값을 가지려면, 뽑을 개수가 전체 개수의 절반에 최대한 가까워야 함.(따라서 6 또는 7일 때 최대)</li>
</ol>
<p>-&gt; 100 * 13 * ₁₃C₆ = 2,230,800</p>
<hr>
<h1 id="2-qa">2. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="3-마치며">3. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/948">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0D강 - 시뮬레이션&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222605154765&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 08. 구현 + 시뮬레이션&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 5. 백트랙킹(Backtracking)]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-5.-%EB%B0%B1%ED%8A%B8%EB%9E%99%ED%82%B9Backtracking</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-5.-%EB%B0%B1%ED%8A%B8%EB%9E%99%ED%82%B9Backtracking</guid>
            <pubDate>Mon, 02 Jan 2023 04:29:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/945">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0C강 - 백트래킹&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222604405729&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 07. 백트래킹(Backtracking)&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>백트랙킹(Backtracking)</li>
<li>STL next_permutation</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-백트랙킹backtracking">1. 백트랙킹(Backtracking)</h1>
<blockquote>
<ul>
<li><em><strong>백트랙킹(Backtracking)</strong></em>
: 현재 상태에서 <strong>가능한 모든 후보군을 따라 들어가며 탐색</strong>하는 알고리즘</li>
</ul>
</blockquote>
<p>(예제는, <a href="https://blog.encrypted.gg/945">&#39;바킹독님 블로그&#39;</a> 참조)</p>
<h2 id="1-가지-치기pruning">1) 가지 치기(pruning)</h2>
<p>재귀 호출을 트리 구조로 생각했을 때,
가지치기는 트리 edge를 끊어서 해당 subtree는 탐색하지 않는 것을 의미.</p>
<ol>
<li><p>naive한 재귀 풀이를 생각</p>
</li>
<li><p>현재 상태에서 정답을 갱신할 수 있는지 확인하는 로직</p>
</li>
<li><p>가지치기를 적용해 더 효율적으로 해결</p>
</li>
</ol>
<hr>
<h1 id="2-stl-next_permutation">2. STL next_permutation</h1>
<pre><code class="language-cpp">int a[3] = {1, 2, 3};
do{
    for(int i = 0; i &lt; 3; i++)
        cout &lt;&lt; a[i];
    cout &lt;&lt; &#39;\n&#39;;
}while(next_permutation(a, a+3));
/*
123
132
213
231
312
321
*/</code></pre>
<p>next_permutation은 현재의 수열을 사전 순으로 생각했을 때의, 다음 수열을 만들고 true를 반환하는 함수.</p>
<p>만약 다음 수열이 존재하지 않으면 false를 반환.</p>
<p>따라서 do-while문을 이용하면 코드가 깔끔해짐.</p>
<p>조합을 구할 때는, 원래의 수열에서 <code>0</code>과 <code>1</code>을 이용하면 구할 수 있음
위의 예시에서, 3개 중 2개를 뽑는다고 하면
배열 <code>a</code>를 <code>{0,1,1}</code>처럼 두면 됨.</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/945">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0C강 - 백트래킹&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222604405729&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 07. 백트래킹(Backtracking)&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 4. 재귀]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-4.-%EC%9E%AC%EA%B7%80</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-4.-%EC%9E%AC%EA%B7%80</guid>
            <pubDate>Thu, 29 Dec 2022 05:56:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/943">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0B강 - 재귀&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222587262628&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 05. 재귀 / 분할정복(DnC)&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>재귀</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-재귀recursion">1. 재귀(Recursion)</h1>
<blockquote>
<ul>
<li><em><strong>재귀(Recursion)</strong></em>
: 하나의 함수에서 자기 자신을 다시 호출해 작업을 수행하는 알고리즘</li>
</ul>
</blockquote>
<p>예를 들어, 다음은 1부터 n까지 출력하는 함수, 1부터 n까지의 합을 구하는 함수입니다.</p>
<pre><code class="language-cpp">void func1(int n){
    if(n == 0) return;
    cout &lt;&lt; n &lt;&lt; &#39; &#39;;
    func1(n-1);
}

int func2(int n){
    if(n == 0) return 0;
    return n + func2(n-1);
}</code></pre>
<h2 id="1-수학적-귀납법">1) 수학적 귀납법</h2>
<p>예를 들어 도미노를 쓰러뜨릴 때, 왜 모든 도미노가 쓰러지는지 설명해야 함</p>
<ol>
<li><p>1번 도미노가 쓰러지면 2번도 쓰러짐 -&gt; 2번이 쓰러지면 3번도 쓰러짐 -&gt; ...</p>
</li>
<li><p>수학적 귀납법
1번 도미노가 쓰러짐 -&gt; k번 도미노가 쓰러지면 k+1번 도미노도 쓰러짐이 True! -&gt; 따라서 모든 도미노가 쓰러짐.</p>
</li>
</ol>
<p>앞으로는 1번 방식이 아닌, 2번 방식으로 바로 사고할 수 있도록 해야 함.
(이전까지의 절차지향적 사고를 탈피해야 함!)</p>
<h3 id="1-절차지향적-사고">(1) 절차지향적 사고</h3>
<p>다음은 위의 1부터 n까지 출력하는 함수.</p>
<pre><code class="language-cpp">void func1(int n){
    if(n == 0) return;
    cout &lt;&lt; n &lt;&lt; &#39; &#39;;
    func1(n-1);
}</code></pre>
<p><code>func1(3)</code>을 호출하면 <code>3</code>을 출력하고 <code>func1(2)</code>를 호출함.
<code>func1(2)</code>를 호출하면 <code>2</code>를 출력하고 <code>func1(1)</code>을 호출함.
<code>func1(1)</code>을 호출하면 <code>1</code>을 출력하고 <code>func1(0)</code>을 호출함.
<code>func1(0</code>은 조건문에 의해 걸려, 함수가 종료됨.
따라서 출력은 <code>3 2 1</code>이 됨.</p>
<h3 id="2-귀납적-사고">(2) 귀납적 사고</h3>
<ol>
<li><code>func(1)</code>은 <code>1</code>을 출력한다.</li>
</ol>
<p>1번은 굉장히 자명함.</p>
<ol start="2">
<li><p>그 다음은
&#39;<code>func1(k)</code>가 <code>k, k-1, k-2, ... , 1</code>을 출력한다면, <code>func1(k+1)</code>가 <code>k+1, k, k-1, ... ,1</code>을 출력한다&#39;
는 것을 보여야 함.</p>
</li>
<li><p><code>func1(k+1)</code>을 호출하면, <code>k+1</code>을 출력하고 <code>func1(k)</code>를 호출함.
이 때, <code>func1(k)</code>는 위의 가정에 의해, <code>k ~ 1</code>까지를 출력한다고 했으므로,
<code>func1(k+1)</code>은 <code>(k + 1) ~ 1</code>까지 출력함.</p>
</li>
</ol>
<p>앞으로 모든 재귀는, 1번과 같은 절차지향적 사고가 아닌, 2번과 같은 <em><strong>귀납적 사고로 이해해야 함.</strong></em></p>
<hr>
<h2 id="2-재귀-함수의-조건">2) 재귀 함수의 조건</h2>
<blockquote>
<ul>
<li><em><strong>재귀 함수의 조건</strong></em>
: <strong>특정 입력에 대해서는</strong> 자기 자신을 호출하지 않고 <strong>종료되어야 함</strong>(Base Condition, Base Case)
: <strong>모든 입력은 Base Condition으로 수렴</strong>해야 함.</li>
</ul>
</blockquote>
<pre><code class="language-cpp">void func1(int n){
    if(n == 0) return;
    cout &lt;&lt; n &lt;&lt; &#39; &#39;;
    func1(n-1);
}</code></pre>
<p>위의 코드에서, if문에서 <code>n</code>이 <code>0</code>이면 자기 자신을 호출하지 않고 종료됩니다.
따라서 이것이 Base Condition.</p>
<p>그리고 우리는 이 함수에 자연수만 넣을테니, 모든 입력은 <code>n = 0(Base Condition)</code>으로 수렴됨.</p>
<p>만약 이 두 조건 중 하나라도 지켜지지 않는다면, 재귀함수는 무한번 실행되다가 런타임 에러를 발생시킴.</p>
<hr>
<h2 id="3-재귀에-대한-tip">3) 재귀에 대한 Tip</h2>
<h3 id="1-함수를-명확하게-정의한다">(1) 함수를 명확하게 정의한다!</h3>
<p>정의라는 것은</p>
<ol>
<li><p>함수의 인자로 어떤 것을 받는가</p>
</li>
<li><p>어디까지 계산한 후에, 자기 자신으로 넘겨주는가</p>
</li>
</ol>
<p>를 의미함.</p>
<h3 id="2-모든-재귀는-반복문만으로도-동일한-동작을-할-수-있다">(2) 모든 재귀는 반복문만으로도 동일한 동작을 할 수 있다!</h3>
<p>재귀는 상황에 따라 코드는 간결해지지만, 함수 호출 비용은 비용이 큰 연산이므로, 메모리와 시간에서 손해를 봄.</p>
<p>따라서, 재귀를 굳이 쓰지 않아도 되고 구현에 큰 어려움이 없으면 반복문으로 쓰는게 좋음.</p>
<p>하지만 반복문으로 구현했을 때 코드가 복잡해지면 재귀로 구현하는 게 나음.</p>
<h3 id="3-재귀는-비효율적일-수-있다">(3) 재귀는 비효율적일 수 있다!</h3>
<p>재귀 함수가 자기 자신을 여러 번 호출하면 예상과는 다르게 비효율적일 수 있음.</p>
<p>예를 들어 피보나치 수열을 재귀함수로 짜면, 시간복잡도는 지수함수 형태임
(n = 100만 되어도 2만년이 넘게 걸림)</p>
<p>그 이유는 다음과 같음.
<code>fibo(5)</code>를 할 경우, <code>fibo(4), fibo(3)</code>을 호출
<code>fibo(4)</code>를 할 경우, <code>fibo(3), fibo(2)</code>를 호출
<code>fibo(3)</code>를 할 경우, <code>fibo(2), fibo(1)</code>을 호출</p>
<p>그러면 <code>fibo(4)</code>에서 나온 <code>fibo(3)</code>을 계산하고, 또 <code>fibo(5)</code>에서 나온 <code>fibo(3)</code>을 다시 계산해야 함.</p>
<p>이미 다른 과정에서 계산한 값을, 또 다른 과정에서 다시 계산하는 일이 일어남.</p>
<h3 id="4-재귀-함수가-자기-자신을-부를-때-스택-영역에-계속-누적된다">(4) 재귀 함수가 자기 자신을 부를 때, 스택 영역에 계속 누적된다!</h3>
<p>만약 문제를 풀 때, 메모리 제한이 512MB라면, 프로그램이 점유하는 메모리가 최대 512MB여야 함.
일부 컴파일 환경에서는 스택 영역의 메모리가 별도로 1MB로 제한되어 있기도 함.</p>
<p>BOJ에서는 스택 메모리 제한이 없지만, swexpertacademy에는 있음.
따라서 후자처럼 스택 메모리 제한이 작게 제한된 곳에서는, 재귀를 깊게 들어가는 풀이라면 반복문으로 변환해야 함...</p>
<hr>
<h2 id="4-예시---하노이-탑">4) 예시 - 하노이 탑</h2>
<h3 id="1-귀납적-사고">(1) 귀납적 사고</h3>
<ol>
<li>n-1개의 원판을 기둥1에서 기둥 2로 옮긴다.</li>
<li>n번 원판을 기둥 1에서 기둥 3으로 옮긴다.</li>
<li>n-1개의 원판을 기둥2에서 기둥 3으로 옮긴다.</li>
</ol>
<p>즉, 원판이 n-1개일 때 옮길 수 있으면 원판이 n개일 때도 옮길 수 있음.</p>
<p>귀납적 사고를 하면,</p>
<ol>
<li>원판이 1개 일 때 원하는 곳으로 옮길 수 있음.</li>
<li>원판이 k개 일 때 원하는 곳으로 옮길 수 있으면, 원판이 k+1개 일 때에도 옮길 수 있음.</li>
</ol>
<h3 id="2-구현">(2) 구현</h3>
<h4 id="21-함수의-정의">(2.1) 함수의 정의</h4>
<p>함수의 정의는, 함수가 어떤 역할을 수행하고 어떤 인자를 받는지 정하는 것.</p>
<pre><code class="language-cpp">void func(int n)</code></pre>
<p>위의 정의는 틀림!
왜냐 하면, 우리는 n-1개를 기둥1에서 기둥 3으로 옮기는 것이 아닌, 기둥2로 옮겨야 하기 때문.</p>
<pre><code class="language-cpp">void func(int a, int b, int n)</code></pre>
<p>원판 n개를 기둥a에서 기둥b로 옮기는 함수의 정의.</p>
<h4 id="22-base-condition">(2.2) Base Condition</h4>
<p>n = 1일 때, a에서 b로 옮기면 됨.</p>
<h4 id="23-재귀-식">(2.3) 재귀 식</h4>
<ol>
<li><p>n-1개의 원판을 기둥 a에서 기둥 6-a-b로 옮김</p>
<pre><code class="language-cpp">func(a, 6-a-b, n-1);</code></pre>
</li>
<li><p>n번 원판을 기둥a에서 기둥b로 옮김</p>
<pre><code class="language-cpp">cout &lt;&lt; a &lt;&lt; &#39; &#39; &lt;&lt; b &lt;&lt; &#39;\n&#39;;</code></pre>
</li>
<li><p>n-1개의 원판을 기둥6-a-b에서 기둥b로 옮김</p>
<pre><code class="language-cpp">func(6-a-b, b, n-1);</code></pre>
</li>
</ol>
<p>참고로, 옮긴 횟수도 생각할 수 있음.
만약 원판 k + 1개를 옮길 때,
원판 k개를 옮기기 위해 x번,
원판 k+1번을 옮길 때 1번,
원판 k개를 다시 옮길 때 x번으로,
총 2X + 1번의 이동이 일어남.</p>
<p>더욱이 초항이 1이므로, 수열의 일반한은 2^k - 1이 됨.</p>
<hr>
<h1 id="2-qa">2. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="3-마치며">3. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/943">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0B강 - 재귀&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222587262628&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 05. 재귀 / 분할정복(DnC)&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 3. DFS]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3.-DFS</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3.-DFS</guid>
            <pubDate>Thu, 15 Dec 2022 05:31:33 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/942">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0A강 - DFS&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>DFS</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-bfs">1. BFS</h1>
<blockquote>
<ul>
<li><em><strong>DFS(Depth First Search)</strong></em>
: 다차원 배열에서 각 칸을 방문할 때, <strong>깊이를 우선</strong>으로 방문하는 알고리즘</li>
</ul>
</blockquote>
<h2 id="1-다차원-배열의-dfs의-방법">1) 다차원 배열의 DFS의 방법</h2>
<ol>
<li><p>시작하는 칸을 큐에 넣고, 방문했다는 표시를 남김</p>
</li>
<li><p>스택에서 원소를 꺼내어, 그 칸의 상하좌우로 인접한 칸에 대해 3번을 진행</p>
</li>
<li><p>해당 칸을 이전에 방문했다면 아무것도 하지 않고, 처음으로 방문했다면 방문했다는 표시를 남기고 해당 칸을 스택에 삽입</p>
</li>
<li><p>스택가 빌 때까지 2번을 반복</p>
</li>
</ol>
<p>DS의 시간복잡도는,
방문했다는 표시를 남기기 때문에 모든 칸은 스택에 1번씩만 들어가게 됨.
따라서 시간 복잡도는, 칸이 N개 일 때 O(N).
만약 행이 R개, 열이 C개라면 O(RC).</p>
<p>BFS에서 <em><strong>큐가 스택으로 변한 것</strong></em> 외에는 전부 똑같음.
즉, 큐를 쓰면 BFS, 스택을 쓰면 DFS.</p>
<hr>
<h2 id="2-다차원-배열의-bfs의-구현">2) 다차원 배열의 BFS의 구현</h2>
<p>BFS와 동일하고, 큐가 스택으로만 바뀌면 됨.</p>
<p>(코드가 커서 &#39;<a href="https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x0A/DFS.cpp">바킹독님의 깃헙 링크</a>&#39;를 참고)</p>
<hr>
<h2 id="3-bfs-vs-dfs">3) BFS vs DFS</h2>
<p>BFS와 DFS는, 큐와 스택을 쓴다는 차이점이 있지만, 원소 하나를 빼내고 주변을 살펴본다는 알고리즘의 흐름은 동일.</p>
<p>하지만 _<strong>방문순서</strong>_에서 가장 큰 차이가 있음.</p>
<p>BFS는 시작점을 중심으로, 동심원처럼 퍼져나감.
(BFS의 성질, 거리 순으로 원소를 방문함)
DFS는 한 방향으로 막힐 때까지 나아감.</p>
<p>따라서, BFS에서 사용한, &quot;<em><strong>현재 칸으로부터 인접한 칸은 (현재 칸 + 1)이다&quot;는 DFS에서는 성립하지 않음.</strong></em></p>
<p>결국, <em><strong>우리는 다차원 배열에서 DFS를 굳이 써야 하는 이유가 없음.</strong></em>
Flodd Fill은 어느 것을 써도 되지만, 거리 측정은 BFS만 가능하므로 <em><strong>BFS가 더 용이.</strong></em></p>
<p>대부분 다차원 배열을 순회하는 문제는 BFS를 사용할 것.
(DFS는 그래프에서 더 다룰 것)</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/942">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x0A강 - DFS&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 2. BFS]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2.-BFS</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2.-BFS</guid>
            <pubDate>Wed, 14 Dec 2022 07:44:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/941">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x09강 - BFS&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>BFS</li>
<li>BFS의 유형</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-bfs">1. BFS</h1>
<blockquote>
<ul>
<li><em><strong>BFS(Breadth First Search)</strong></em>
: 다차원 배열에서 각 칸을 방문할 때, <strong>너비를 우선</strong>으로 방문하는 알고리즘</li>
</ul>
</blockquote>
<p>원래 BFS는 그래프 자료구조에서 모든 노드를 방문하기 위한 알고리즘.
따라서, BFS를 정확하게 이해하려면, 그래프 자료구조에 대해 알고 있어야 함.</p>
<p>여기서는, 다차원 배열에서의 BFS를 중심.</p>
<h2 id="1-다차원-배열의-bfs의-방법">1) 다차원 배열의 BFS의 방법</h2>
<ol>
<li><p>시작하는 칸을 큐에 넣고, 방문했다는 표시를 남김</p>
</li>
<li><p>큐에서 원소를 꺼내어, 그 칸의 상하좌우로 인접한 칸에 대해 3번을 진행</p>
</li>
<li><p>해당 칸을 이전에 방문했다면 아무것도 하지 않고, 처음으로 방문했다면 방문했다는 표시를 남기고 해당 칸을 큐에 삽입</p>
</li>
<li><p>큐가 빌 때까지 2번을 반복</p>
</li>
</ol>
<p>BFS의 시간복잡도는,
방문했다는 표시를 남기기 때문에 모든 칸은 큐에 1번씩만 들어가게 됨.
따라서 시간 복잡도는, 칸이 N개 일 때 O(N).
만약 행이 R개, 열이 C개라면 O(RC).</p>
<hr>
<h2 id="2-다차원-배열의-bfs의-구현">2) 다차원 배열의 BFS의 구현</h2>
<p>우선 <code>utility</code>헤더의 <code>pair</code>라는 STL을 사용할 것.
(BFS를 구현할 때, 큐에 좌표를 넣을 때 사용)</p>
<p>(코드가 커서 &#39;<a href="https://github.com/encrypted-def/basic-algo-lecture/blob/master/0x09/BFS.cpp">바킹독님의 깃헙 링크</a>&#39;를 참고)
(BFS의 정석적인 구현은, 거의 외우다시피 해야함.)</p>
<hr>
<h1 id="2-bfs의-유형">2. BFS의 유형</h1>
<h2 id="1-flood-fill">1) Flood Fill</h2>
<p>어떤 칸과 연결된 영역을 찾는 유형</p>
<p><em><strong>이중 for문으로 각각의 칸이 BFS의 시작점이 될 수 있는지 체크해주기.</strong></em>
시간복잡도는 칸의 개수. O(NM).</p>
<h2 id="2-거리-측정">2) 거리 측정</h2>
<p>_<strong>기존 배열을 <code>-1</code>로 초기화</strong>_하면, 굳이 vis 배열 없이 방문 여부 확인 가능.</p>
<h2 id="3-시작점이-여러-개일-때">3) 시작점이 여러 개일 때</h2>
<ol>
<li><p><em><strong>거리가 0인 칸들을 큐에 다 추가</strong></em></p>
</li>
<li><p>거리가 0인 칸들에 의해, 거리가 1인 칸들이 큐에 다 추가됨. 마지막으로 거리가 0인 칸들은 큐에서 모두 pop됨.</p>
</li>
<li><p>마찬가지로, 거리가 1인 칸들에 의해, 거리가 2인 칸들이 큐에 다 추가됨. 마지막으로 거리가 1인 칸들은 큐에서 모두 pop됨.</p>
</li>
<li><p>...</p>
</li>
<li><p>즉, 전체적인 큐의 모양을 확인하면, _<strong>BFS를 돌 때 큐에 쌓이는 순서는 반드시 거리 순</strong>_인게 됨.</p>
</li>
</ol>
<h2 id="4-시작점이-두-종류일-때">4) 시작점이 두 종류일 때</h2>
<p><em><strong>각각의 종류마다, BFS를 돌리면 됨.</strong></em></p>
<p>두 종류면, 두 번의 BFS를 돌리기</p>
<h2 id="5-1차원에서의-bfs">5) 1차원에서의 BFS</h2>
<p>2차원 BFS와 큰 차이 없음.</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/941">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x09강 - BFS&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 6. 스택의 활용(수식의 괄호쌍)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-6.-%EC%8A%A4%ED%83%9D%EC%9D%98-%ED%99%9C%EC%9A%A9%EC%88%98%EC%8B%9D%EC%9D%98-%EA%B4%84%ED%98%B8%EC%8C%8D</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-6.-%EC%8A%A4%ED%83%9D%EC%9D%98-%ED%99%9C%EC%9A%A9%EC%88%98%EC%8B%9D%EC%9D%98-%EA%B4%84%ED%98%B8%EC%8C%8D</guid>
            <pubDate>Mon, 12 Dec 2022 06:10:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/936">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x08강 - 스택의 활용(수식의 괄호 쌍)&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>수식의 괄호쌍</li>
<li>문제 해결</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-수식의-괄호쌍">1. 수식의 괄호쌍</h1>
<p><code>32-{6-(2+4)*7} -&gt; { ( ) }  : X</code>
<code>5+{6-(12+4}*7) -&gt; { ( } )  : O</code></p>
<p>수식의 괄호쌍이란, 주어진 괄호 문자열이 올바른지 판단하는 문제.</p>
<hr>
<h1 id="2-문제-해결">2. 문제 해결</h1>
<h2 id="1-문제-해결을-위한-관찰">1) 문제 해결을 위한 관찰</h2>
<p><code>(())()() : O</code>
<code>))(()    : X</code>
<code>()())(() : X</code></p>
<p>해당 괄호쌍이 맞는지 아닌지 판단하는 &#39;로직&#39;에 대해 생각해보아야 함.</p>
<ul>
<li><p>가장 보편적인 방법, 안쪽부터 짝맞추기를 해서 지워가는 방법. 짝이 다 맞으면 올바른 괄호쌍, 아니면 틀린 괄호쌍.</p>
</li>
<li><p><code>)</code>의 개수가 <code>(</code>개수를 넘지 않으면 됨.</p>
</li>
<li><blockquote>
<p>괄호의 종류가 여러가지라면, 괄호의 갯수만으로 판단하기는 부족.
ex) <code>({}) : O, ({)} : X</code></p>
</blockquote>
</li>
</ul>
<p>대신, 붙어있는 <code>(), {}</code>를 지우는 방법은 여전히 동작.</p>
<p>위 방법을 배열로 구현할 경우 최대 N번 중간에 있는 원소의 삭제가 발생하므로 O(N²), 연결 리스트의 경우 O(N).
그러나 스택은 더 간단하게 구현 가능.</p>
<p>따라서 스택으로 구현을 할텐데, 추가로 생각해야 하는 점이 있음.</p>
<ul>
<li>닫는 괄호를, &quot;<em><strong>남아있는 괄호 중에서 가장 최근에 들어온 여는 괄호와 짝을 지어 없애버리는 명령</strong></em>&quot;이라고 생각해도 됨.</li>
</ul>
<h2 id="2-문제-해결-방법">2) 문제 해결 방법</h2>
<h3 id="1-여는-괄호가-나오면-스택에-추가">(1) 여는 괄호가 나오면 스택에 추가</h3>
<h3 id="2-닫는-괄호가-나왔을-경우">(2) 닫는 괄호가 나왔을 경우,</h3>
<h4 id="21-스택이-비어있으면-올바르지-않은-괄호쌍">(2.1) 스택이 비어있으면, 올바르지 않은 괄호쌍</h4>
<h4 id="22-스택의-top이-짝이-맞지-않는-괄호일-경우-올바르지-않은-괄호쌍">(2.2) 스택의 top이 짝이 맞지 않는 괄호일 경우, 올바르지 않은 괄호쌍</h4>
<h4 id="23-스택의-top이-짝이-맞는-괄호일-경우-pop">(2.3) 스택의 top이 짝이 맞는 괄호일 경우, pop</h4>
<h3 id="3-모든-과정을-끝낸-후">(3) 모든 과정을 끝낸 후,</h3>
<h4 id="31-스택에-괄호가-남아있으면-올바르지-않은-괄호쌍">(3.1) 스택에 괄호가 남아있으면, 올바르지 않은 괄호쌍</h4>
<h4 id="32-스택에-괄호가-남아있지-않으면-올바른-괄호쌍">(3.2) 스택에 괄호가 남아있지 않으면, 올바른 괄호쌍</h4>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/936">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x08강 - 스택의 활용(수식의 괄호 쌍)&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 5. 덱(Deque)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-5.-%EB%8D%B1Deque</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-5.-%EB%8D%B1Deque</guid>
            <pubDate>Fri, 09 Dec 2022 12:25:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/935">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x07강 - 덱&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222447852688&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ deque&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>덱(Deque)</li>
<li>STL deque</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-덱deque">1. 덱(Deque)</h1>
<blockquote>
<ul>
<li><em><strong>덱(Deque, Double Ended Queue)</strong></em>
: 양쪽 끝에서 원소 넣거나 뺄 수 있는 자료구조</li>
</ul>
</blockquote>
<p>스택과 큐는 덱의 특수한 예시라고 볼 수도 있음.</p>
<h2 id="1-덱의-성질">1) 덱의 성질</h2>
<h3 id="1-원소의-추가--제거-o1">(1) 원소의 추가 / 제거 O(1)</h3>
<h3 id="2-제일-앞--뒤의-원소-확인-o1">(2) 제일 앞 / 뒤의 원소 확인 O(1)</h3>
<h3 id="3-제일-앞--뒤가-아닌-나머지-원소들의-확인--변경이-원칙적으로-불가능">(3) 제일 앞 / 뒤가 아닌, 나머지 원소들의 확인 / 변경이 원칙적으로 불가능</h3>
<p><em><strong>단, STL deque는 인덱스로 원소에 접근 가능함.</strong></em></p>
<hr>
<h2 id="2-덱의-구현">2) 덱의 구현</h2>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[2*MX+1];
int head = MX, tail = MX;</code></pre>
<p><code>dat</code>배열에는 덱 원소들의 값이 들어가고, <code>head</code>는 제일 앞의 원소를 가리키고, <code>tail</code>은 제일 뒤의 원소 + 1을 가리키고 있음.
그리고 <code>dat[head]부터 dat[tail - 1]</code>까지 큐의 원소들이 있는 자리임. 그리고 큐의 크기는 <code>tail - head</code>로 계산 가능.</p>
<p>단, <code>head</code>와 <code>tail</code>의 초기값이 <code>0</code>이 아닌, <code>MX</code>임.
덱은 원소를 양쪽 끝에서 추가할 수 있기 때문에, <code>head</code>를 <code>0</code>으로 했을 때 왼쪽으로 확장할 수 없음.
따라서 중간지점에서 양쪽 끝으로 확장해 나가야 함.</p>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[2*MX+1];
int head = MX, tail = MX;

void push_front(int x){
    dat[--head] = x;
}

void push_back(int x){
    dat[tail++] = x;
}

void pop_front(){
    head++;
}

void pop_back(){
    tail--;
}

int front(){
    return dat[head];
}

int back(){
    return dat[tail - 1];
}</code></pre>
<hr>
<h1 id="3-벡터와-덱">3) 벡터와 덱</h1>
<p>STL queue는 double ended queue보다는 vector의 느낌이 강함.
pop_front, push_front, pop_back, push_front는 그렇다 하지만, 
insert, erase, 인덱스 접근도 가능.</p>
<h3 id="1-deque의-장점">(1) deque의 장점</h3>
<h4 id="11-vector와-달리-push_front-pop_front-연산-제공">(1.1) vector와 달리 push_front(), pop_front() 연산 제공</h4>
<h4 id="12-capacity와-size가-같아지면-특정-크기의-chunk를-할당해서-크기를-늘려줌">(1.2) capacity와 size가 같아지면 특정 크기의 chunk를 할당해서 크기를 늘려줌.</h4>
<p>vector는 1.5 ~ 2배로 늘려서 전체를 재할당함.</p>
<p>따라서 vector에 비해 확장 비용을 줄여줌.</p>
<h3 id="2-deque의-단점">(2) deque의 단점</h3>
<h4 id="21-비연속적으로-메모리-상에-존재하기-때문에-cache-hit-rate가-떨어져-vector에-비해-느림">(2.1) 비연속적으로 메모리 상에 존재하기 때문에, cache hit rate가 떨어져 vector에 비해 느림.</h4>
<h4 id="22-비연속적으로-메모리-상에-존재하기-때문에-원소들을-가리키는-포인터-간의-연산은-불가능">(2.2) 비연속적으로 메모리 상에 존재하기 때문에, 원소들을 가리키는 포인터 간의 연산은 불가능.</h4>
<p>단, iterator 간의 연산은 가능.</p>
<hr>
<h2 id="4-덱과-스택-큐">4) 덱과 스택, 큐</h2>
<p>덱은 스택과 큐의 연산을 모두 지원.
따라서 스택과 큐는 덱으로 대체 가능.</p>
<p>그래도,
push_back, pop_back 연산만 사용하면 stack,
push_back, pop_front 연산만 사용하면 queue를 사용하는 것이 좋음.
이렇게 하면 해당 객체가 어떤 역할을 수행하는지 판단할 수 있으므로 가독성 향상!</p>
<p>단, 스택과 큐는 default container가 deque이기 때문에, 스택과 큐가 약간 느릴 수 있지만 큰 차이는 없음.</p>
<hr>
<h1 id="2-stl-deque">2. STL deque</h1>
<h2 id="1-생성자">1) 생성자</h2>
<pre><code class="language-cpp">1. deque&lt;int&gt; DQ;
2. deque&lt;int&gt; DQ(n); 또는 deque&lt;int&gt; DQ(n, val);
3. deque&lt;int&gt; DQ(dq.begin(), dq.end());
4. deque&lt;int&gt; DQ{ 1, 2, 3}; 또는 deque&lt;int&gt; DQ = { 1, 2, 3 };</code></pre>
<ol>
<li>비어있는 덱 생성</li>
<li>n개의 원소를 갖는 덱 생성
후자는 n개의 원소를 val값으로 초기화</li>
<li>다른 컨테이너의 iterator를 받아온 뒤, 순회하며 덱에 해당 범위의 원소를 채워줌</li>
<li>initializer_list를 인자로 받는 생성자</li>
</ol>
<hr>
<h2 id="2-멤버-함수">2) 멤버 함수</h2>
<pre><code class="language-cpp">// DQ = { }
deque&lt;int&gt; DQ;

// DQ = { 1, 2, 3, 4, 5 }
for (int i = 1; i &lt;= 5; i++) DQ.push_back(i);
// DQ = { 10, 9, 8, 7, 6, 1, 2, 3, 4, 5 }
for (int i = 6; i &lt;= 10; i++) DQ.push_front(i);

// DQ = { 7, 6, 1, 2, 3, 4, 5 }
for (int i = 1; i &lt;= 3; i++) DQ.pop_front();
// DQ = { 7, 6, 1, 2 }
for (int i = 1; i &lt;= 3; i++) DQ.pop_back();</code></pre>
<h3 id="1-push_back--push_front">(1) push_back / push_front</h3>
<p>마지막 위치에 원소를 삽입. O(1)</p>
<p>시작 위치에 원소를 삭제. O(1)</p>
<p>emplace_back, emplace_front로 대체 가능.</p>
<h3 id="2-pop_back--pop_front">(2) pop_back / pop_front</h3>
<p>마지막 위치에 원소를 삭제. O(1)</p>
<p>시작 위치에 원소를 삭제. O(1)</p>
<p>반환값은 void.</p>
<h3 id="3-insert--erase">(3) insert / erase</h3>
<p>임의의 위치에 원소 삽입 / 삭제. O(n)</p>
<p>시간 복잡도가 O(n)이므로 deque는 n이 클 때 insert와 erase 연산을 여러번 하기 적합하지 않음.</p>
<hr>
<pre><code class="language-cpp">// 4
cout &lt;&lt; &quot;DQ.size() : &quot; &lt;&lt; DQ.size() &lt;&lt; &#39;\n&#39;;
// 0
cout &lt;&lt; &quot;DQ.empty() : &quot; &lt;&lt; DQ.empty() &lt;&lt; &#39;\n&#39; &lt;&lt; &#39;\n&#39;;

// DQ = { }
DQ.clear();
// DQ = { 0 }
DQ.resize(1);
// DQ = { 0, 2, 2 }
DQ.resize(3, 2);
// DQ = { 0, 1, 2 }
DQ[1] = 1;</code></pre>
<h3 id="4-size--empty">(4) size / empty</h3>
<p>deque의 크기를 size_t 자료형으로 반환.</p>
<p>deque가 비어있는지 여부를 bool 자료형으로 반환.</p>
<h3 id="5-resize--clear">(5) resize / clear</h3>
<p>deque의 크기를 변경</p>
<p>deque의 모든 원소를 삭제</p>
<p>deque의 크기를 변경.
변경될 크기가 원래의 크기보다 작은 경우, 뒤의 원소부터 차례로 지워지고,
원래의 크기보다 큰 경우, 두 번째 인자가 없는 경우 초기값, 있는 경우는 해당 값으로 남은 칸이 채워짐.</p>
<hr>
<pre><code class="language-cpp">// 0 1 2
for (auto it = DQ.begin(); it != DQ.end(); it++) cout &lt;&lt; *it &lt;&lt; &#39; &#39;; cout &lt;&lt; &#39;\n&#39;;
// 2 1 0
for (auto it = DQ.rbegin(); it != DQ.rend(); it++) cout &lt;&lt; *it &lt;&lt; &#39; &#39;; cout &lt;&lt; &#39;\n&#39;;
// 0
cout &lt;&lt; &quot;DQ.front() : &quot; &lt;&lt; DQ.front() &lt;&lt; &#39;\n&#39;;
// 2
cout &lt;&lt; &quot;DQ.end() : &quot; &lt;&lt; DQ.back() &lt;&lt; &#39;\n&#39;;</code></pre>
<h3 id="6-begin--end">(6) begin / end</h3>
<p>deque의 첫번째 원소를 가리키는 iterator 반환</p>
<p>deque의 <strong>마지막 원소의 다음 칸</strong>을 가리키는 iterator 반환</p>
<h3 id="7-rbegin--rend">(7) rbegin / rend</h3>
<p>begin의 reserve_iterator 버전</p>
<p>end의 reserve_iterator 버전</p>
<h3 id="8-front--back">(8) front / back</h3>
<p>DQ[0]을 reference로 반환. O(1)</p>
<p>DQ[n-1]을 reference로 반환. O(1)</p>
<p>빈 vector에서 둘을 호출하는 것은 &#39;UB(Undefined Behavior)&#39;</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/935">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x07강 - 덱&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222447852688&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ deque&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 4. 큐(Queue)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-4.-%ED%81%90Queue</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-4.-%ED%81%90Queue</guid>
            <pubDate>Fri, 09 Dec 2022 06:31:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/934">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x06강 - 큐&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222447436441&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ queue&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>큐(Queue)</li>
<li>STL queue</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-큐queue">1. 큐(Queue)</h1>
<blockquote>
<ul>
<li><em><strong>큐(Queue)</strong></em>
: 한쪽 끝에서 원소를 넣고 반대쪽 끝에서 원소를 뺼 수 있는 자료구조</li>
</ul>
</blockquote>
<p>스택은 구조적으로 제일 먼저 들어간 원소가 제일 나중에 나오게 됨.
FILO(First in Last out) 자료구조.</p>
<p>큐는 먼저 들어간 원소가 제일 먼저 나오게 됨.
FIFO(First in First out) 자료구조.</p>
<h2 id="1-큐의-성질">1) 큐의 성질</h2>
<h3 id="1-원소의-추가--제거-o1">(1) 원소의 추가 / 제거 O(1)</h3>
<p>스택에서는 원소가 추가되고 제거되는 곳을 top이라고 함. 그리고 원소가 위 아래로 배치된 것으로 생각함.</p>
<p>큐에서는 추가되는 곳을 rear(뒤쪽), 제거되는 곳을 front(앞쪽)이라고 함.</p>
<h3 id="2-제일-앞--뒤의-원소-확인-o1">(2) 제일 앞 / 뒤의 원소 확인 O(1)</h3>
<h3 id="3-제일-앞--뒤가-아닌-나머지-원소들의-확인--변경이-원칙적으로-불가능">(3) 제일 앞 / 뒤가 아닌, 나머지 원소들의 확인 / 변경이 원칙적으로 불가능</h3>
<hr>
<h2 id="2-스택의-구현">2) 스택의 구현</h2>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[MX];
int head = 0, tail = 0;</code></pre>
<p><code>dat</code>배열에는 스택 원소들의 값이 들어가고, <code>head</code>는 제일 앞의 원소를 가리키고, <code>tail</code>은 제일 뒤의 원소 + 1을 가리키고 있음.
그리고 <code>dat[head]부터 dat[tail - 1]</code>까지 큐의 원소들이 있는 자리임. 그리고 큐의 크기는 <code>tail - head</code>로 계산 가능.</p>
<p>push를 하게되면 <code>tail</code>이 증가, pop을 하면 <code>head</code>가 증가.
따라서 <code>dat</code>배열에서 큐의 원소들이 있는 장소는 오른쪽으로 점점 밀림.
그러면 pop을 여러 번 진행하면, 앞쪽에 쓸모없는 공간이 계속 생기게 됨.
이를 해결하기 위해서는, 큐의 언소가 들어갈 배열을 원형으로 만드는 것.
실제로 구현할 때는 <code>head</code>나 <code>tail</code>이 다시 <code>0</code>번지로 돌아오게끔 구현.
이러한 원형 배열로 만든 큐를 &#39;<em><strong>원형 큐(Circular Queue)</strong></em>&#39;라고 함.</p>
<p>그래서 실무에서 STL말고 큐를 직접 구현을 할 때는 원형 큐를 구현하는 것이 좋음.</p>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[MX];
int head = 0, tail = 0;

void push(int x){
    dat[tail++] = x;
}

void pop(){
    head++;
}

int front(){
    return dat[head];
}

int back(){
    return dat[tail - 1];
}</code></pre>
<hr>
<h1 id="2-stl-queue">2. STL queue</h1>
<p>queue는 보통 BFS나 Flodd Fill을 할 때 쓰게 됨.
둘 다 코테 단골 문제여서 queue를 쓸 일이 아주 많을 것.</p>
<h2 id="1-생성자">1) 생성자</h2>
<pre><code class="language-cpp">1. queue&lt;int&gt; Q;</code></pre>
<p>1번은 비어있는 큐를 생성합니다.</p>
<p>(스택과 마찬가지로 queue는 내부적으로 queue 연산을 지원하는 deque, list 등을 이용해 구현되며, default값은 deque입니다.)</p>
<h2 id="2-멤버-함수">2) 멤버 함수</h2>
<pre><code class="language-cpp">queue&lt;int&gt; Q;

// Q = [ 1(front), 2, 3, 4, 5(back) ]
for(int i = 1; i &lt;= 5; i++) Q.push(i);
while(!Q.empty()){
    cout &lt;&lt; Q.size() &lt;&lt; &#39; &#39; &lt;&lt; Q.front() &lt;&lt; &#39;\n&#39;;
    Q.pop();
}</code></pre>
<h3 id="1-push--pop">(1) push / pop</h3>
<p>back()쪽 위치에 원소를 삽입. O(1)</p>
<p>front()쪽 위치에 원소를 삭제. O(1)</p>
<p>push는 emplace로 대체 가능.</p>
<p>pop의 반환값은 void이며 front()가 아님.</p>
<h3 id="2-front--back">(2) front / back</h3>
<p>가장 먼저 들어온 원소를 reference로 반환. O(1)</p>
<p>가장 마지막에 들어온 원소를 reference로 반환. O(1) (잘 사용하지 않음)</p>
<h3 id="3-size--empty">(3) size / empty()</h3>
<p>queue의 크기를 size_t 자료형으로 반환.</p>
<p>queue가 비어있는지 여부를 bool 자료형으로 반환.</p>
<h3 id="4-참고">(4) 참고</h3>
<p>std::queue에는 <code>clear</code>라는 멤버 함수가 없음.
따라서 해당 동작을 하고자 하면</p>
<pre><code class="language-cpp">while(Q.size()){ Q.pop() }</code></pre>
<p>으로 처리해야 함.</p>
<p>빈 Queue에서 pop, front, back을 호출하는 것은 &#39;UB(Undefined Behavior)&#39;이며 _<strong>런타임 에러를 발생</strong>_시킴.</p>
<p>일반적으로 큐의 원소에 접근할 때는 <code>front()</code>만을 이용하며, <code>back()</code>이 필요한 경우는 드뭄.</p>
<p>또한 deque와 달리 <code>[]</code>연산자, iterator가 없으므로 <code>front(), back()</code> 이외의 원소에는 접근할 수 없음.</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/934">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x06강 - 큐&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222447436441&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ queue&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 3. 스택(Stack)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-2.-%EC%8A%A4%ED%83%9DStack</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-2.-%EC%8A%A4%ED%83%9DStack</guid>
            <pubDate>Wed, 07 Dec 2022 06:46:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/933">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x05강 - 스택&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222446815488&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ stack&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>스택(Stack)</li>
<li>STL stack</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-스택stack">1. 스택(Stack)</h1>
<blockquote>
<ul>
<li><em><strong>스택(Stack)</strong></em>
: 한쪽 끝에서만 원소를 넣거나 뺄 수 있는 자료구조</li>
</ul>
</blockquote>
<p>스택은 구조적으로 제일 먼저 들어간 원소가 제일 나중에 나오게 됨.
FILO(First in Last out) 자료구조.</p>
<p>스택, 큐 ,덱 모두 특정 위치에서만 원소를 넣거나 뺄 수 있음.
그래서 &#39;<em><strong>Restricted Structure</strong></em>&#39;라고 부름.</p>
<h2 id="1-스택의-성질">1) 스택의 성질</h2>
<h3 id="1-원소의-추가--제거-o1">(1) 원소의 추가 / 제거 O(1)</h3>
<h3 id="2-최상단의-원소-확인-o1">(2) 최상단의 원소 확인 O(1)</h3>
<h3 id="3-최상단이-아닌-나머지-원소들의-확인--변경이-원칙적으로-불가능">(3) 최상단이 아닌, 나머지 원소들의 확인 / 변경이 원칙적으로 불가능</h3>
<hr>
<h2 id="2-스택의-구현">2) 스택의 구현</h2>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[MX];
int pos = 0;</code></pre>
<p><code>dat</code>배열에는 스택 원소들의 값이 들어가고, <code>pos</code>는 다음 원소가 추가될 때 삽입해야하는 곳을 가리키고 있음.
그리고 <code>pos</code>의 값은 곧 스택의 길이, 즉 원소의 개수를 나타냄.</p>
<pre><code class="language-cpp">const int MX = 1000005;
int dat[MX];
int pos = 0;

void push(int x){
    dat[pos++] = x;
}

void pop(){
    pos--;
}

int top(){
    return dat[pos-1];
}</code></pre>
<hr>
<h2 id="3-벡터와-스택">3) 벡터와 스택</h2>
<p>사실 벡터는 스택의 모든 기능을 대체할 수 있음.
(마지막 원소의 추가 / 제거는 <code>push_back(), pop_back()</code>. 또한 <code>[]</code>연산자를 통해 임의 위치에 접근 가능.)</p>
<p>하지만 그럼에도 불구하고 스택을 쓰는 경우가 있음.
바로, stack의 연산만을 이용하는 경우.(의미가 더 명확해짐)
하지만 벡터를 사용한다고 하더라도 성능 차이는 거의 없음.</p>
<p>(사실, <code>push, pop, top</code>연산이 정의된 자료구조는 모두 stack이라 부름(vector, string, deque, list). 하지만 c++에서 스택은 std::stack을 의미)</p>
<hr>
<h1 id="2-stl-stack">2. STL stack</h1>
<h2 id="1-생성자">1) 생성자</h2>
<pre><code class="language-cpp">1. stack&lt;int&gt; S;</code></pre>
<p>1번은 비어있는 스택을 생성합니다.</p>
<p>(스택을 내부적으로 stack의 연산을 지원하는 deque, vector 등을 이용해 구현되며, default값은 deque입니다.)</p>
<h2 id="2-멤버-함수">2) 멤버 함수</h2>
<pre><code class="language-cpp">stack&lt;int&gt; S;

// S = { 1, 2, 3, 4, 5 }
for(int i = 1; i &lt;= 5; i++) S.push(i);
while(!S.empty()){
    cout &lt;&lt; S.size() &lt;&lt; &#39; &#39; &lt;&lt; S.top() &lt;&lt; &#39;\n&#39;;
    S.pop();
}</code></pre>
<h3 id="1-push--pop">(1) push / pop</h3>
<p>마지막 위치에 원소를 삽입 / 삭제. O(1)</p>
<p>push는 emplace로 대체 가능.</p>
<p>pop의 반환값은 void이며 마지막 원소가 아님(파이썬 list의 pop()과 다름)</p>
<p>빈 stack에서 둘을 호출하는 것은 &#39;UB(Undefined Behavior)&#39;이며 _<strong>런타임 에러를 발생</strong>_시킴.</p>
<h3 id="2-top">(2) top</h3>
<p>마지막 위치의 원소를 reference로 반환. O(1)</p>
<h3 id="3-size--empty">(3) size / empty()</h3>
<p>stack의 크기를 size_t 자료형으로 반환.</p>
<p>stack이 비어있는지 여부를 bool 자료형으로 반환.</p>
<h3 id="4-참고">(4) 참고</h3>
<p>std::stack에는 <code>clear</code>라는 멤버 함수가 없음.
따라서 해당 동작을 하고자 하면</p>
<pre><code class="language-cpp">while(S.size()){ S.pop() }</code></pre>
<p>으로 처리해야 함.</p>
<p>또한 deque와 달리 <code>[]</code>연산자, iterator가 없으므로 <code>top()</code>이외의 원소에는 접근할 수 없음.</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/933">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x05강 - 스택&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222446815488&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ stack&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 2. 연결 리스트(List)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-2.-%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8List</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-2.-%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8List</guid>
            <pubDate>Mon, 05 Dec 2022 08:46:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/932">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x04강 - 연결 리스트&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222508424732&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ list&#39;</a></li>
<li>iterator(반복자)
: <a href="http://www.tcpschool.com/cpp/cpp_iterator_category">http://www.tcpschool.com/cpp/cpp_iterator_category</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>연결 리스트(List)</li>
<li>STL list</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-연결-리스트list">1. 연결 리스트(List)</h1>
<blockquote>
<ul>
<li><em><strong>연결 리스트(List)</strong></em>
: 원소들을 저장할 때, <strong>그 다음 원소가 있는 위치를 포함</strong>시키는 방식으로 저장한 자료구조</li>
</ul>
</blockquote>
<h2 id="1-연결-리스트의-성질">1) 연결 리스트의 성질</h2>
<h3 id="1-k번째-원소를-확인--변경하기-위해-ok가-필요">(1) k번째 원소를 확인 / 변경하기 위해 O(k)가 필요</h3>
<p>예를 들어, <code>3 -&gt; 13 -&gt; 72 -&gt; 5</code>의 연결 리스트에서 <code>72</code>를 찾기 위해서는, <code>3</code>을 거쳐 <code>13</code>을, <code>13</code>을 거쳐 <code>72</code>로 가야함.
이는, 배열과 다르게 원소들이 연속적으로 위치하지 않기 때문.</p>
<h3 id="2-임의의-위치에-원소를-추가--제거는-o1">(2) 임의의 위치에 원소를 추가 / 제거는 O(1)</h3>
<h3 id="3-cache-hit-rate가-낮음-대신-메모리-할당은-쉬움">(3) Cache hit rate가 낮음. 대신 메모리 할당은 쉬움</h3>
<p>메모리 상에서 데이터가 연속적으로 위치하지 않음.</p>
<p>Cache hit rate는, 캐시된 데이터를 요청할 때 해당 키 값이 메모리에 존재하여 얼마만큼의 비율로 잘 찾았는지에 대한 비율.</p>
<hr>
<h2 id="2-연결-리스트의-종류">2) 연결 리스트의 종류</h2>
<p><img src="https://velog.velcdn.com/images/wonder_land/post/1e828596-d7cf-4ef3-9ee7-a4b2dfeb1243/image.png" alt="">
<a href="https://blog.encrypted.gg/932">(출처 : &#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x04강 - 연결 리스트&#39;)</a></p>
<h3 id="1-단일-연결-리스트singly-linked-list">(1) 단일 연결 리스트(Singly Linked List)</h3>
<p>각 원소가 자신의 _<strong>다음 원소</strong>_의 주소를 들고 있는 연결 리스트</p>
<h3 id="2-이중-연결-리스트doubly-linked-list">(2) 이중 연결 리스트(Doubly Linked List)</h3>
<p>각 원소가 자신의 _<strong>다음과 이전의 원소</strong>_의 주소를 들고 있는 연결 리스트.</p>
<p>STIL의 연결 리스트에서, 컨테이너의 이름은 <code>list</code>이고, 구조는 이중 연결 리스트입니다.</p>
<h3 id="3-원형-연결-리스트circular-linked-list">(3) 원형 연결 리스트(Circular Linked List)</h3>
<p>처음과 끝이 연결된 연결 리스트.</p>
<p>이 때 연결 리스트는 단일 / 이중 연결 리스트이다.</p>
<hr>
<h2 id="3-배열과-연결-리스트">3) 배열과 연결 리스트</h2>
<p>배열과 연결 리스트는,
메모리 상에서 데이터를 놓는 방식은 다르더라도, _<strong>원소들 사이에 선후 관계가 일대일로 정의</strong>_됨</p>
<p>그래서 이 둘 자료형은, &#39;<em><strong>선형 자료구조</strong></em>&#39;라고 불림.
트리, 그래프, 해쉬 등은 &#39;비선형 자료구조&#39;라고 불림.</p>
<p>둘 다 선형 자료구조이므로, 면접에서 둘을 비교하라는 내용이 나오기도 하니 알고있어야 함!</p>
<h3 id="1-k번째-원소의-접근">(1) k번째 원소의 접근</h3>
<ul>
<li>배열 : O(1)</li>
<li>연결 리스트 : O(k)</li>
</ul>
<h3 id="2-임의의-위치에-원소-추가--제거">(2) 임의의 위치에 원소 추가 / 제거</h3>
<ul>
<li>배열 : O(N)</li>
<li>연결 리스트 : O(1)</li>
</ul>
<p>사실 엄밀히 말하면, 연결 리스트에 원소를 추가 / 제거 하는 경우,
임의 위치까지 찾아간 후에 O(1) 연산을 하는 것이므로 주의!</p>
<h3 id="3-메모리-상의-배치">(3) 메모리 상의 배치</h3>
<ul>
<li>배열 : 연속적</li>
<li>연결 리스트 : 비연속적</li>
</ul>
<h3 id="4-추가적으로-필요한-공간overhead">(4) 추가적으로 필요한 공간(Overhead)</h3>
<ul>
<li>배열 : 필요 없음</li>
<li>연결 리스트 : O(N)</li>
</ul>
<p>배열의 경우 데이터만 저장하면 되니 추가 공간 필요 없음
(굳이 따지자면, 길이 정보를 저장할 int 1개가 필요할 수 있지만 미미하므로 신경 쓸 필요 없음)</p>
<p>연결 리스트의 경우, 데이터 뿐만 아니라, 다음 / 이전 원소의 주소값을 가지고 있어야 하므로,
32비트 체제의 경우 <code>4N(32비트)</code>바이트,
64비트 체제의 경우 <code>8N(64비트)</code>바이트가 추가적으로 필요.
즉, N에 비례하는 만큼 메모리를 추가로 쓰게 됨.</p>
<p>즉, 임의의 위치에 원소를 추가하거나 제거하는 연산을 많이 해야하는 경우, 연결 리스트의 사용을 고려할 수 있음.</p>
<hr>
<h2 id="3-연결-리스트의-직접-구현">3) 연결 리스트의 직접 구현</h2>
<h3 id="1-정석적인-구현">(1) 정석적인 구현</h3>
<pre><code class="language-cpp">struct NODE{
    struct NODE *prev, *next;
    int data;
};</code></pre>
<p>코테에서는 STL list를 사용하기.
만약 STL이 사용불가하면 2번 방법을 활용하기.</p>
<p>위의 코드 경우 면접에서 손코딩을 시키거나 질문을 하는 경우가 있기 때문에 알고는 있어야 함.</p>
<h3 id="2-코테에서-stl-사용이-불가할-때">(2) 코테에서 STL 사용이 불가할 때</h3>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

const int MX = 1000005;
int dat[MX], pre[MX], nxt[MX];
int unused = 1;

void traverse(){
    int cur = nxt[0];
    while(cur != -1){
        cout &lt;&lt; dat[cur] &lt;&lt; &#39; &#39;;
        cur = nxt[cur];
    }
    cout &lt;&lt; &#39;\n\n&#39;;
}

void insert(int addr, int num) {
    // 1. Write new element
    dat[unused] = num;
    pre[unused] = addr;
    nxt[unused] = nxt[addr];

    // 2. Fix pre &amp; nxt element&#39;s information
    if(nxt[addr] != -1) pre[nxt[addr]] = unused;
    nxt[addr] = unused;

    // 3. Fix unused
    unused++;
}

void erase(int addr){
    // 1. Fix pre &amp; nxt element&#39;s information
    nxt[pre[addr]] = nxt[addr];
    if(nxt[addr] != -1) pre[nxt[addr]] = pre[addr];

    // 2. Fix unused
    unused--;
}

int main (){
    fastio;
    fill(pre, pre + MX, -1);
    fill(nxt, nxt + MX, -1);
}</code></pre>
<p>원소를 배열로 관리하고, <code>pre</code>와 <code>nxt</code>에 이전 / 다음 원소의 포인터 대신, 배열 상의 인덱스를 저장하는 방시긍로 구현한 연결 리스트.</p>
<p>메모리 누수 때문에 실무에서는 사용할 수 없지만,
구현 난이도가 낮고 시간복잡도도 동일해 코테에서는 활용 가능.</p>
<p>코드 상에서,
<code>dat[i]</code>는 i번지 원소의 값,
<code>pre[i]</code>는 i번지 원소에 대해 이전 원소의 인덱스,
<code>nxt[i]</code>는 i번지 원소에 대해 다음 원소의 인덱스.
만약 <code>pre</code>와 <code>nxt</code>의 값이 <code>-1</code>인 경우 이전 / 다음 원소는 존재하지 않는 경우임.
<code>unused</code>는 현재 사용되지 않는 인덱스, 즉 새로운 원소가 들어갈 수 있는 인덱스이고 원소가 추가된 이후에는 <code>1</code>씩 증가시킴.
특별히 <code>0</code>번지는 연결 리스트의 시작 원소로 고정되어 있음. 따라서 값이 들어가지 않고 시작점을 나타내기 위한 dummy node.
<code>0</code>번지의 <code>dat</code>값은 <code>-1</code>, <code>pre</code>는 <code>-1</code>, <code>nxt</code>는 다음 원소의 번지 인덱스를 적으면 됨.
(dummy node를 만들어야 예외 처리가 쉬워짐. 만약 원소가 없는 경우, 원소 추가 / 삭제를 생각할 때 dummy node가 있는게 쉬움)</p>
<p>만약 길이 정보가 필요하면 <code>len</code>변수를 선언하고 추가 / 제거 될때 1씩 증가 / 감소시키면 됨.</p>
<hr>
<h1 id="2-stl-list">2. STL list</h1>
<p>st::list는 Doubly Linked List로 구현되어 있습니다.
임의의 위치에 원소를 삽입 / 삭제(O(1) : 해당 원소의 위치를 알 고 있을 때만... 모르면 해당 위치로 일일이 찾아가야 함...)할 수 있지만,
<code>[]</code>연산자를 이용해 접근할 수 없으며, 처음부터 하나씩 접근해야만 합니다.</p>
<p>따라서, list는 1) 중간 위치에서 삽입, 삭제가 빈번하게 일어나고,
2) 데이터에 랜덤하게 접근하지 않는 경우가 많을 때 사용됩니다.</p>
<h2 id="1-생성자">1) 생성자</h2>
<pre><code class="language-cpp">1. list&lt;int&gt; L;
2. list&lt;int&gt; L(n); 또는 list&lt;int&gt; L(n, val);
3. list&lt;int&gt; L{1, 2, 3}; 또는 list&lt;int&gt; L = {1, 2, 3};</code></pre>
<ol>
<li>비어있는 리스트를 생성합니다.</li>
<li>n개의 원소를 갖는 리스트를 생성합니다.
후자는 n개의 원소를 val값으로 초기화합니다.</li>
<li>initializer_list를 인자로 받는 생성자</li>
</ol>
<hr>
<h2 id="2-멤버-함수">2) 멤버 함수</h2>
<pre><code class="language-cpp">// L = { 1, 2, 3 }
list&lt;int&gt; L = { 1, 2, 3 };

auto it1 = next(L.begin()); // *it1 == 2

// L = { 1, 10, 2, 3 }, *it1 == 2, *it2 == 10
auto it2 = L.insert(it1, 10);
// L = { 1, 2, 3 }, *it2 == 2
it2 = L.erase(it2);</code></pre>
<h3 id="1-push_back--push_front">(1) push_back / push_front</h3>
<p>마지막 / 시작 위치에 원소를 삽입. O(1)</p>
<p>emplace_back, emplace_front로 대체 가능.</p>
<h3 id="2-pop_back--pop_front">(2) pop_back / pop_front</h3>
<p>마지막 / 시작 위치의 원소를 삽입. O(1)</p>
<p>반환값은 void.</p>
<h3 id="3-insert--erase">(3) insert / erase</h3>
<p>매개변수로 받은 itertor의 위치의 <strong>앞</strong>에 원소를 삽입. O(1)
insert는 emplace로 대체 가능.</p>
<p>매개변수로 받은 iterator 위치의 원소를 삭제 후, <strong>원래 위치의 다음 원소의 itertator 반환</strong>. O(1)</p>
<p>이 때, erase의 인자로 전달한 iterator는 무효화(invalidated)되기 때문에 <code>*it</code>, <code>it++</code> 등으로 사용하면 UB를 발생시킴.
그래서 일반적으로, <code>it = erase(it)</code>으로 사용하여 이를 방지함.</p>
<hr>
<pre><code class="language-cpp">// L = { 1, 2, 3 }
list&lt;int&gt; L = { 1, 2, 3 };

// *** Result : 3 ***
cout &lt;&lt; L.size();

// *** Result : 1 2 ***
L.resize(2);
for(auto i : L) cout &lt;&lt; i ;

// *** Result : False ***
cout &lt;&lt; L.empty();

// *** Result : True ***
L.clear();
cout &lt;&lt; L.empty();</code></pre>
<h3 id="4-size--resize">(4) size / resize</h3>
<p>list의 크기를 size_t 자료형으로 반환.</p>
<p>list의 크기를 변경.</p>
<p>list의 크기를 변경
변경될 크기가 원래의 크기보다 작은 경우, 뒤의 원소부터 차례로 지워지고,
원래의 크기보다 큰 경우, 두 번째 인자가 없는 경우 초기값, 있는 경우는 해당 값으로 남은 칸이 채워짐.</p>
<h3 id="5-empty--clear">(5) empty / clear</h3>
<p>list가 비어있는지 여부를 bool 자료형으로 반환.</p>
<p>list의 모든 원소를 삭제</p>
<hr>
<pre><code class="language-cpp">// L = { 1, 2, 3 }
list&lt;int&gt; L = { 1, 2, 3 };

// *** Result : 1 3 ***
cout &lt;&lt; *L.begin() &lt;&lt; &#39; &#39; &lt;&lt; *L.end();

// *** Result : 3 1 ***
cout &lt;&lt; *L.begin() &lt;&lt; &#39; &#39; &lt;&lt; *L.rend();

// *** Result : 1 3 ***
cout &lt;&lt; L.front() &lt;&lt; &#39; &#39; &lt;&lt; L.back();

// *** Result : 1 2 3 4 5 ***
L = { 3, 1, 2, 4, 5 };
L.sort();
for(auto i : L) cout &lt;&lt; i &lt;&lt; &#39; &#39;;</code></pre>
<h3 id="6-begin--end">(6) begin / end</h3>
<p>list의 첫번째 원소를 가리키는 iterator 반환</p>
<p>list의 <strong>마지막 원소의 다음 칸</strong>을 가리키는 iterator 반환</p>
<h3 id="7-rbegin--rend">(7) rbegin / rend</h3>
<p>begin의 reserve_iterator 버전</p>
<p>end의 reserve_iterator 버전</p>
<h3 id="8-front--back">(8) front / back</h3>
<p>list의 첫번째 원소를 reference로 반환. O(1)</p>
<p>list의 마지막 원소를 reference로 반환. O(1)</p>
<p>빈 vector에서 둘을 호출하는 것은 &#39;UB(Undefined Behavior)&#39;</p>
<h3 id="9-sort">(9) sort</h3>
<p>list 내의 원소를 정렬. O(nlogn)
sort는 merge sort로 구현.</p>
<p>첫 번째 인자로 <code>cmp</code>를 넣어줄 수 있으며,
생략한 경우 <code>less&lt;&gt;{}</code>가 default parameter로 들어감. </p>
<p>list의 원소를 정렬할 경우, <code>std::sort</code> 대신 <code>L.sort()</code>를 사용해야 함.
(random access iterator가 없기 때문)</p>
<h3 id="10-참고">(10) 참고</h3>
<p>list의 iterator는 random access iterator가 아니라, bidirectional iterator임</p>
<p>따라서, <code>L.begin() + 3</code>과 같은 <em><strong><code>+</code> 연산을 지원하지 않음.</strong></em></p>
<p>따라서, iterator를 이동시키기 위해서는, <code>it++, it--</code>와 같은 _<strong>증감연산자를 사용</strong>_해야함.</p>
<p>이 떄 증감연산자 대신, 
_<strong><code>std::prev, std::next</code>함수</strong>_를 이용할 수 있음.
만약 여러 칸을 이동해야 하면, _<strong><code>std::advance</code></strong>_를 사용할 수도 있음.
단, <code>std::advance</code>의 시간복잡도는 O(n)임에 주의.</p>
<p>또한, list의 iterator는 iterator 간의 <code>-</code>연산을 지원하지 않아서, <code>it - v.begin()</code> 등으로 인덱스를 구할 수 없음.</p>
<p>이 떄는, O(n)의 시간복잡도를 가지는 <code>std::distance</code>를 사용
(보통 이러한 연산은 피하는게 나음)</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<h2 id="1-iterator반복자">1) iterator(반복자)</h2>
<p><a href="http://www.tcpschool.com/cpp/cpp_iterator_category">http://www.tcpschool.com/cpp/cpp_iterator_category</a></p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/932">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x04강 - 연결 리스트&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222508424732&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=1&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=1">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ list&#39;</a></li>
<li>iterator(반복자)
: <a href="http://www.tcpschool.com/cpp/cpp_iterator_category">http://www.tcpschool.com/cpp/cpp_iterator_category</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 1. 배열(Array & Vector)]]></title>
            <link>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2.-%EB%B0%B0%EC%97%B4Array-Vector</link>
            <guid>https://velog.io/@wonder_land/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2.-%EB%B0%B0%EC%97%B4Array-Vector</guid>
            <pubDate>Sat, 03 Dec 2022 12:07:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/927">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x03강 - 배열&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222441617780&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=2&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=2">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ array, vector&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>배열(Array)</li>
<li>벡터(Vector)</li>
<li>range-based for loop</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-배열array">1. 배열(Array)</h1>
<blockquote>
<ul>
<li><em><strong>배열(Array)</strong></em>
: 메모리 상에 원소를 연속하게 배치한 자료구조
ex) int arr[10]</li>
</ul>
</blockquote>
<h2 id="1-배열의-성질">1) 배열의 성질</h2>
<h3 id="1-원소의-접근-및-수정은-연산자를-통해-o1에-수행">(1) 원소의 접근 및 수정은 <code>[]</code>연산자를 통해 O(1)에 수행</h3>
<h3 id="2-다른-자료구조와-다르게-추가적으로-소모되는-메모리의-양overhead가-거의-없음">(2) 다른 자료구조와 다르게, 추가적으로 소모되는 메모리의 양(Overhead)가 거의 없음</h3>
<h3 id="3-cache-hit-rate가-높음">(3) Cache hit rate가 높음</h3>
<p>Cache hit rate는, 캐시된 데이터를 요청할 때 해당 키 값이 메모리에 존재하여 얼마만큼의 비율로 잘 찾았는지에 대한 비율.</p>
<h3 id="4-메모리-상에서-연속된-구간을-잡아야-하므로-할당에-제약">(4) 메모리 상에서 연속된 구간을 잡아야 하므로, 할당에 제약</h3>
<hr>
<h2 id="2-배열의-기능과-구현">2) 배열의 기능과 구현</h2>
<h3 id="1-임의의-위치에-있는-원소의-확인--변경--o1">(1) 임의의 위치에 있는 원소의 확인 / 변경 : O(1)</h3>
<h3 id="2-원소를-배열의-끝에-추가--o1">(2) 원소를 배열의 끝에 추가 : O(1)</h3>
<h3 id="3-마지막-원소를-제거--o1">(3) 마지막 원소를 제거 : O(1)</h3>
<h3 id="4-임의의-위치에-원소를-추가--on">(4) 임의의 위치에 원소를 추가 : O(N)</h3>
<p>만약 <code>n</code>번째 위치에 원소를 추가하게 되면, <code>n+1</code>부터 뒤의 칸들은 한 칸씩 밀림. </p>
<p>따라서, 추가하는 위치가 끝에 가까울수록 시간을 줄어들고, 앞에 가까울수록 늘어남
그러면 평균적으로 <code>N/2</code>개를 밀어야하므로 O(N)으로 표현가능함.</p>
<pre><code class="language-cpp">void insert(int idx, int num, int arr[], int&amp; len){
    for(int i = len; i &gt; idx; i--){
        arr[i] =  arr[i-1];
    }
    arr[idx] = num;
    len++;
}</code></pre>
<p>위의 코드는 insert를 구현한 코드.
배열의 맨 끝부터 하나씩 오른쪽으로 당김.</p>
<p>이 때, <code>i &gt; idx</code>에서 <code>&gt;=</code>이 되면 안됨.
만약, <code>idx</code>가 <code>0</code>일 경우, 밑의 코드에서 <code>arr[0] = arr[-1]</code>이 되기 때문.</p>
<h3 id="5-임의의-위치에-있는-원소를-제거--on">(5) 임의의 위치에 있는 원소를 제거 : O(N)</h3>
<p>4번과 마찬가지로 <code>n</code>번쨰 원소를 삭제 후 <code>n+1</code>부터 뒤 칸들을 한 칸씩 앞당겨야 함.
그러므로 O(N)</p>
<p>만약 제거 후 당기지 않으면 어떡하나?
-&gt; 메모리 상에서 원소가 연속적으로 존재하지 않으므로 배열이라고 볼 수 없으며, <code>k</code>번쨰 원소를 O(1)에 찾을 수 없게 됨.</p>
<pre><code class="language-cpp">void erase(int idx, int arr[], int&amp; len){
    for(int i = idx; i &lt; len; i++){
        arr[i] = arr[i+1];
    }
    len--;
}</code></pre>
<p>위의 코드는 erase를 구현한 코드.
4번과 마찬가지로 배열의 맨 끝부터 하나씩 오른쪽으로 당김.</p>
<hr>
<h2 id="3-사용-팁">3) 사용 팁</h2>
<h3 id="1-원소-전체를-특정값으로-초기화">(1) 원소 전체를 특정값으로 초기화</h3>
<h4 id="11-memset함수---비추천">(1.1) <code>memset</code>함수 -&gt; 비추천!!!</h4>
<pre><code class="language-cpp">int a[21];
int b[21][21];

memset(a, 0, sizeof a);
memset(b, 0, sizeof b);</code></pre>
<p>cstring 헤더의 <code>memset</code>함수를 이용.</p>
<p>하지만 실수할 여지가 많음.
<code>0</code>이나 <code>-1</code>외의 값을 넣으면 오작동.
2차원 이상의 배열을 인자로 넘겨 그곳에서 memset을 사용하면 실수할 여지가 있음.</p>
<h4 id="12-for문-이용---무난함">(1.2) for문 이용 -&gt; 무난함</h4>
<pre><code class="language-cpp">int a[21];
int b[21][21];

for(int i = 0; i &lt; 21; i++){
    a[i] = 0;
}
for(int i = 0; i &lt; 21; i++){
    for(int j = 0; j&lt;21; j++){
        b[i][j] = 0;
    }
}</code></pre>
<p>for문을 이용해 원소 하나하나를 수정하는 방식.</p>
<p>투박하지만 실수할 여지가 없으므로 무난함.</p>
<h4 id="13-fill함수---추천">(1.3) <code>fill</code>함수 -&gt; 추천!!!</h4>
<pre><code class="language-cpp">int a[21];
int b[21][21];

fill(a, a+21, 0);
for(int i = 0; i &lt; 21; i++){
    fill(b[i], b[i]+21, 0);
}</code></pre>
<p>algorithm헤더의 <code>fill</code>함수를 이용</p>
<p><code>fill</code>함수는 실수할 여지도 별로 없고, 코드도 짧으므로 추천!</p>
<hr>
<h1 id="2-벡터vector">2. 벡터(Vector)</h1>
<blockquote>
<ul>
<li>벡터(Vector)
: 가변 길이의 배열</li>
</ul>
</blockquote>
<pre><code class="language-cpp">vector&lt;Type&gt; name;    // 1차원
vector&lt;vector&lt;Type&gt;&gt; name;    //2차원</code></pre>
<h2 id="1-생성자">1) 생성자</h2>
<pre><code class="language-cpp">1. vector&lt;int&gt; v;
2. vector&lt;int&gt; v(n); 또는 vector&lt;int&gt; v(n, val);
3. vector&lt;int&gt; v(w); 또는 vector&lt;int&gt; v = w; //단, w는 vector&lt;int&gt;
4. vector&lt;int&gt; v(w.begin(), w.end());
5. vector&lt;int&gt; v{1,2,3}; 또는 vector&lt;int&gt; v = {1,2,3};</code></pre>
<ol>
<li>비어있는 벡터를 생성합니다.</li>
<li>n개의 원소를 갖는 벡터를 생성합니다.
후자는 n개의 원소를 val값으로 초기화합니다.</li>
<li>copy constructor로, 다른 vector와 동일한 벡터를 복사 생성할 때 사용됩니다.</li>
<li>다른 컨테이너의 iterator를 받은 후, 순회하며 vector에 해당 범위의 원소를 채워주는 생성자</li>
<li>initializer_list를 인자로 받는 생성자</li>
</ol>
<hr>
<h2 id="2-멤버-함수">2) 멤버 함수</h2>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

int main(void) {
    fastio;
    // v = { 0, 0, 0 }
    // t = { 5, 5 }
    // w = { { -1, -1, -1 }, { -1, -1, -1 }}
    vector&lt;int&gt; v(3), t(2, 5);
    vector&lt;vector&lt;int&gt;&gt; w(2, vector&lt;int&gt;(3, -1));

    v.push_back(1); // v = { 0, 0, 0, 1 }
    v.pop_back();    // v = { 0, 0, 0 }

    // insert 2 at v[0], v = { 2, 0, 0, 0 }
    v.insert(v.begin(), 2);
    // insert { 4, 4, 4 } at v[2], v = { 2, 0, 4, 4, 4, 0, 0, 0 }
    v.insert(v.begin() + 2, 3, 4);
    // insert [t.begin(), t.end()] at v.begin(), v = { 5, 5, 2, 0, 4, 4, 4, 0, 0 }
    v.insert(v.begin(), t.begin(), t.end());

    // erase v[4], v = { 5, 5, 0, 4, 4, 4, 0, 0 }
    v.erase(v.begin() + 2);
    // erase [v[0], v[6]), v= { 0, 0 }
    v.erase(v.begin(), v.begin()+6);

    cout &lt;&lt; &quot;v.size() : &quot; &lt;&lt; v.size() &lt;&lt; &#39;\n&#39;; // 2
    cout &lt;&lt; &quot;v.empty() : &quot; &lt;&lt; v.empty() &lt;&lt; &#39;\n&#39; &lt;&lt; &#39;\n&#39;; // 0

    v.clear(); // v = { }
    v.resize(1); // v = { 0 } 
    v.resize(3, 2); // v = { 0, 2, 2 }
    v[1] = 1; // v = { 0, 1, 2 }
}</code></pre>
<h3 id="1-push_back--pop_back">(1) push_back / pop_back</h3>
<p>마지막 위치에 원소를 삽입 / 제거. O(1)</p>
<h3 id="2-insert--erase">(2) insert / erase</h3>
<p>임의의 위치에 원소를 삽입 / 제거. O(n)</p>
<p>O(n)이므로, n이 클 떄 해당 함수들을 여러 번 적용하기는 적합하지 않음.</p>
<hr>
<h3 id="3-size--resize">(3) size / resize</h3>
<p>vector의 크기를 size_t 자료형으로 반한
(return값은 unsigend int이므로, <code>v.size() - 1</code>의 연산 시 언더플로우 위험 주의)</p>
<p>vector의 크기를 변경
변경될 크기가 원래의 크기보다 작은 경우, 뒤의 원소부터 차례로 지워지고,
원래의 크기보다 큰 경우, 두 번째 인자가 없는 경우 초기값, 있는 경우는 해당 값으로 남은 칸이 채워짐.</p>
<h3 id="4-capacity--reserve">(4) capacity / reserve</h3>
<p>vector의 capacity를 size_t 자료형으로 반한
(return값은 unsigend int이므로, <code>v.capacity() - 1</code>의 연산 시 언더플로우 위험 주의)</p>
<p>vector의 capacity를 변경
<strong>reserve를 잘 이용하면 메모리를 절약</strong>할 수 있음.
: 만약 size와 capacity가 각각 6일 때, 하나의 원소를 추가해보자.
size는 7이 되지만, size는 capacity보다 클 수 없기 때문에 capacity가 늘어난다.
이 때 capacity는 자동적으로 1.5 ~ 2배 정도 늘어난다. 그러면 12가 될 것이다.
그러나 남은 공간 5(12 - 7)은 사실 필요가 없는 공간이다.
따라서 reserve(7)로 해주면 size도 만족하고, 용량도 절약할 수 있다.</p>
<p>(capacity는 vector의 원소들을 담을 수 있는 메모리가 할당되어 있는 <strong>공간의 용량</strong>, size는 실제 유효 <strong>원소들의 개수</strong>)</p>
<h3 id="5-empty">(5) empty</h3>
<p>vector가 비어져 있는지 bool자료형으로 반환</p>
<h3 id="6-clear">(6) clear</h3>
<p>vector의 모든 원소를 삭제</p>
<hr>
<h3 id="7-begin--end">(7) begin / end</h3>
<p>vector의 첫번째 원소를 가리키는 iterator 반환</p>
<p>vector의 <strong>마지막 원소의 다음 칸</strong>을 가리키는 iterator 반환</p>
<p>iterator(반복자)는 포인터처럼, 자료구조의 원소를 가리키는 자료형.</p>
<h3 id="8-rbegin--rend">(8) rbegin / rend</h3>
<p>begin의 reserve_iterator 버전</p>
<p>end의 reserve_iterator 버전</p>
<h3 id="9-front--back">(9) front / back</h3>
<p>v[0]을 reference로 반환</p>
<p>v[n-1]을 reference로 반환</p>
<p>빈 vector에서 둘을 호출하는 것은 &#39;UB(Undefined Behavior)&#39;</p>
<hr>
<h1 id="3-range-based-for-loop">3. range-based for loop</h1>
<pre><code class="language-cpp">vector&lt;int&gt; v = {1,2,3,4,5,6};

// 1. ranged-based for loop (since C++11)
for(int e : v) { cout &lt;&lt; e &lt;&lt; &#39; &#39;; }
for(int&amp; e: v) { cout &lt;&lt; e &lt;&lt; &#39; &#39;; }

// 2. not bad
for(int i = 0; i &lt; v.size(); i++) { cout &lt;&lt; v[i] &lt;&lt; &#39; &#39;; }

// 3. **Wrong**
for(int i = 0; i &lt;= v.size() - 1; i++) { cout &lt;&lt; v[i] &lt;&lt; &#39; &#39;; }
}</code></pre>
<p>1번의 방법은 C++11부터 적용가능한 range-based for loop
위의 방법은 <code>v</code>의 각 원소의 <strong><em>복사값</em></strong>이 <code>e</code>에 들어감. 따라서 for문 내에서 <code>e</code>를 바꿔도 원본 <code>v</code>에는 아무런 영향이 없음
그러나 아래의 방법은 <code>v</code>의 각 원소의 _<strong>참조</strong>_를 통해 <code>e</code>에 들어감. 따라서 for문 내에서 <code>e</code>를 바꾸면 원본 <code>v</code>의 값이 바뀜.</p>
<p>이 방법은 나중에 배울 list, map, set에도 지원.</p>
<p>만약 위 방법이 익숙치 않으면, 2번 3번을 사용해도 됨.</p>
<p>그러나 3번처럼 사용하면 안됨.
vector의 size는 unsigned int나 unsigned long long을 반환함
만약 <code>v</code>가 빈 벡터일 경우, unsigned int 0에서 1을 뺴는 것이므로, -1이 아닌 4294967295이 됨. unsigned int overflow가 발생함.(런타임에러)</p>
<hr>
<h1 id="4-qa">4. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="5-마치며">5. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/927">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x03강 - 배열&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222441617780&amp;categoryNo=82&amp;parentCategoryNo=0&amp;viewDate=&amp;currentPage=2&amp;postListTopCurrentPage=1&amp;from=postList&amp;userTopListOpen=true&amp;userTopListCount=30&amp;userTopListManageOpen=false&amp;userTopListCurrentPage=2">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 02. STL 자료구조 _ array, vector&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 1. 기초 코드 작성 요령]]></title>
            <link>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-1.-%EA%B8%B0%EC%B4%88-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%9A%94%EB%A0%B9</link>
            <guid>https://velog.io/@wonder_land/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-1.-%EA%B8%B0%EC%B4%88-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EC%9A%94%EB%A0%B9</guid>
            <pubDate>Sat, 03 Dec 2022 06:19:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/922">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x01강 - 기초 코드 작성 요령 I&#39;</a>
: <a href="https://blog.encrypted.gg/923">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x01강 - 기초 코드 작성 요령 II&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440262604&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ cin, cout&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440894006&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ stringstream&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440944366&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ FastIO&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>시간, 공간복잡도</li>
<li>정수 자료형</li>
<li>실수 자료형</li>
<li>STL과 함수 인자</li>
<li>표준 입출력</li>
<li>FastIO</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-시간∙공간복잡도">1. 시간∙공간복잡도</h1>
<h2 id="1-시간복잡도">1) 시간복잡도</h2>
<blockquote>
</blockquote>
<ul>
<li><p><em><strong>시간복잡도(Time Complexity)</strong></em>
: 입력의 크기와 문제를 해결하는데 걸리는 시간의 상관관계</p>
<br></li>
<li><p><em><strong>빅오표기법(Big-O Notation)</strong></em>
: 주어진 식을 값이 가장 큰 대표항만 남겨서 나타내는 방법</p>
</li>
<li><p>컴퓨터는 1초에 대략 3~5억개의 연산 처리 가능</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wonder_land/post/69ff6953-ad35-46fb-ac46-ed8d66ee4e36/image.png" alt=""> (출처: <a href="https://blog.encrypted.gg/922">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x01강 - 기초 코드 작성 요령 I&#39;</a>)</p>
<ul>
<li><p>O(1) : 상수 시간
O(lg N) : 로그 시간
O(N) : 선형 시간
lgN이나 N의 거듭제곱끼리의 곱 : 다항 시간
O(2ⁿ) : 지수 시간</p>
</li>
<li><p>지수 시간은 N이 <code>25</code>이하 정도로 굉장히 작은게 아니면 시간 제한을 통과하기 힘듦
팩토리얼은 지수 시간보다 훨씬 가파르기 때문에, <code>11</code>이하 정도로 굉장히 작은게 아니면 시간 제한을 통과하기 힘듦</p>
</li>
</ul>
<p>+
|N의 크기|허용 시간복잡도|
|:-----:|:------------:|
|N ≤ 11|O(N!)|
|N ≤ 25|O(2ⁿ)|
|N ≤ 100|O(N⁴)|
|N ≤ 500|O(N³)|
|N ≤ 3,000|O(N²lgN)|
|N ≤ 5,000|O(N²)|
|N ≤ 1,000,000|O(NlgN)|
|N ≤ 10,000,000|O(lgN), O(1)|</p>
<blockquote>
</blockquote>
<p><em><strong>문제 1.</strong></em>
N 이하의 자연수 중에서 3의 배수이거나 5의 배수인 수를 모두 합한 값을 반환하는 함수 func1(int N)을 작성하라. N은 5만 이하의 자연수이다.</p>
<pre><code class="language-cpp">int func1(int N){
    int sum = 3*(N/3 * (N/3 + 1))/2 +
              5*(N/5 * (N/5 + 1))/2 -
              15*(N/15 * (N/15 + 1))/2;
    return sum;
}</code></pre>
<p>원래는 1부터 N까지 순회하는 O(N)으로 코드를 작성.
그런데 본문에 O(1)로 할 수 있다는 내용을 참고하여 위 코드로 작성함.
<code>(3의 배수의 합) + (5의 배수의 합) - (15의 배수의 합)</code>을 이용</p>
<blockquote>
</blockquote>
<p><em><strong>문제 2.</strong></em>
주어진 길이 N의 int 배열 arr에서 합이 100인 서로 다른 위치의 두 원소가 존재하면 1을, 존재하지 않으면 0을 반환하는 함수 func2(int arr[], int N)을 작성하라. arr의 각 수는 0이상 100 이하이고 N은 100이하이다.</p>
<pre><code class="language-cpp">int func2(int n){
    for(int i = 0; i &lt; N; i++){
        for(int j = i+1; j &lt; N; j++){
            if(arr[i] + arr[j] == 100){
                return i;
             }
         }
     }
    return sum;
}</code></pre>
<blockquote>
</blockquote>
<p><em><strong>문제 3.</strong></em>
N이 제곱수이면 1을 반환하고, 제곱수가 아니면 0을 반환하는 함수 func3(int N)을 작성하라. N은 10억 이하의 자연수이다.</p>
<pre><code class="language-cpp">int func3(int N){
    for(int i = 0; i&lt;= N; i++){
        if(i*i == N) return 1;
    }
    return 0;
}</code></pre>
<blockquote>
</blockquote>
<p><em><strong>문제 4.</strong></em>
N이하의 수 중에서 가장 큰 2의 거듭제곱수를 반환하는 함수 func4(int N)을 작성하라. N은 10억 이하의 자연수이다.</p>
<pre><code class="language-cpp">int func4(int N){
    int i = 1;
    while(2 * i &lt;= N) { i *= 2;}
    return i;
}</code></pre>
<hr>
<h2 id="2-공간복잡도">2) 공간복잡도</h2>
<blockquote>
</blockquote>
<ul>
<li><p><em><strong>공간복잡도(Space Complexity)</strong></em>
: 입력의 크기와 문제를 해결하는데 필요한 공간의 상관관계</p>
</li>
<li><p>512MB = 1.2억개의 int</p>
</li>
<li><p>보통 코딩테스트의 경우, 공간복잡도의 문제보다는, 시간복잡도 떄문에 문제를 많이 틀림.</p>
</li>
</ul>
<hr>
<h1 id="2-정수-자료형">2. 정수 자료형</h1>
<p>+
|자료형|범위|
|-----|----|
|unsigned char|0<del>2^8-1 (255)|
|char|-2^7 ~ 2^7-1 (-128</del>127)|
|short|2^15-1(32,767)|
|int|-2^31 ~ 2^31-1 (-21억 ~ 21억 사이)|
|long long|-2^63 ~ 2^63-1|</p>
<ul>
<li>Integer Overflow
: 정수형의 자료형으로 표현할 수 있는 수의 범위를 넘어갈 때 발생하는 오류</li>
</ul>
<hr>
<h1 id="3-실수-자료형">3. 실수 자료형</h1>
<ul>
<li><p>실수의 저장/연산 과정에서 _<strong>반드시 오차가 발생</strong>_할 수 밖에 없다. 
(float는 상대 오차 10^(-6)까지, double은 10^(-15)까지 안전. 즉, 참값이 <code>1</code>인 경우, <code>1 - 10^(-15)부터 1+10^(-15)</code>사이의 값을 가짐)
: 실수자료형은 오차가 필연적으로 발생하므로, 문제에서 절대/상대 오차를 허용한다는 단서를 줌. 없으면 정수에서 해결할 수 있는 문제.</p>
</li>
<li><p>두 자료형의 차이가 크기 떄문에, 실수 자료형을 사용해야할 경우 float 대신, _<strong>double</strong>_형을 써야함!</p>
</li>
<li><p>double에 _<strong>long long 범위의 정수</strong>_를 함부로 담으면 안된다.(int는 상관 없음)
: double은 유효 숫자가 15자리인데, long long은 최대 19자리니까 <code>1</code>과 <code>1 + 10^18</code>을 구분할 수 없어, 같은 값으로 저장.</p>
</li>
<li><p>실수를 비교할 때는 _<strong>등호</strong>_를 사용하면 안된다.
: <code>0.1 + 0.1 + 0.1 != 0.3</code></p>
</li>
</ul>
<hr>
<h1 id="4-stl과-함수-인자">4. STL과 함수 인자</h1>
<ul>
<li>함수인자로 숫자/구조체/STL은 값의 복사가 일어나고, 배열은 주소값을 이용</li>
</ul>
<p>+</p>
<pre><code class="language-cpp">bool cmp1(vector&lt;int&gt; v1, vector&lt;int&gt; v2, int idx){
    return v1[idx] &gt; v2[idx];
}</code></pre>
<p>: 해당 코드의 시간복잡도는 O(1)이 아닌, O(N). <code>v1</code>과 <code>v2</code>를 인자로 이용하여 복사본을 만드는 비용이 추가되어야 함.</p>
<p>+</p>
<pre><code class="language-cpp">bool cmp2(vector&lt;int&gt;&amp; v1, vector&lt;int&gt;&amp; v2, int idx){
    return v1[idx] &gt; v2[idx];
}</code></pre>
<p>: 해당 코드는 인자를 reference로 사용. 따라서 호출될 때 복사본을 만들지 않고, 참조 대상의 주소 정보만 넘기므로 시간복잡도는 O(1).</p>
<hr>
<h1 id="5-표준-입출력">5. 표준 입출력</h1>
<ul>
<li><p>C++에는 <code>cin</code>, <code>cout</code>함수가 있음.</p>
</li>
<li><p><code>cout</code>은 공백을 포함한 문자열을 받을 때 사용하기 힘듦
: 줄바꿈(<code>\n</code>)이 나오기 전까지 입력을 받는다고 명시하기
: <code>gets</code>함수 사용하기. 그러나 보안상의 이유로 제거됨
: _<strong><code>getline</code></strong>_을 사용. 대신 type이 C++ string이어야 함.</p>
</li>
<li><p><strong><code>ios::sync_with_stdio(0)</code>, <code>cin.tie(0)</code></strong>을 입력해야만, 시간초과를 막을 수 있음.</p>
</li>
<li><p><code>ios::sync_with_stdio(0)</code>
: 기본적으로 C stream(<code>scanf</code>, <code>printf</code>)와 C++ stream(<code>cin</code>, <code>cout</code>)은 분리되어 있음. 하지만 코드의 출력을 위해 두 stream은 동기화되어 있음.
그러나 만약 두 stream을 동시에 쓰지 않고 하나의 stream만을 사용한다면 굳이 동기화가 필요 없음.
(참고로, VS 2017/2019에서는 해당 명령어를 무시하고 무조건 동기화 유지. 하지만 채점 서버는 gcc이므로 차이가 있음)</p>
</li>
<li><p><code>cin.tie(0)</code>
: <code>cin</code>명령어를 수행하기 전에 <code>cout</code> 버퍼를 비우지 않도록 하는 명령어</p>
</li>
<li><p><em><strong><code>endl</code> 사용하지 않기</strong></em>
: <code>endl</code>은 개행문자(<code>\n</code>)를 출력하고 출력 버퍼를 비우는 명령
: 어차피 judge는 프로그램이 종료될 때 출력이 어떻게 생겼는지로 채점하기 때문에 중간에 버퍼를 비울 필요가 없음</p>
</li>
<li><p><em><strong>string stream</strong></em>(<strong>i</strong>stringstream + <strong>o</strong>stringstream)을 이용하면, 문자열로부터 입력을 받아올 수 있음. 또한 출력값을 문자열로 저장할 수 있음
: istringstream형 변수를 선언한 후, 생성자 또는 <code>str</code> 함수를 이용해 스트림으로 이용할 문자열 이용</p>
</li>
</ul>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

int main(){
    fastio;
    istringstream in_1(&quot;123 456 789&quot;);
    int n;
    while (in_1 &gt;&gt; n) cout &lt;&lt; n &lt;&lt; &#39;\n&#39;;

    istringstream in_2;
    string s = &quot;hi my name is jinhan&quot;, t;
    in_2.str(s);
    cout &lt;&lt; in_2.str() &lt;&lt; &#39;\n&#39;;
    while (in_2 &gt;&gt; t) cout &lt;&lt; t &lt;&lt; &#39;\n&#39;;
}</code></pre>
<p>: ostringstream형 변수를 선언한 뒤 <code>cout</code>처럼 사용</p>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#define fastio cin.tie(0)-&gt;sync_with_stdio(0)
using namespace std;

int main(){
    fastio;
    ostringstream out;
    out &lt;&lt; &quot;Hello World!&quot; &lt;&lt; &quot;\n&quot;;
    out &lt;&lt; setw(5) &lt;&lt; setfill(&#39;0&#39;) &lt;&lt; 123 &lt;&lt; &quot;\n&quot;;
    out &lt;&lt; fixed &lt;&lt; setprecision(10) &lt;&lt; 123.456789 &lt;&lt; &quot;\n&quot;;
    string s = out.str();
    cout &lt;&lt; s &lt;&lt; &quot;\n&quot;;
}</code></pre>
<hr>
<h1 id="6-fastio">6. FastIO</h1>
<ul>
<li><p><code>cin, cout</code>은 <code>scanf, printf</code>보다 빠르지만 그래도 느림
: 1) 입력, 출력 외에 다른 기능을 제공하기 위해 추가적 연산 수행. 2) 일반적인 경우의 입력을 위해 작성되었기 때문에 모든 case를 고려함(ex <code>0</code> 이상의 정수가 입력될 때 <code>-</code>를 확인). 3) 입출력 버퍼의 크기가 작음
: 1, 2번은 어쩔 수 없지만, _<strong>3번은 개선</strong>_할 수 있음</p>
</li>
<li><p>3번은 방법은, 더 큰 <code>char[] 버퍼</code>에 입∙출력값을 한 번에 많이 읽고 보내면 됨.
: 이 때 char 배열에 보내고 읽을 떄 <code>read, write</code>와 같은 Low-Level I/O 함수가 사용됨
: 하지만, <code>cin, cout</code>과 같이 다양한 자료형을 처리하는 기능은 결여. 따라서 직접 파싱하면서 자료형을 변환해야 함(밑의 코드는 이 과정을 구현한 코드. 따라서 코드 최상단에 복붙하여 사용하면, <code>cin, cout</code>이 <code>mmap, wrtie</code>로 구현된 빠른 입출력 함수로 대체)</p>
<pre><code class="language-cpp">#include &lt;bits/stdc++.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;unistd.h&gt;
using namespace std;
</code></pre>
</li>
</ul>
<p>/////////////////////////////////////////////////////////////////////////////////////////////
/*</p>
<ul>
<li>Author : jinhan814</li>
<li>Date : 2021-05-06</li>
<li>Source : <a href="https://blog.naver.com/jinhan814/222266396476">https://blog.naver.com/jinhan814/222266396476</a></li>
<li>Description : FastIO implementation for cin, cout. (mmap ver.)</li>
<li>/
constexpr int SZ = 1 &lt;&lt; 20;</li>
</ul>
<p>class INPUT {
private:
    char* p;
    bool <strong>END_FLAG</strong>, <strong>GETLINE_FLAG</strong>;
public:
    explicit operator bool() { return !<strong>END_FLAG</strong>; }
    INPUT() {
        struct stat st; fstat(0, &amp;st);
        p = (char*)mmap(0, st.st_size, PROT_READ, MAP_SHARED, 0, 0);
    }
    bool IsBlank(char c) { return c == &#39; &#39; || c == &#39;\n&#39;; }
    bool IsEnd(char c) { return c == &#39;\0&#39;; }
    char <em>ReadChar() { return *p++; }
    char ReadChar() {
        char ret = <em>ReadChar();
        for (; IsBlank(ret); ret = _ReadChar());
        return ret;
    }
    template<typename T> T ReadInt() {
        T ret = 0; char cur = _ReadChar(); bool flag = 0;
        for (; IsBlank(cur); cur = _ReadChar());
        if (cur == &#39;-&#39;) flag = 1, cur = _ReadChar();
        for (; !IsBlank(cur) &amp;&amp; !IsEnd(cur); cur = _ReadChar()) ret = 10 * ret + (cur &amp; 15);
        if (IsEnd(cur)) __END_FLAG</em></em> = 1;
        return flag ? -ret : ret;
    }
    string ReadString() {
        string ret; char cur = <em>ReadChar();
        for (; IsBlank(cur); cur = <em>ReadChar());
        for (; !IsBlank(cur) &amp;&amp; !IsEnd(cur); cur = _ReadChar()) ret.push_back(cur);
        if (IsEnd(cur)) __END_FLAG</em></em> = 1;
        return ret;
    }
    double ReadDouble() {
        string ret = ReadString();
        return stod(ret);
    }
    string getline() {
        string ret; char cur = <em>ReadChar();
        for (; cur != &#39;\n&#39; &amp;&amp; !IsEnd(cur); cur = <em>ReadChar()) ret.push_back(cur);
        if (__GETLINE_FLAG</em></em>) <strong>END_FLAG</strong> = 1;
        if (IsEnd(cur)) <strong>GETLINE_FLAG</strong> = 1;
        return ret;
    }
    friend INPUT&amp; getline(INPUT&amp; in, string&amp; s) { s = in.getline(); return in; }
} _in;</p>
<p>class OUTPUT {
private:
    char write_buf[SZ];
    int write_idx;
public:
    ~OUTPUT() { Flush(); }
    explicit operator bool() { return 1; }
    void Flush() {
        write(1, write_buf, write_idx);
        write_idx = 0;
    }
    void WriteChar(char c) {
        if (write_idx == SZ) Flush();
        write_buf[write_idx++] = c;
    }
    template<typename T> int GetSize(T n) {
        int ret = 1;
        for (n = n &gt;= 0 ? n : -n; n &gt;= 10; n /= 10) ret++;
        return ret;
    }
    template<typename T> void WriteInt(T n) {
        int sz = GetSize(n);
        if (write_idx + sz &gt;= SZ) Flush();
        if (n &lt; 0) write_buf[write_idx++] = &#39;-&#39;, n = -n;
        for (int i = sz; i --&gt; 0; n /= 10) write_buf[write_idx + i] = n % 10 | 48;
        write_idx += sz;
    }
    void WriteString(string s) { for (auto&amp; c : s) WriteChar(c); }
    void WriteDouble(double d) { WriteString(to_string(d)); }
} _out;</p>
<p>/* operators <em>/
INPUT&amp; operator&gt;&gt; (INPUT&amp; in, char&amp; i) { i = in.ReadChar(); return in; }
INPUT&amp; operator&gt;&gt; (INPUT&amp; in, string&amp; i) { i = in.ReadString(); return in; }
template&lt;typename T, typename std::enable_if_t&lt;is_arithmetic_v<T>&gt;</em> = nullptr&gt;
INPUT&amp; operator&gt;&gt; (INPUT&amp; in, T&amp; i) {
    if constexpr (is_floating_point_v<T>) i = in.ReadDouble();
    else if constexpr (is_integral_v<T>) i = in.ReadInt<T>(); return in; }</p>
<p>OUTPUT&amp; operator&lt;&lt; (OUTPUT&amp; out, char i) { out.WriteChar(i); return out; }
OUTPUT&amp; operator&lt;&lt; (OUTPUT&amp; out, string i) { out.WriteString(i); return out; }
template&lt;typename T, typename std::enable_if_t&lt;is_arithmetic_v<T>&gt;* = nullptr&gt;
OUTPUT&amp; operator&lt;&lt; (OUTPUT&amp; out, T i) {
    if constexpr (is_floating_point_v<T>) out.WriteDouble(i);
    else if constexpr (is_integral_v<T>) out.WriteInt<T>(i); return out; }</p>
<p>/* macros */
#define fastio 1
#define cin _in
#define cout _out
#define istream INPUT
#define ostream OUTPUT
/////////////////////////////////////////////////////////////////////////////////////////////</p>
<p>```</p>
<hr>
<h1 id="7-qa">7. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="8-마치며">8. 마치며</h1>
<p>이번에는 자료구조&amp;알고리즘 입니다.</p>
<p>사실 책을 사야하나 아님 온라인 강의를 사서 들어봐야 하나 고민하고 있었는데...</p>
<p>정말 좋은 블로그를 발견했습니다.</p>
<ol>
<li><a href="https://blog.encrypted.gg/">&#39;바킹독&#39;님 블로그</a></li>
<li><a href="https://blog.naver.com/PostList.naver?blogId=jinhan814">&#39;Jinhan&#39;s Note&#39;님 블로그</a></li>
</ol>
<p>입니다.</p>
<p>이 두 분의 글들을 참고하며 알고리즘 공부를 해보려고 합니다.</p>
<p>두 분의 강의는 정말 자세하고 저같은 초보자가 이해하기 큰 무리 없이 정말 섬세하게 구성되어 있습니다. (감동ㅠㅠ)
그리고 각 강의마다 백준 연습문제들도 직접 선정해서 제공해주고 있습니다.
그리고 다 무료!!!!!!!!!!!!!!!!!!!!!!!!!!
진짜 들숨에 무한한 건강과 날숨에 엄청난 재력을 얻으시길...(매일 밤마다 기도할게용😉) </p>
<p>두 분의 커리큘럼(?)도 비슷하고 워낙 유명하신 분들이라 신뢰감이 뿜뿜하네요😎</p>
<p>또 열심히 해보겠습니다!!</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://blog.encrypted.gg/922">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x01강 - 기초 코드 작성 요령 I&#39;</a>
: <a href="https://blog.encrypted.gg/923">&#39;바킹독&#39;님의 블로그 중 &#39;[실전 알고리즘] 0x01강 - 기초 코드 작성 요령 II&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440262604&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ cin, cout&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440894006&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ stringstream&#39;</a>
: <a href="https://blog.naver.com/PostView.naver?blogId=jinhan814&amp;logNo=222440944366&amp;parentCategoryNo=&amp;categoryNo=82&amp;viewDate=&amp;isShowPopularPosts=false&amp;from=postList">&#39;Jinhan&#39;s Note&#39; 중 &#39;Theme 01. 입출력 _ FastIO&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[하고 싶은 말] 4. 드림핵(Dreamhack) 시스템 해킹 첫 번째 로드맵 완료!]]></title>
            <link>https://velog.io/@wonder_land/%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%9D%80-%EB%A7%90-4.-%EB%93%9C%EB%A6%BC%ED%95%B5Dreamhack-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%B4%ED%82%B9-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%A1%9C%EB%93%9C%EB%A7%B5-%EC%99%84%EB%A3%8C</link>
            <guid>https://velog.io/@wonder_land/%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%9D%80-%EB%A7%90-4.-%EB%93%9C%EB%A6%BC%ED%95%B5Dreamhack-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%B4%ED%82%B9-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%A1%9C%EB%93%9C%EB%A7%B5-%EC%99%84%EB%A3%8C</guid>
            <pubDate>Thu, 24 Nov 2022 06:19:47 GMT</pubDate>
            <description><![CDATA[<p>드디어 드림핵의 시스템 해킹 첫번째 로드맵을 완주했습니다🎉</p>
<p>첫번째 강의을 시작한게 22/10/18이네요. 두달 조금 넘게 공부했습니다.</p>
<p>사실 중간중간 놀고 쉬고 한 거 생각하면, 두 달을 안채우고도 마무리 할 수 있었을텐데....
뭐 지나갔으니 어쩔 수 없죠😂</p>
<p>비교적 쉽고 기초적인 내용으로 구성된 첫 번쨰 로드맵이었지만, 공부하는 동안 얼마나 힘들었는지 모릅니다...
정말 구글링하면서 짜증도 나고 욕도 하고(ㅎㅎ...) 후회도 하고 좌절도 하고 그랬네요..</p>
<p>사실 로드맵을 완주한 지금도 모든 내용이 완전하게 숙지된 것은 아닙니다.
아마 &quot;아 이런 것들이 있구나!&quot; 정도죠.</p>
<p>그래도 뒤를 돌아보면, 미약하지만 발전이 눈에 보이기도 합니다.
정말 아무것도 몰랐던 해킹에 대해, 어떻게 공부를 해야하는지 조차 몰랐던 해킹에 대해,
나름 조금씩이나마 파악하고 공부하고 있는 중이라는 것은 저한텐 엄청난 발전이라고 생각합니다.
(그렇게나마 생각해야 안 우울할 거 같아요😊)</p>
<p>_<strong><a href="https://dreamhack.io/">드림핵</a></strong>_이라는 사이트를 몰랐다면 정말 여기까지 할 수 없었을겁니다.
지금까지 시작도 못하고 있을 수도 있겠네요.
드림핵을 만들어준 개발자분들에게 정말 절이라도 하고 싶습니다🐶
그리고 이 사이트를 발견하고 공부를 시작해보자라는 마음을 먹은 과거의 저에게도 맛있는 걸 하나 주고 싶군요
(오늘도 이렇게 다이어트는 미룹니다. 그딴 거 알 바 아니죠😎)</p>
<p>이제는 무엇을 공부할지 사실 고민 중입니다.
시스템 해킹 두 번째 로드맵을 할까 아님 알고리즘을 공부할까 아님 웹 해킹을 공부할까....</p>
<p>사실 시스템 해킹 두 번째 로드맵을 공부해보고 싶습니다만, 몇 달 있으면 복학을 하기 때문에 알고리즘도 공부해야 할 것 같습니다...
<img src="https://velog.velcdn.com/images/wonder_land/post/2b84aca1-d7d4-4d56-a4c4-2d23082fc752/image.jpg" alt=""></p>
<p>그래서 아마 시스템 해킹 두 번째 로드맵과 알고리즘을 병행하지 않을까 생각합니다.
(한 번 해보고 너무 힘들면 하나만 하겠죠? 그건 미래의 저한테 맡깁니다.)</p>
<p>해킹이라는 막연한 분야를 손쉽게 큰 장벽 없이 공부할 수 있게 해준 드림핵과,
지금껏 힘들지만 고생해준 과거의 저에게 감사하고,
앞으로 더 고생해줄 드림핵과 저에게 화이팅을 주고 싶네용😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pwnable] 21. Logical Bug: Path Traversal]]></title>
            <link>https://velog.io/@wonder_land/Pwnable-21.-Logical-Bug-Path-Traversal</link>
            <guid>https://velog.io/@wonder_land/Pwnable-21.-Logical-Bug-Path-Traversal</guid>
            <pubDate>Thu, 24 Nov 2022 05:52:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://dreamhack.io/lecture/courses/111">&#39;DreamHack&#39; 중 &#39;System Hacking&#39; 중 &#39;Logical Bug: Path Traversal&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>서론</li>
<li>리눅스 경로</li>
<li>Path Traversal</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-서론">1. 서론</h1>
<p>리눅스 프로그램은 파일 시스템에 접근하여 어떤 파일의 데이터를 읽거나, 파일에 데이터를 쓸 수 있습니다.
예를 들어, 리눅스의 기본 유틸리티인 <code>cat</code>으로 파일의 데이터를 출력하게 하면,
<code>cat</code>은 파일을 열고, 읽은 다음에 <code>stdout</code>에 데이터를 출력합니다.</p>
<p>로컬 파일 시스템에 접근하는 서비스를 외부에 공개할 때는 접근할 수 있는 파일의 경로에 제한을 두어야 합니다.
예를 들어, 사용자에게 각자의 디렉토리를 생성해주고, 그 디렉토리를 자유롭게 활용할 수 있게 해주는 서비스가 있다고 합시다.
이런 서비스를 개발할 때는 당연히 사용자 자신의 디렉토리만 접근할 수 있게 해야 합니다.
악의적인 사용자가 다른 사용자의 디렉토리에 접근하면 악용이 될 수 있기 때문이죠.</p>
<p>&#39;<em><strong>Path Traversal</strong></em>&#39;은 위와 같은 서비스가 있을 때,
_<strong>사용자가 허용되지 않은 경로에 접근할 수 있는 취약점</strong>_을 말합니다.
_<strong>사용자가 접근하려는 경로에 대한 검사가 미흡</strong>_하여 발생하며, 임의 파일 읽기 및 쓰기의 수단으로 활용될 수 있습니다.</p>
<hr>
<h1 id="2-리눅스의-경로">2. 리눅스의 경로</h1>
<p>리눅스에는 파일의 경로를 지정하는 두 가지 방법으로 </p>
<ol>
<li>절대 경로(Absolute Path)</li>
<li>상대 경로(Relative Path)</li>
</ol>
<p>두 가지가 있습니다.</p>
<h2 id="1-절대-경로absolute-path">1) 절대 경로(Absolute Path)</h2>
<blockquote>
<ul>
<li><em><strong>절대 경로(Absolute Path)</strong></em>
: <strong>루트 디렉토리(<code>/</code>)부터 파일에 이를 때까지 거쳐야 하는 디렉토리 명을 모두 연결</strong>하여 구성
: 각 디렉토리는 <code>/</code>로 구분되며, 끝에 대상 파일의 이름을 추가하여 완성</li>
</ul>
</blockquote>
<p>리눅스 파일 시스템에서,
한 파일에 대응되는 절대 경로는 유일하며, 그 파일만의 고유한 값입니다.
그래서 현재 사용자가 어떤 디렉토리에 있더라도, 절대 경로를 사용하면 대상 파일을 가리킬 수 있습니다.</p>
<pre><code>/a/b/target</code></pre><h2 id="2-상대-경로relative-path">2) 상대 경로(Relative Path)</h2>
<blockquote>
<ul>
<li><em><strong>상대 경로(Relative Path)</strong></em>
: <strong>현재 디렉토리를 기준으로, 다른 파일에 이르는 경로를 상대적으로 표현</strong>한 것
: 리눅스에서 <code>..</code>은 이전 디렉토리, <code>.</code>은 현재 디렉토리를 의미하고, 이를 이용하여 상대 경로를 구성</li>
</ul>
</blockquote>
<p>절대 경로와 다르게,
어떤 파일을 가리키는 상대 경로의 수는 무한합니다.</p>
<pre><code>../c/target
../../../a/b/c/target</code></pre><hr>
<h1 id="3-path-traversal">3. Path Traversal</h1>
<blockquote>
<ul>
<li><em><strong>Path Traversal</strong></em>
: <strong>권한이 없는 경로</strong>에 프로세스가 접근할 수 있는 취약점</li>
</ul>
</blockquote>
<p>여기서의 &#39;<em><strong>권한</strong></em>&#39;은 리눅스 파일 시스템에서의 권한이 아닌,
_<strong>서비스 로직 관점에서의 권한</strong>_을 의미합니다.
(앞서 서론에서 예시로 든 예제에서 알 수 있습니다.)</p>
<p>아래의 예시는 Path Traversal 취약점이 있는 예제 코드입니다.</p>
<pre><code class="language-c">// Name: path_traversal.c
// Compile: gcc -o path_traversal path_traversal.c

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

const int kMaxNameLen = 0x100;
const int kMaxPathLen = 0x200;
const int kMaxDataLen = 0x1000;
const char *kBasepath = &quot;/tmp&quot;;

int main() {
  char file_name[kMaxNameLen];
  char file_path[kMaxPathLen];
  char data[kMaxDataLen];
  FILE *fp = NULL;

  // Initialize local variables
  memset(file_name, &#39;\0&#39;, kMaxNameLen);
  memset(file_path, &#39;\0&#39;, kMaxPathLen);
  memset(data, &#39;\0&#39;, kMaxDataLen);

  // Receive input from user
  printf(&quot;File name: &quot;);
  fgets(file_name, kMaxNameLen, stdin);

  // Trim trailing new line
  file_name[strcspn(file_name, &quot;\n&quot;)] = &#39;\0&#39;;

  // Construct the `file_path`
  snprintf(file_path, kMaxPathLen, &quot;%s/%s&quot;, kBasepath, file_name);

  // Read the file and print its content
  if ((fp = fopen(file_path, &quot;r&quot;)) == NULL) {
    fprintf(stderr, &quot;No file named %s&quot;, file_name);
    return -1;
  }

  fgets(data, kMaxDataLen, fp);
  printf(&quot;%s&quot;, data);

  fclose(fp);

  return 0;
}</code></pre>
<p>위의 코드에서 <code>/etc/passwd</code>라는 파일을 읽기 위해서는 파일 이름이 <code>../etc/passwd</code>가 되면 됩니다.</p>
<p>이를 통해 알 수 있듯이,
Path Traversal은 서버의 중요한 데이터를 공격자에게 노출시키는 취약점입니다.
만약 파일에 데이터를 쓸 수 있다면, <code>/etc/passwd</code>를 조작하여 <code>root</code>의 비밀번호를 제거하거나 <code>ssh</code>의 설정을 변경하는 등 서버에 위협되는 행위도 할 수 있습니다.</p>
<hr>
<h1 id="4-qa">4. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="5-마치며">5. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://dreamhack.io/lecture/courses/111">&#39;DreamHack&#39; 중 &#39;System Hacking&#39; 중 &#39;Logical Bug: Path Traversal&#39;</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pwnable] 20. Logical Bug: Command Injection]]></title>
            <link>https://velog.io/@wonder_land/Pwnable-20.-Logical-Bug-Command-Injection</link>
            <guid>https://velog.io/@wonder_land/Pwnable-20.-Logical-Bug-Command-Injection</guid>
            <pubDate>Thu, 24 Nov 2022 04:48:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://dreamhack.io/lecture/courses/108">&#39;DreamHack&#39; 중 &#39;System Hacking&#39; 중 &#39;Logical Bug: Command Injection&#39;</a></li>
</ul>
<hr>
<blockquote>
</blockquote>
<ol>
<li>서론</li>
<li>Command Injection</li>
<li>Q&amp;A</li>
<li>마치며</li>
</ol>
<hr>
<h1 id="1-서론">1. 서론</h1>
<p>프로그램을 만들다보면 스스로 코드를 작성하기보다, 이미 설치된 소프트웨어를 사용하는 것이 필요한 경우가 있습니다.</p>
<p>C/C++으로 프로그래밍할 때는 <code>system</code>함수를 사용하기도 합니다.
이 함수는 함수에 전달된 인자를 셸 프로그램에 전달해 명령어를 실행시킵니다.
<code>system(&quot;cat /etc/passwd&quot;)</code>를 호출하면, 셸 프로그램으로 <code>cat /etc/passwd</code>를 실행한 것과 같습니다.</p>
<p>이러한 이점도 있지만, 그와 동시에 함수의 인자를 셸 명령어로 전달한다는 점에서 치명적인 취약점으로 이어지기도 합니다.</p>
<hr>
<h1 id="2-command-injection">2. Command Injection</h1>
<blockquote>
<ul>
<li><em><strong>인젝션(Injection)</strong></em>
: 악의적인 데이터를 프로그램에 입력하여, 이를 시스템 명령어, 코드, 데이터베이스 쿼리 등으로 실행되게 하는 기법
: 이 중, 사용자의 입력을 시스템 명령어로 실행하게 하는 것을 &#39;<strong>Command Injection</strong>&#39;이라고 함</li>
</ul>
</blockquote>
<p>Command Injection은 <em><strong>명령어를 실행하는 함수에 사용자가 임의의 인자를 전달할 수 있을 때</strong></em> 발생합니다.
사용자가 입력한 임의 IP에 ping을 전송하고 싶다면 <code>system(&quot;ping [user-input]&quot;)</code>,
임의 파일을 읽고 싶다면 <code>system(&quot;cat [user-input]&quot;)</code>등의 형태로 <code>system</code>함수를 사용할 수 있습니다.</p>
<p>하지만, 이러한 호출 과정에서 _<strong>사용자의 입력을 제대로 검사하지 않으면 임의 명령어가 실행</strong>_될 수도 있습니다.
이는 리눅스 셸 프로그램이 지원하는 여러 메타 문자 때문입니다.</p>
<p><code>system</code>함수는 셸 프로그램에 명령어를 전달하여 실행합니다.
셸 프로그램은 다양한 메타 문자를 지원하기도 하죠.</p>
<table>
<thead>
<tr>
<th align="center">Meta 문자</th>
<th align="left">설명</th>
<th align="left">Example</th>
</tr>
</thead>
<tbody><tr>
<td align="center">$</td>
<td align="left">셸 환경변수</td>
<td align="left">$ echo $PWD <br> /homeTheori</td>
</tr>
<tr>
<td align="center">&amp;&amp;</td>
<td align="left">이전 명령어 실행 후 <br> 다음 명령어 실행</td>
<td align="left">$ echo hello &amp;&amp; echo theori <br> hello <br> theori</td>
</tr>
<tr>
<td align="center">;</td>
<td align="left">명령어 구분자</td>
<td align="left">$ echo hello ; echo theori <br> hello <br> theori</td>
</tr>
<tr>
<td align="center">|</td>
<td align="left">명령어 파이핑</td>
<td align="left">$ echo id | /bin/sh <br>  uid=1001(theori) ...</td>
</tr>
<tr>
<td align="center">*</td>
<td align="left">와일드 카드</td>
<td align="left">$ echo .* <br> ...</td>
</tr>
<tr>
<td align="center">`</td>
<td align="left">명령어 치환</td>
<td align="left">$ echo `echo hello theori` <br> hellotheori</td>
</tr>
</tbody></table>
<p>아래의 예제에서는, 사용자가 입력한 IP를 ping의 인자로 전달합니다.
(ping은 특정 IP의 서버가 작동하는지 확인하려고 자주 사용합니다.)</p>
<pre><code class="language-c">// Name: cmdi.c
// Compile: gcc -o cmdi cmdi.c

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

const int kMaxIpLen = 36;
const int kMaxCmdLen = 256;

int main() {
  char ip[kMaxIpLen];
  char cmd[kMaxCmdLen];

  // Initialize local vars
  memset(ip, &#39;\0&#39;, kMaxIpLen);
  memset(cmd, &#39;\0&#39;, kMaxCmdLen);
  strcpy(cmd, &quot;ping -c 2 &quot;);

  // Input IP
  printf(&quot;Health Check\n&quot;);
  printf(&quot;IP: &quot;);
  fgets(ip, kMaxIpLen, stdin);

  // Construct command
  strncat(cmd, ip, kMaxCmdLen);
  printf(&quot;Execute: %s\n&quot;,cmd);

  // Do health-check
  system(cmd);
  return 0;
}</code></pre>
<p>IP로 <code>127.0.0.1</code>을 입력하면 다음과 같이 작동합니다.</p>
<pre><code>$ ./cmdi
Health Check
IP: 127.0.0.1
Execute: ping -c 2 127.0.0.1

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.019 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.032 ms

--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1065ms
rtt min/avg/max/mdev = 0.019/0.025/0.032/0.008 ms</code></pre><p>만약, 악의적인 사용자라고 하면, 코드 상에서 아무런 검사가 없다는 점을 파악해 Command Injection을 시도할 수 있습니다.</p>
<p>다음은 <code>;</code>를 메타문자로 사용하여 셸을 실행시키는 예제입니다.
ping이 정상적으로 실행된 후, <code>/bin/sh</code>가 이어서 실행됩니다.</p>
<pre><code>$ ./cmdi
Health Check
IP: 127.0.0.1; /bin/sh
Execute: ping -c 2 127.0.0.1; /bin/sh

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.046 ms

--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1059ms
rtt min/avg/max/mdev = 0.020/0.033/0.046/0.013 ms

$ id
uid=1000(dreamhack) gid=1000(dreamhack) groups=1000(dreamhack)</code></pre><p>따라서 개발자는 이러한 취약점을 막기 위해,
입력값에 대해 _<strong>메타 문자의 유무를 철저히 검사</strong>_해야합니다.
또한, 꼭 필요한 상황이 아니면 _<strong><code>system</code>과 같은 함수의 사용을 자제</strong>_해야 합니다.
이러한 함수는 memory corruption과 관련된 익스플로잇에도 사용될 수 있기 때문입니다.</p>
<hr>
<h1 id="3-qa">3. Q&amp;A</h1>
<p>-</p>
<hr>
<h1 id="4-마치며">4. 마치며</h1>
<p>-</p>
<blockquote>
<p><em><strong>[Reference]</strong></em> : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.</p>
</blockquote>
<ul>
<li>전반적 내용
: <a href="https://dreamhack.io/lecture/courses/108">&#39;DreamHack&#39; 중 &#39;System Hacking&#39; 중 &#39;Logical Bug: Command Injection&#39;</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>