<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>twi_twi.log</title>
        <link>https://velog.io/</link>
        <description>행복, 사랑, 건강, 개발, 하세요 !</description>
        <lastBuildDate>Wed, 03 Sep 2025 07:30:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>twi_twi.log</title>
            <url>https://velog.velcdn.com/images/twi_twi/profile/deb25826-b979-49ee-8e17-7ae2c6fd3fdf/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. twi_twi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/twi_twi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Study!세미나] ⏱ 시간복잡도 계산 예제 코드 모음 + 연습문제 해설]]></title>
            <link>https://velog.io/@twi_twi/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0-%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-%EB%AA%A8%EC%9D%8C-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C-%ED%95%B4%EC%84%A4</link>
            <guid>https://velog.io/@twi_twi/%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84-%EA%B3%84%EC%82%B0-%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-%EB%AA%A8%EC%9D%8C-%EC%97%B0%EC%8A%B5%EB%AC%B8%EC%A0%9C-%ED%95%B4%EC%84%A4</guid>
            <pubDate>Wed, 03 Sep 2025 07:30:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>면접/세미나 대비용 정리</strong>임두, 같이 풀어봅시다 🙌🏻</p>
</blockquote>
<hr>
<h2 id="1-o1--상수-시간">1) O(1) — 상수 시간</h2>
<pre><code class="language-java">// 배열의 첫, 중간, 마지막 원소를 접근
// 입력 크기와 관계없이 항상 같은 횟수 연산
int accessEdges(int[] a) {
    int first = a[0];
    int mid   = a[a.length / 2];
    int last  = a[a.length - 1];
    return first + mid + last; // O(1)
}</code></pre>
<hr>
<h2 id="2-olog-n--로그-시간">2) O(log n) — 로그 시간</h2>
<pre><code class="language-java">// 이진 탐색
int binarySearch(int[] arr, int target) {
    int l = 0, r = arr.length - 1;
    while (l &lt;= r) {
        int m = (l + r) &gt;&gt;&gt; 1;
        if (arr[m] == target) return m;
        if (arr[m] &lt; target)  l = m + 1;
        else                  r = m - 1;
    }
    return -1;
}</code></pre>
<hr>
<h2 id="3-o√n--제곱근-시간">3) O(√n) — 제곱근 시간</h2>
<pre><code class="language-java">// 약수 개수 세기
int countDivisors(int n) {
    int cnt = 0;
    for (int i = 1; i * i &lt;= n; i++) {
        if (n % i == 0) {
            cnt += (i * i == n) ? 1 : 2;
        }
    }
    return cnt;
}</code></pre>
<hr>
<h2 id="4-on--선형-시간">4) O(n) — 선형 시간</h2>
<pre><code class="language-java">// 배열 합 구하기
long sum(int[] a) {
    long s = 0;
    for (int x : a) s += x;
    return s;
}</code></pre>
<hr>
<h2 id="5-on-log-n--정렬분할정복">5) O(n log n) — 정렬/분할정복</h2>
<pre><code class="language-java">// 정렬 후 k번째 원소 얻기
int kthAfterSort(int[] a, int k) {
    java.util.Arrays.sort(a); // 평균 O(n log n)
    return a[k];
}</code></pre>
<hr>
<h2 id="6-on²--이중-루프">6) O(n²) — 이중 루프</h2>
<pre><code class="language-java">// 모든 쌍 검사
int countPairsLE(int[] a, int T) {
    int n = a.length, cnt = 0;
    for (int i = 0; i &lt; n; i++)
        for (int j = i + 1; j &lt; n; j++)
            if (a[i] + a[j] &lt;= T) cnt++;
    return cnt;
}</code></pre>
<hr>
<h2 id="7-on³--삼중-루프">7) O(n³) — 삼중 루프</h2>
<pre><code class="language-java">// 세 원소 합이 0인 경우의 수
int tripleZeroCount(int[] a) {
    int n = a.length, cnt = 0;
    for (int i = 0; i &lt; n; i++)
        for (int j = i + 1; j &lt; n; j++)
            for (int k = j + 1; k &lt; n; k++)
                if (a[i] + a[j] + a[k] == 0) cnt++;
    return cnt;
}</code></pre>
<hr>
<h2 id="8-o2n--부분집합백트래킹">8) O(2^n) — 부분집합/백트래킹</h2>
<pre><code class="language-java">// 모든 부분집합 탐색
void subsets(int[] a, int idx, java.util.List&lt;Integer&gt; cur) {
    if (idx == a.length) {
        return; // 처리
    }
    subsets(a, idx + 1, cur);          // 선택 X
    cur.add(a[idx]);
    subsets(a, idx + 1, cur);          // 선택 O
    cur.remove(cur.size() - 1);
}</code></pre>
<hr>
<h2 id="9-on--순열-생성">9) O(n!) — 순열 생성</h2>
<pre><code class="language-java">// 순열 생성
void permute(int[] a, boolean[] used, java.util.List&lt;Integer&gt; cur) {
    if (cur.size() == a.length) {
        return; // 처리
    }
    for (int i = 0; i &lt; a.length; i++) {
        if (used[i]) continue;
        used[i] = true;
        cur.add(a[i]);
        permute(a, used, cur);
        cur.remove(cur.size() - 1);
        used[i] = false;
    }
}</code></pre>
<hr>
<h2 id="10-on--트리-dfs">10) O(n) — 트리 DFS</h2>
<pre><code class="language-java">// 트리 전체 DFS
import java.util.*;

class TreeDFS {
    List&lt;List&lt;Integer&gt;&gt; g = new ArrayList&lt;&gt;();
    boolean[] vis;

    TreeDFS(int n) {
        for (int i = 0; i &lt;= n; i++) g.add(new ArrayList&lt;&gt;());
        vis = new boolean[n + 1];
    }
    void addEdge(int u, int v) {
        g.get(u).add(v); g.get(v).add(u);
    }
    void dfs(int v, int p) {
        vis[v] = true;
        for (int nxt : g.get(v)) {
            if (nxt == p) continue;
            if (!vis[nxt]) dfs(nxt, v);
        }
    }
}</code></pre>
<hr>
<h1 id="📝-시간복잡도-계산-연습-문제">📝 시간복잡도 계산 연습 문제</h1>
<hr>
<h3 id="문제-1">문제 1</h3>
<pre><code class="language-java">int foo(int n) {
    int cnt = 0;
    for (int i = 1; i &lt;= n; i++) {
        int x = i;
        while (x &gt; 0) {
            x &gt;&gt;= 1;
            cnt++;
        }
    }
    return cnt;
}</code></pre>
<p><strong>풀이</strong>: 각 i마다 while은 log i번 → 합 Σ log i = Θ(n log n).
<strong>정답: O(n log n)</strong></p>
<hr>
<h3 id="문제-2">문제 2</h3>
<pre><code class="language-java">int bar(int n) {
    int cnt = 0;
    for (int i = 1; i &lt;= n; i++)
        for (int j = i; j &lt;= n; j++)
            cnt++;
    return cnt;
}</code></pre>
<p><strong>풀이</strong>: 내부 반복은 (n - i + 1). 합 = n(n+1)/2 = O(n²).
<strong>정답: O(n²)</strong></p>
<hr>
<h3 id="문제-3">문제 3</h3>
<pre><code class="language-java">int baz(int[] a, int T) {
    java.util.Arrays.sort(a); // O(n log n)
    int l = 0, r = a.length - 1, cnt = 0;
    while (l &lt; r) {
        if (a[l] + a[r] &lt;= T) { cnt += (r - l); l++; }
        else r--;
    }
    return cnt;
}</code></pre>
<p><strong>풀이</strong>: 정렬 O(n log n) + 투포인터 O(n). 지배항은 정렬.
<strong>정답: O(n log n)</strong></p>
<hr>
<h3 id="문제-4">문제 4</h3>
<pre><code class="language-java">void dfs(int v, int p) {
    size[v] = 1;
    for (int nxt : g.get(v)) {
        if (nxt == p) continue;
        dfs(nxt, v);
        size[v] += size[nxt];
    }
}</code></pre>
<p><strong>풀이</strong>: 모든 노드와 간선을 1번 방문 → O(n).
<strong>정답: O(n)</strong></p>
<hr>
<h3 id="문제-5">문제 5</h3>
<pre><code class="language-java">int rec(int n) {
    if (n &lt;= 1) return 1;
    return rec(n / 2) + 1;
}</code></pre>
<p><strong>풀이</strong>: T(n) = T(n/2) + O(1). 로그 깊이만큼 → O(log n).
<strong>정답: O(log n)</strong></p>
<hr>
<h3 id="문제-6">문제 6</h3>
<pre><code class="language-java">int rec2(int n) {
    if (n &lt;= 1) return 1;
    return rec2(n/2) + rec2(n/2) + n;
}</code></pre>
<p><strong>풀이</strong>: T(n) = 2T(n/2) + O(n). 마스터정리 → O(n log n).
<strong>정답: O(n log n)</strong></p>
<hr>
<h3 id="문제-7">문제 7</h3>
<pre><code class="language-java">int qux(int n) {
    int cnt = 0;
    for (int i = 1; i &lt;= n; i *= 3)
        for (int j = 1; j &lt;= n; j++)
            cnt++;
    return cnt;
}</code></pre>
<p><strong>풀이</strong>: 바깥은 log n, 안쪽은 n. 곱해서 O(n log n).
<strong>정답: O(n log n)</strong></p>
<hr>
<h3 id="문제-8">문제 8</h3>
<pre><code class="language-java">int myst(int n) {
    int cnt = 0;
    for (int i = 1; i &lt;= n; i++)
        for (int j = 1; j * j &lt;= i; j++)
            cnt++;
    return cnt;
}</code></pre>
<p><strong>풀이</strong>: 내부는 √i번. 합 Σ√i ≈ n^(3/2).
<strong>정답: O(n^(3/2))</strong></p>
<hr>
<h3 id="문제-9">문제 9</h3>
<pre><code class="language-java">void backtrack(int[] a, int idx) {
    if (idx == a.length) return;
    backtrack(a, idx + 1);
    backtrack(a, idx + 1);
}</code></pre>
<p><strong>풀이</strong>: 각 원소마다 선택/비선택 → 2^n 가지.
<strong>정답: O(2^n)</strong></p>
<hr>
<h3 id="문제-10">문제 10</h3>
<pre><code class="language-java">int g(int n) {
    if (n &lt;= 1) return 1;
    return g(n-1) + g(n-1);
}</code></pre>
<p><strong>풀이</strong>: T(n) = 2T(n-1) + O(1). 지수적으로 증가.
<strong>정답: O(2^n)</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study!세미나] 트리(Tree) 기반 알고리즘 3대장: 순회, 지름, 최소 공통 조상 (feat. 시간복잡도)]]></title>
            <link>https://velog.io/@twi_twi/Study%EC%84%B8%EB%AF%B8%EB%82%98-%ED%8A%B8%EB%A6%ACTree-%EA%B8%B0%EB%B0%98-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3%EB%8C%80%EC%9E%A5-%EC%88%9C%ED%9A%8C-%EC%A7%80%EB%A6%84-%EC%B5%9C%EC%86%8C-%EA%B3%B5%ED%86%B5-%EC%A1%B0%EC%83%81-feat.-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@twi_twi/Study%EC%84%B8%EB%AF%B8%EB%82%98-%ED%8A%B8%EB%A6%ACTree-%EA%B8%B0%EB%B0%98-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-3%EB%8C%80%EC%9E%A5-%EC%88%9C%ED%9A%8C-%EC%A7%80%EB%A6%84-%EC%B5%9C%EC%86%8C-%EA%B3%B5%ED%86%B5-%EC%A1%B0%EC%83%81-feat.-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Wed, 03 Sep 2025 03:50:18 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@twi_twi/Study%EC%84%B8%EB%AF%B8%EB%82%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B8%B0%EB%B3%B8feat.-%ED%95%B4%EC%8B%9C%EC%B6%A9%EB%8F%8C-%ED%8A%B8%EB%A6%AC">🪴이전 글(트리의 개념/종류) 바로가기 🪴</a></p>
<br/>

<blockquote>
<p>트리 <strong>종류</strong>는 이전에 다뤘으니, 이번에는 <strong>“트리 위에서 실제로 자주 쓰는 알고리즘”</strong>에 집중해보도록 한다.</p>
</blockquote>
<hr>
<h2 id="0-트리-복습-🌳">0. 트리 복습 🌳</h2>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/2c8d38a8-4ff1-4131-a70f-0b099c70728e/image.png" alt=""></p>
<ul>
<li><p><strong>정의</strong>: 노드(Node)와 간선(Edge)로 이루어진 <strong>계층적 자료구조</strong>.</p>
</li>
<li><p><strong>특징</strong>: 사이클이 없고, <code>n</code>개의 노드가 있으면 간선은 <code>n-1</code>. (연결 트리 기준)</p>
</li>
<li><p><strong>용어</strong>:</p>
<ul>
<li>루트(root), 부모/자식(parent/child), 형제(sibling)</li>
<li>깊이(depth): 루트에서 노드까지 간선 수</li>
<li>높이(height): 트리/노드의 최대 깊이</li>
</ul>
</li>
<li><p><strong>표현</strong>:</p>
<ul>
<li><strong>인접 리스트</strong>(자주 씀): <code>List&lt;List&lt;Integer&gt;&gt; graph</code></li>
<li><strong>부모 배열</strong>: <code>parent[v]</code></li>
<li><strong>자식 리스트</strong>: <code>children[v]</code> (루트 고정 트리일 때 편함)</li>
</ul>
</li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="1-시간복잡도time-complexity-⏰">1. 시간복잡도(Time Complexity) ⏰</h2>
<h3 id="1-0-시간복잡도와-공간복잡도">1-0. 시간복잡도와 공간복잡도</h3>
<p><a href="https://adjh54.tistory.com/186">참고링크</a></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/642ac090-c479-4cf6-928e-eeb58ba8ec76/image.png" alt=""></p>
<p><strong>1) 시간 복잡도(Time Complexity)</strong></p>
<blockquote>
<ul>
<li>알고리즘이 실행될 때 _<strong>필요한 ‘입력 값’과 ‘연산 수행 시간’에 따라 ‘효율적인 알고리즘’을 나타내는 척도</strong>_를 의미</li>
<li>즉, _<strong>입력 값이 커질수록 알고리즘의 수행 시간이 어떻게 증가하는지에 따른 지표</strong>_를 의미</li>
<li>시간 복잡도는 ‘빅오 표기법(Big-O notation)’를 통해 표현하며, _<strong>‘수치가 작을수록 효율적인 알고리즘’</strong>_을 의미</li>
</ul>
</blockquote>
<p><strong>2) 공간 복잡도(Space Complexity)</strong></p>
<blockquote>
<ul>
<li>알고리즘이 실행될 때 <strong><em>필요한 ‘메모리 공간의 양’</em></strong>을 의미</li>
<li>즉 알고리즘의 <em><strong>효율성을 판단</strong>_하는 데 사용되며 **_일반적으로 메모리 사용량이 적을수록 더 효율적인 알고리즘</em>**</li>
</ul>
</blockquote>
<br/>

<p>공간 복잡도는 일반적으로 알고리즘의 시간 복잡도와 함께 고려되며 알고리즘이 실행되는 환경에 따라 달라질 수 있음.
ex) 일부 알고리즘은 실행될 때 추가적인 메모리를 필요로 하지 않지만 다른 알고리즘은 입력 데이터의 양에 따라 필요한 메모리 공간이 증가할 수 있음.</p>
<p>따라서 <strong>알고리즘을 설계할때에는 시간 복잡도와 공간 복잡도를 함께 고려해야 함.</strong></p>
<br/>

<h3 id="1-1-왜-필요한가">1-1. 왜 필요한가?</h3>
<ul>
<li>입력 크기 <code>n</code>이 커질수록 <strong>실행 시간이 어떻게 증가</strong>하는지 비교하는 척도.</li>
<li>코드 한 줄의 시간보다 <strong>증가율</strong>이 중요(기계/언어/환경에 독립).</li>
</ul>
<br/>

<h3 id="1-2-표기법-3형제">1-2. 표기법 3형제</h3>
<ul>
<li><strong>Big-O, O(f(n))</strong>: <strong>최악</strong> 기준 상한 (보수적/기본적으로 이걸 씀) : 빅오 표기법</li>
<li><strong>Big-Ω, Ω(f(n))</strong>: 최선 기준 하한 : 오메가 표기법</li>
<li><strong>Big-Θ, Θ(f(n))</strong>: 상한=하한 (정확한 증가율) : 세타 표기법</li>
</ul>
<br/>

<h3 id="1-3-자주-보는-증가율-ex">1-3. 자주 보는 증가율 ex.</h3>
<table>
<thead>
<tr>
<th>복잡도</th>
<th>예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td>배열 인덱스 접근</td>
<td>입력 크기와 무관</td>
</tr>
<tr>
<td>O(log n)</td>
<td>이진 탐색</td>
<td>절반씩 줄어듦</td>
</tr>
<tr>
<td>O(n)</td>
<td>한 번 전부 순회</td>
<td>입력만큼 비례</td>
</tr>
<tr>
<td>O(n log n)</td>
<td>병합/힙/퀵(평균) 정렬</td>
<td>n번 × log n 단계</td>
</tr>
<tr>
<td>O(n²)</td>
<td>이중 루프</td>
<td>완전탐색/단순 DP</td>
</tr>
<tr>
<td>O(2ⁿ), O(n!)</td>
<td>부분집합/순열 전부</td>
<td>폭발적 증가</td>
</tr>
</tbody></table>
<br/>

<h3 id="1-4-어떻게-계산하는가-">1-4. 어떻게 계산하는가 ?</h3>
<p><strong>(A) 반복문 합치기</strong></p>
<ul>
<li><code>for (i=1..n)</code> → O(n)</li>
<li><code>for + for (중첩)</code> → O(n²)</li>
<li><code>for (i=1; i&lt;=n; i*=2)</code> → i가 1,2,4,...,n → <strong>O(log n)</strong></li>
</ul>
<p><strong>(B) 등차/등비 합</strong></p>
<ul>
<li><code>1 + 2 + ... + n = n(n+1)/2 = O(n²)</code></li>
<li><code>1 + 2 + 4 + ... + n ≈ 2n = O(n)</code></li>
</ul>
<p><strong>(C) 재귀식</strong></p>
<ul>
<li><p><code>T(n) = T(n/2) + O(1) → O(log n)</code> (이진탐색)</p>
</li>
<li><p><code>T(n) = 2T(n/2) + O(n) → O(n log n)</code> (병합정렬)</p>
</li>
<li><p><strong>트리 DFS</strong>: 각 노드를 <strong>딱 한 번</strong> 방문 → <code>T(n) = Σ T(child) + O(1) = O(n)</code></p>
<ul>
<li>이유: 모든 간선을 많아야 두 번(양방향 방문)만 통과 → <strong>O(n)</strong></li>
</ul>
</li>
</ul>
<br/>


<h3 id="1-5-트리-알고리즘과-복잡도-정리표">1-5. 트리 알고리즘과 복잡도 정리표</h3>
<table>
<thead>
<tr>
<th>주제</th>
<th align="right">전처리</th>
<th align="right">질의/실행</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>DFS/BFS 순회</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>노드·간선 총합에 비례</td>
</tr>
<tr>
<td>트리의 지름</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>DFS/BFS 2번</td>
</tr>
<tr>
<td>LCA(단순)</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>두 노드 경로 비교</td>
</tr>
<tr>
<td>LCA(Binary Lifting)</td>
<td align="right"><strong>O(n log n)</strong></td>
<td align="right"><strong>O(log n)</strong></td>
<td>대량 질의 유리</td>
</tr>
<tr>
<td>서브트리 크기/합</td>
<td align="right"><strong>O(n)</strong></td>
<td align="right"><strong>O(1)</strong> 조회</td>
<td>DFS 한 번</td>
</tr>
<tr>
<td>Euler Tour + 세그먼트</td>
<td align="right"><strong>O(n)</strong></td>
<td align="right">업데이트/쿼리 <strong>O(log n)</strong></td>
<td>구간 질의로 바꿈</td>
</tr>
</tbody></table>
<br/>
<br/>

<hr>
<br/>
<br/>


<h2 id="2-트리-순회-dfsbfs-모든-트리-문제의-뼈대-🦴">2. 트리 순회: DFS/BFS, 모든 트리 문제의 뼈대 🦴</h2>
<p><a href="https://devuna.tistory.com/32">여기 사이트 정리 굳 👀</a></p>
<h3 id="2-1-dfs-전위중위후위--재귀-버전">2-1. DFS (전위/중위/후위) — 재귀 버전</h3>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/a8bedf07-6d3e-452a-92e5-b09305abbe8e/image.png" alt=""></p>
<h1 id="2-트리-순회-dfs--bfs">2. 트리 순회 (DFS / BFS)</h1>
<hr>
<p>DFS는 <strong>“한쪽 길을 끝까지 파고들었다가, 막히면 뒤로 돌아와서 다른 길을 탐색”</strong>하는 방식이다.
미로 탐험한다고 생각하면 쉽다. 출발점에서 한쪽 길을 계속 따라가다가 막히면 돌아와서 다른 길을 가는 것과 같다.</p>
<p>예시 트리:</p>
<pre><code>        1
       / \
      2   3
     / \   \
    4   5   6</code></pre><h3 id="dfs-세-가지-순서">DFS 세 가지 순서</h3>
<ul>
<li><p><strong>Preorder (전위)</strong>: Root → Left → Right
👉 <code>1 2 4 5 3 6</code>
(루트 먼저 → 마치 “대표자가 먼저 말하고 나머지가 뒤따르는” 느낌)</p>
</li>
<li><p><strong>Inorder (중위)</strong>: Left → Root → Right
👉 <code>4 2 5 1 3 6</code>
(왼쪽 → 부모 → 오른쪽 → 특히 이진 탐색 트리에서는 오름차순 정렬 결과가 나옴)</p>
</li>
<li><p><strong>Postorder (후위)</strong>: Left → Right → Root
👉 <code>4 5 2 6 3 1</code>
(모든 일을 다 처리한 뒤에 부모가 마지막으로 보고받는 느낌)</p>
</li>
</ul>
<p>📌 비유:</p>
<ul>
<li>Preorder = “부모님이 먼저 아이들에게 말 걸기”</li>
<li>Inorder = “왼쪽 아이 → 부모님 → 오른쪽 아이 순서로 대화하기”</li>
<li>Postorder = “아이들끼리 먼저 다 놀고, 부모님이 마지막에 확인하기”</li>
</ul>
<h3 id="dfs-재귀-코드-java">DFS 재귀 코드 (Java)</h3>
<pre><code class="language-java">class Node {
    int key;
    Node left, right;
    Node(int k) { key = k; }
}

class Traversal {
    // 전위: Root -&gt; Left -&gt; Right
    void preorder(Node cur) {
        if (cur == null) return;
        System.out.print(cur.key + &quot; &quot;);
        preorder(cur.left);
        preorder(cur.right);
    }

    // 중위: Left -&gt; Root -&gt; Right
    void inorder(Node cur) {
        if (cur == null) return;
        inorder(cur.left);
        System.out.print(cur.key + &quot; &quot;);
        inorder(cur.right);
    }

    // 후위: Left -&gt; Right -&gt; Root
    void postorder(Node cur) {
        if (cur == null) return;
        postorder(cur.left);
        postorder(cur.right);
        System.out.print(cur.key + &quot; &quot;);
    }
}</code></pre>
<p>⏱ <strong>복잡도</strong>: 모든 노드를 1번씩 방문 → O(n)
⚠️ <strong>주의</strong>: 트리가 한쪽으로만 긴 경우(편향 트리)에는 재귀 깊이가 너무 깊어져서 StackOverflow가 날 수 있다 → 이때는 스택을 직접 쓰는 반복문 방식으로 구현하면 안전하다.</p>
<hr>
<h2 id="bfs-breadth-first-search-너비-우선-탐색">BFS (Breadth-First Search, 너비 우선 탐색)</h2>
<p>BFS는 DFS와 다르게, <strong>한 줄로 줄 세우는 것처럼 같은 레벨의 노드를 먼저 전부 방문</strong>한 다음 그다음 레벨로 넘어간다.
학교에서 운동장에서 <strong>“키 순서대로 줄 서라”</strong>가 아니라, <strong>“1학년 먼저, 2학년 다음, 3학년 다음”</strong> 이렇게 학년별로 모이는 느낌이라고 보면 된다.</p>
<p>예시 트리:</p>
<pre><code>레벨별: [1] → [2,3] → [4,5,6]</code></pre><p>👉 결과: <code>1 2 3 4 5 6</code></p>
<h3 id="bfs-코드-큐-활용">BFS 코드 (큐 활용)</h3>
<pre><code class="language-java">import java.util.*;

class LevelOrder {
    void levelOrder(Node root) {
        if (root == null) return;
        Queue&lt;Node&gt; q = new LinkedList&lt;&gt;();
        q.add(root);

        while (!q.isEmpty()) {
            Node cur = q.poll();             // 맨 앞 꺼내기
            System.out.print(cur.key + &quot; &quot;); // 방문 처리
            if (cur.left != null)  q.add(cur.left);
            if (cur.right != null) q.add(cur.right);
        }
    }
}</code></pre>
<p>⏱ <strong>복잡도</strong>: 모든 노드와 간선을 한 번씩 → O(n)
⚠️ 큐에는 한 레벨이 통째로 들어가므로, 트리의 최대 너비만큼 메모리가 필요하다.</p>
<hr>
<p>👉 요약:</p>
<ul>
<li><strong>DFS</strong> = 깊게 파고드는 탐색 (미로 끝까지 가보기)</li>
<li><strong>BFS</strong> = 넓게 훑는 탐색 (운동장에서 학년별로 줄 세우기)</li>
</ul>
<p>트리 문제는 결국 이 두 가지 패턴을 얼마나 자유롭게 쓸 수 있는지가 관건이다.</p>
<pre><code class="language-java">// 트리 노드 정의 (이진트리 예시)
// 실제 코테에서는 인접 리스트 + DFS를 더 자주 씀
class Node {
    int key;
    Node left, right;
    Node(int k) { key = k; }
}

class Traversal {
    // 전위: Root -&gt; Left -&gt; Right
    void preorder(Node cur) {
        if (cur == null) return;
        System.out.print(cur.key + &quot; &quot;);
        preorder(cur.left);
        preorder(cur.right);
    }

    // 중위: Left -&gt; Root -&gt; Right
    // 이진 &quot;탐색&quot; 트리(BST)에서는 &quot;오름차순&quot; 순회가 됨
    void inorder(Node cur) {
        if (cur == null) return;
        inorder(cur.left);
        System.out.print(cur.key + &quot; &quot;);
        inorder(cur.right);
    }

    // 후위: Left -&gt; Right -&gt; Root
    // 서브트리 계산(크기/합) 등 후처리에 유용
    void postorder(Node cur) {
        if (cur == null) return;
        postorder(cur.left);
        postorder(cur.right);
        System.out.print(cur.key + &quot; &quot;);
    }
}</code></pre>
<ul>
<li><strong>복잡도</strong>: 방문을 각 노드 1번 → <strong>O(n)</strong></li>
<li><strong>실전 팁</strong>: 깊은 트리에서 재귀 깊이 주의(필요하면 스택으로 반복 구현)</li>
</ul>
<br/>

<h3 id="2-2-bfs-레벨-순회--큐-사용">2-2. BFS (레벨 순회) — 큐 사용</h3>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/debb766a-da84-430c-bc4c-516f5d6428bf/image.png" alt=""></p>
<pre><code class="language-java">import java.util.*;

class LevelOrder {
    void levelOrder(Node root) {
        if (root == null) return;
        Queue&lt;Node&gt; q = new LinkedList&lt;&gt;();
        q.add(root);

        while (!q.isEmpty()) {
            Node cur = q.poll();               // 맨 앞 꺼내기
            System.out.print(cur.key + &quot; &quot;);   // 방문 처리
            if (cur.left != null)  q.add(cur.left);
            if (cur.right != null) q.add(cur.right);
        }
    }
}</code></pre>
<ul>
<li><strong>복잡도</strong>: 각 노드·간선 1번씩 → <strong>O(n)</strong></li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="3-트리의-지름tree-diameter-가장-먼-두-노드-거리">3. 트리의 지름(Tree Diameter): 가장 먼 두 노드 거리</h2>
<h3 id="3-1-핵심-아이디어-2-번-dfsbfs">3-1. 핵심 아이디어 (2-번 DFS/BFS)</h3>
<ol>
<li>임의 노드 <code>A</code>에서 가장 먼 노드 <code>X</code>를 DFS/BFS로 찾음</li>
<li><code>X</code>에서 가장 먼 노드 <code>Y</code>를 또 DFS/BFS로 찾음</li>
<li><code>dist(X, Y)</code>가 <strong>지름</strong></li>
</ol>
<ul>
<li><strong>근거</strong>: 트리는 사이클이 없고, 최장 경로의 한 끝점은 임의 점에서의 최장 거리 노드 중 하나가 됨.</li>
<li><strong>복잡도</strong>: DFS/BFS 2번 → <strong>O(n)</strong></li>
</ul>
<br/>

<h3 id="3-2-구현-무가중치-인접-리스트">3-2. 구현 (무가중치, 인접 리스트)</h3>
<pre><code class="language-java">import java.util.*;

// 무가중치 트리 지름
class TreeDiameter {
    List&lt;List&lt;Integer&gt;&gt; g;   // 인접 리스트
    boolean[] vis;
    int far;                 // 가장 멀리 있는 노드
    int maxDist;             // 그 거리

    TreeDiameter(int n) {
        g = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt;= n; i++) g.add(new ArrayList&lt;&gt;());
        vis = new boolean[n + 1];
    }

    void addEdge(int u, int v) {
        g.get(u).add(v);
        g.get(v).add(u);
    }

    // DFS로 가장 먼 노드/거리 갱신
    void dfs(int cur, int dist) {
        vis[cur] = true;
        if (dist &gt; maxDist) { maxDist = dist; far = cur; }
        for (int nxt : g.get(cur)) {
            if (!vis[nxt]) dfs(nxt, dist + 1);
        }
    }

    int diameter(int start) {
        // 1) start에서 가장 먼 노드 far 찾기
        Arrays.fill(vis, false);
        maxDist = 0;
        dfs(start, 0);

        // 2) far에서 가장 먼 거리 = 지름
        Arrays.fill(vis, false);
        maxDist = 0;
        dfs(far, 0);
        return maxDist; // 간선 수 기준 지름
    }
}</code></pre>
<br/>

<blockquote>
<p><strong>가중치 있는 트리</strong>(길이가 있는 간선)는 <code>dist + w</code>로 누적만 바꾸면 됨.
(DFS/BFS 대신 다익스트라 불필요: 트리는 유일 경로라 음수만 아니면 DFS로 충분)</p>
</blockquote>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="4-lca-lowest-common-ancestor-최소-공통-조상-⭐️">4. LCA (Lowest Common Ancestor, 최소 공통 조상) ⭐️</h2>
<blockquote>
<p>두 노드 <code>u, v</code>의 공통 조상 중 <strong>가장 가까운</strong> 조상을 찾는 문제. 면접/코테 단골 등장.</p>
</blockquote>
<br/>

<h3 id="4-1-단순-방법-루트까지-경로-비교--on">4-1. 단순 방법 (루트까지 경로 비교) — O(n)</h3>
<ul>
<li><code>u</code>의 조상들을 집합/배열에 저장</li>
<li><code>v</code>에서 루트까지 올라가며 처음 만나는 <code>u</code>의 조상 반환</li>
<li>간단하지만, <strong>질의가 많으면 비효율</strong></li>
</ul>
<br/>

<h3 id="4-2-binary-lifting--전처리-on-log-n-질의-olog-n">4-2. Binary Lifting — 전처리 O(n log n), 질의 O(log n)</h3>
<p><strong>아이디어</strong></p>
<ul>
<li><code>parent[v][k]</code> = 정점 <code>v</code>의 <code>2^k</code>번째 조상</li>
<li><strong>전처리</strong>로 <code>parent</code>와 <code>depth</code>를 채워둠</li>
<li>질의 시 깊이를 맞춘 후, 큰 점프(2^k)로 동시에 끌어올리며 공통 조상 직전까지 이동</li>
</ul>
<p><strong>장점</strong>: LCA를 <strong>여러 번</strong> 물어볼 때 아주 빠름</p>
<br/>

<h4 id="구현">구현</h4>
<pre><code class="language-java">import java.util.*;

class LCA {
    int N, LOG;
    List&lt;List&lt;Integer&gt;&gt; g;
    int[][] parent;   // parent[v][k] = v의 2^k번째 조상
    int[] depth;

    LCA(int n) {
        N = n;
        LOG = 1;
        while ((1 &lt;&lt; LOG) &lt;= N) LOG++; // ceil(log2(N))
        g = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt;= N; i++) g.add(new ArrayList&lt;&gt;());
        parent = new int[N + 1][LOG];
        depth = new int[N + 1];
    }

    void addEdge(int u, int v) {
        g.get(u).add(v);
        g.get(v).add(u);
    }

    // 루트 root 기준으로 depth/부모 채우기
    void dfs(int v, int p) {
        parent[v][0] = p;                         // 바로 위 조상
        for (int k = 1; k &lt; LOG; k++) {
            parent[v][k] = parent[parent[v][k-1]][k-1]; // 2^k = 2^(k-1) + 2^(k-1)
        }
        for (int nxt : g.get(v)) {
            if (nxt == p) continue;
            depth[nxt] = depth[v] + 1;
            dfs(nxt, v);
        }
    }

    // 전처리: root에서 DFS 시작
    void preprocess(int root) {
        depth[root] = 0;
        dfs(root, 0); // 루트의 부모는 0(없음)으로
    }

    int lca(int a, int b) {
        // 1) 깊이 맞추기: a를 더 깊은 노드로 가정
        if (depth[a] &lt; depth[b]) { int t = a; a = b; b = t; }

        // a를 b 깊이까지 끌어올림
        int diff = depth[a] - depth[b];
        for (int k = 0; diff &gt; 0; k++) {
            if ((diff &amp; 1) == 1) a = parent[a][k];
            diff &gt;&gt;= 1;
        }
        if (a == b) return a;

        // 2) 최상위부터 공통 조상 직전까지 점프
        for (int k = LOG - 1; k &gt;= 0; k--) {
            if (parent[a][k] != parent[b][k]) {
                a = parent[a][k];
                b = parent[b][k];
            }
        }
        return parent[a][0];
    }
}</code></pre>
<ul>
<li><strong>전처리</strong>: DFS 한 번 + <code>parent</code> 테이블 채우기 → <strong>O(n log n)</strong></li>
<li><strong>질의</strong>: <strong>O(log n)</strong></li>
<li><strong>주의</strong>: 트리가 1-index인지/루트가 어디인지 통일!</li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="5-서브트리--경로-응용">5. 서브트리 &amp; 경로 응용</h2>
<h3 id="5-1-서브트리-크기합-구하기--dfs-한-번으로-끝">5-1. 서브트리 크기/합 구하기 — DFS 한 번으로 끝</h3>
<ul>
<li>후위 순회(왼쪽·오른쪽 처리 후 현재 처리) 아이디어로, <strong>자식 결과를 모아 부모 값 갱신</strong>.</li>
</ul>
<pre><code class="language-java">import java.util.*;

class SubtreeCalc {
    List&lt;List&lt;Integer&gt;&gt; g;
    int[] size;    // 서브트리 크기 (자기 자신 포함)
    long[] sum;    // 서브트리 값의 합 (예: 노드 가중치가 있을 때)
    int[] value;   // 각 노드의 값 (예시)

    SubtreeCalc(int n) {
        g = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt;= n; i++) g.add(new ArrayList&lt;&gt;());
        size = new int[n + 1];
        sum = new long[n + 1];
        value = new int[n + 1];
    }

    void addEdge(int u, int v) {
        g.get(u).add(v);
        g.get(v).add(u);
    }

    void setValue(int v, int val) {
        value[v] = val;
    }

    // 부모 p, 현재 v
    void dfs(int v, int p) {
        size[v] = 1;
        sum[v] = value[v];
        for (int nxt : g.get(v)) {
            if (nxt == p) continue;
            dfs(nxt, v);
            size[v] += size[nxt];  // 자식 서브트리 크기 누적
            sum[v]  += sum[nxt];   // 자식 서브트리 합 누적
        }
    }
}</code></pre>
<ul>
<li><strong>복잡도</strong>: DFS 한 번 → <strong>O(n)</strong></li>
<li><strong>활용</strong>: “서브트리 질의”, “잘라내기 비용”, “부분 트리 조건 판정” 등</li>
</ul>
<br/>

<h3 id="5-2-루트→리프-경로-합이-특정-값인지-백트래킹">5-2. 루트→리프 <strong>경로 합</strong>이 특정 값인지 (백트래킹)</h3>
<pre><code class="language-java">class PathSum {
    // 이진트리 Node 재사용
    boolean hasPathSum(Node root, int target) {
        if (root == null) return false;
        // 리프에 도달했을 때 합이 정확히 target이면 true
        if (root.left == null &amp;&amp; root.right == null) {
            return target == root.key;
        }
        // 왼쪽/오른쪽으로 내려가면서 남은 합 갱신
        return hasPathSum(root.left,  target - root.key) ||
               hasPathSum(root.right, target - root.key);
    }
}</code></pre>
<ul>
<li><strong>복잡도</strong>: 모든 노드 1번 → <strong>O(n)</strong></li>
<li><strong>포인트</strong>: “리프에서 판정”, “부분합 전달”</li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="6-euler-tour--세그먼트-트리">6. Euler Tour + 세그먼트 트리</h2>
<blockquote>
<p>_트리 문제를 <strong>배열 문제</strong>로 바꿔서 ! _
구간 쿼리(합/최솟값/최댓값)를 O(log n)에 처리하는 테크닉</p>
</blockquote>
<p>간단하게 보도록 하자.</p>
<br/>

<h3 id="6-1-아이디어">6-1. 아이디어</h3>
<ul>
<li>DFS로 들어올 때 <code>in[v]</code>에 타임스탬프, 나갈 때 <code>out[v]</code> 저장</li>
<li><code>v</code>의 서브트리 노드는 <strong>Euler 배열</strong>에서 <code>[in[v], out[v]]</code> 구간에 연속 배치</li>
<li>이후 세그먼트 트리/펜윅 트리로 <strong>구간 합/최솟값</strong> 등을 빠르게 질의</li>
</ul>
<pre><code class="language-java">class EulerTour {
    List&lt;List&lt;Integer&gt;&gt; g;
    int time = 0;
    int[] in, out, euler; // euler[time] = 해당 시점의 노드 번호

    EulerTour(int n) {
        g = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt;= n; i++) g.add(new ArrayList&lt;&gt;());
        in = new int[n + 1];
        out = new int[n + 1];
        euler = new int[n + 1]; // 진입 시점만 기록할 경우 크기 n
    }

    void addEdge(int u, int v) {
        g.get(u).add(v);
        g.get(v).add(u);
    }

    void dfs(int v, int p) {
        in[v] = ++time;
        euler[time] = v;
        for (int nxt : g.get(v)) if (nxt != p) dfs(nxt, v);
        out[v] = time;
    }
}</code></pre>
<ul>
<li><strong>전처리</strong>: DFS 한 번 → <strong>O(n)</strong></li>
<li><strong>질의</strong>: 세그먼트/펜윅으로 <strong>O(log n)</strong></li>
<li><strong>응용</strong>: “서브트리 합 갱신/조회”, “서브트리 최솟값”</li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="7-트라이trie와-문자열-검색">7. 트라이(Trie)와 문자열 검색</h2>
<blockquote>
<p>문자열 검색/자동완성에서 <strong>문자 길이 m에 대해 O(m)</strong> 에 탐색</p>
</blockquote>
<p>간단하게 보도록 하자.</p>
<pre><code class="language-java">import java.util.*;

class Trie {
    static class Node {
        Map&lt;Character, Node&gt; child = new HashMap&lt;&gt;();
        boolean end;
    }
    Node root = new Node();

    void insert(String s) {
        Node cur = root;
        for (char c : s.toCharArray()) {
            cur = cur.child.computeIfAbsent(c, k -&gt; new Node());
        }
        cur.end = true;
    }

    boolean search(String s) {
        Node cur = root;
        for (char c : s.toCharArray()) {
            cur = cur.child.get(c);
            if (cur == null) return false;
        }
        return cur.end;
    }

    boolean startsWith(String prefix) {
        Node cur = root;
        for (char c : prefix.toCharArray()) {
            cur = cur.child.get(c);
            if (cur == null) return false;
        }
        return true;
    }
}</code></pre>
<ul>
<li><strong>삽입/탐색</strong>: 문자열 길이 <code>m</code>에 대해 <strong>O(m)</strong></li>
<li><strong>실전</strong>: 자동완성/사전/접두사 판정</li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="8-시간복잡도-요약-표">8. 시간복잡도 요약 표</h2>
<h3 id="8-1-트리-알고리즘-요약">8-1. 트리 알고리즘 요약</h3>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th align="right">전처리</th>
<th align="right">질의/실행</th>
<th>총평</th>
</tr>
</thead>
<tbody><tr>
<td>DFS/BFS 순회</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>모든 문제의 기본</td>
</tr>
<tr>
<td>트리의 지름</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>DFS/BFS 2회</td>
</tr>
<tr>
<td>LCA (단순)</td>
<td align="right">-</td>
<td align="right"><strong>O(n)</strong></td>
<td>질의 적을 때 OK</td>
</tr>
<tr>
<td>LCA (Binary Lifting)</td>
<td align="right"><strong>O(n log n)</strong></td>
<td align="right"><strong>O(log n)</strong></td>
<td>질의 많을 때 베스트</td>
</tr>
<tr>
<td>서브트리 크기/합</td>
<td align="right"><strong>O(n)</strong></td>
<td align="right"><strong>O(1)</strong></td>
<td>DFS 1번 후 조회</td>
</tr>
<tr>
<td>Euler + 세그트리</td>
<td align="right"><strong>O(n)</strong></td>
<td align="right"><strong>O(log n)</strong></td>
<td>서브트리 구간 질의</td>
</tr>
<tr>
<td>트라이</td>
<td align="right">삽입 O(m)</td>
<td align="right">탐색 O(m)</td>
<td>문자열 길이 m</td>
</tr>
</tbody></table>
<h3 id="8-2-복잡도-계산-감-잡기-표">8-2. 복잡도 계산 감 잡기 표</h3>
<table>
<thead>
<tr>
<th>코드 패턴</th>
<th>직관</th>
<th>복잡도</th>
</tr>
</thead>
<tbody><tr>
<td><code>for (i=1..n)</code></td>
<td>한 번씩</td>
<td>O(n)</td>
</tr>
<tr>
<td><code>for i .. n</code> + <code>for j .. n</code></td>
<td>이중 반복</td>
<td>O(n²)</td>
</tr>
<tr>
<td><code>for (i=1; i&lt;=n; i*=2)</code></td>
<td>절반/배수로 증가</td>
<td>O(log n)</td>
</tr>
<tr>
<td>DFS(모든 노드 1회 방문)</td>
<td>간선 합쳐서 두 번 이하 방문</td>
<td>O(n)</td>
</tr>
<tr>
<td><code>T(n) = T(n/2) + 1</code></td>
<td>로그 깊이</td>
<td>O(log n)</td>
</tr>
</tbody></table>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="9-tip--tree-에서-무엇을-조심해야-하는가">9. Tip : tree 에서 무엇을 조심해야 하는가</h2>
<ul>
<li><strong>방문 체크/부모 체크</strong>: 인접 리스트 DFS에서 <code>if (nxt == parent) continue;</code> 잊지 않기</li>
<li><strong>루트 통일</strong>: LCA/서브트리 문제는 어떤 노드를 루트로 볼지 고정</li>
<li><strong>1-index vs 0-index</strong>: 입력과 배열 인덱스 혼용 주의</li>
<li><strong>재귀 깊이</strong>: Java는 <strong>깊은 트리에서 StackOverflow</strong> 가능 → 필요 시 반복 DFS(스택)로 변환</li>
<li><strong>입출력 최적화</strong>: 코테 시 <code>BufferedReader</code>/<code>StringBuilder</code> 사용</li>
<li><strong>가중치/무가중치 구분</strong>: 거리 계산할 때 <code>+1</code>인지 <code>+w</code>인지 혼동 금지</li>
<li><strong>테스트 케이스</strong>: 단노드, 일자형(편향) 트리, 별형(star), 균형형 등 엣지 케이스 체크</li>
</ul>
<br/>
<br/>


<h3 id="📎--최소-예제-입력-구성-tip">📎 : 최소 예제 입력 구성 tip</h3>
<ul>
<li><p><strong>지름</strong>: 1–2–3–4–5 (일자형) → 답 4</p>
</li>
<li><p><strong>LCA</strong>:</p>
<pre><code>     1
   /   \
  2     3
 / \   / \
4  5  6  7</code></pre><ul>
<li>LCA(4,5)=2, LCA(4,6)=1, LCA(6,7)=3</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Study!세미나] 자료구조 기본(feat. 해시충돌,  트리)]]></title>
            <link>https://velog.io/@twi_twi/Study%EC%84%B8%EB%AF%B8%EB%82%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B8%B0%EB%B3%B8feat.-%ED%95%B4%EC%8B%9C%EC%B6%A9%EB%8F%8C-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@twi_twi/Study%EC%84%B8%EB%AF%B8%EB%82%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B8%B0%EB%B3%B8feat.-%ED%95%B4%EC%8B%9C%EC%B6%A9%EB%8F%8C-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Wed, 27 Aug 2025 03:50:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-자료구조의-개념--알고리즘과의-연관성">1. 자료구조의 개념 + 알고리즘과의 연관성</h2>
<h3 id="1️⃣-자료구조란">1️⃣ 자료구조란?</h3>
<p><a href="https://bnzn2426.tistory.com/m/115">🚀 자료구조의 개념 이해</a></p>
<blockquote>
<p>자료구조(Data Structure)는 데이터를 <strong>효율적</strong>으로 저장, 관리, 검색하기 위한 방법.</p>
</blockquote>
<blockquote>
<p>데이터를 단순히 모아두는 것이 아니라, <strong>문제 해결에 적합한 방식으로 조직화</strong>하는 과정이다.</p>
</blockquote>
<h4 id="왜-자료구조가-중요한가">왜 자료구조가 중요한가?</h4>
<ul>
<li>데이터가 많아질수록 단순한 구조로는 한계가 생긴다.</li>
<li>예를 들어 100개의 데이터를 찾는 것과 1억 개 중에서 찾는 것은 효율성 차이가 극명하다.</li>
<li>자료구조를 잘 선택해야 <strong>메모리 낭비를 줄이고, 연산 속도를 최적화</strong>할 수 있다.</li>
</ul>
<hr>
<h3 id="2️⃣-알고리즘algorithm과의-관계">2️⃣ 알고리즘(Algorithm)과의 관계</h3>
<blockquote>
<ul>
<li>알고리즘: 문제 해결을 위한 단계적 절차.</li>
</ul>
</blockquote>
<ul>
<li>자료구조: 알고리즘이 데이터를 다루는 <strong>무대</strong>.</li>
</ul>
<p><strong>같은 알고리즘이라도 어떤 자료구조 위에서 동작하는가에 따라 성능이 달라진다.</strong></p>
<p>예시) 탐색 문제</p>
<ul>
<li>배열에서 선형 탐색 → <code>O(n)</code></li>
<li>정렬된 배열에서 이진 탐색 → <code>O(log n)</code></li>
<li>해시테이블 탐색 → 평균 <code>O(1)</code></li>
</ul>
<hr>
<h3 id="3️⃣-시간-복잡도-time-complexity">3️⃣ 시간 복잡도 (Time Complexity)</h3>
<blockquote>
<p>시간 복잡도는 알고리즘 실행 시간이 입력 크기 n에 따라 어떻게 변하는지를 나타냄.</p>
</blockquote>
<ul>
<li><strong>빅오(Big-O) 표기법</strong>으로 분석한다.</li>
</ul>
<h4 id="✅-주요-시간-복잡도">✅ 주요 시간 복잡도</h4>
<ul>
<li><strong>O(1)</strong> : 상수 시간 (해시테이블 접근)</li>
<li><strong>O(log n)</strong> : 로그 시간 (이진 탐색, 균형 트리 탐색)</li>
<li><strong>O(n)</strong> : 선형 시간 (배열 전체 탐색)</li>
<li><strong>O(n log n)</strong> : 효율적 정렬 (퀵/병합/힙 정렬)</li>
<li><strong>O(n²)</strong> : 단순 정렬 (버블, 삽입, 선택)</li>
</ul>
<pre><code class="language-java">// 예: 선형 탐색 (O(n))
public int linearSearch(int[] arr, int target) {
    for (int i = 0; i &lt; arr.length; i++) {
        if (arr[i] == target) {
            return i;
        }
    }
    return -1; // 찾지 못함
}

// 예: 이진 탐색 (O(log n)) -&gt; 배열이 정렬되어 있어야 함
public int binarySearch(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    while (left &lt;= right) {
        int mid = (left + right) / 2;
        if (arr[mid] == target) return mid;
        else if (arr[mid] &lt; target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}</code></pre>
<br/>
<br/>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="2-자료형에-따른-분류">2. 자료형에 따른 분류</h2>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/5b08f837-bcdd-41e6-971d-9604add07f98/image.png" alt="자료형 분류"></p>
<blockquote>
<p>자료구조는 크게
<strong>기본 자료구조(Primitive Data Structure)</strong>
<strong>비기본 자료구조(Non-Primitive Data Structure)</strong>
로 나눌 수 있다.</p>
</blockquote>
<br/>

<hr>
<br/>

<h4 id="🔹-primitive-data-structure-기본-자료구조"><em>🔹 Primitive Data Structure (기본 자료구조)</em></h4>
<blockquote>
<p>프로그래밍 언어에서 제공하는 <strong>가장 기본적인 단위 자료형</strong>으로, 더 이상 쪼갤 수 없는 기본 블록이다.</p>
</blockquote>
<ul>
<li><p><strong>Integer (정수형)</strong></p>
<ul>
<li>수학에서의 정수에 해당하지만, 컴퓨터에서는 메모리 크기에 따라 표현할 수 있는 범위가 제한된다.</li>
<li>예: <code>int</code>, <code>long</code></li>
</ul>
</li>
<li><p><strong>Float (실수형)</strong></p>
<ul>
<li>수학에서의 실수 개념과 유사하나, 컴퓨터는 유한한 비트 수로 소수를 표현하기 때문에 근사값만 저장할 수 있다.</li>
<li>예: <code>float</code>, <code>double</code></li>
</ul>
</li>
<li><p><strong>Character (문자형)</strong></p>
<ul>
<li>하나의 문자 또는 기호를 저장. 공백 문자(띄어쓰기, tab, enter 등)도 포함된다.</li>
<li>예: <code>&#39;a&#39;</code>, <code>&#39;Z&#39;</code>, <code>&#39; &#39;</code></li>
</ul>
</li>
<li><p><strong>Pointer (포인터)</strong></p>
<ul>
<li>실제 데이터 값 대신 메모리 주소를 저장하는 특수한 자료형.</li>
<li>포인터를 이용해 메모리에 접근하거나 동적 메모리 관리가 가능하다.</li>
</ul>
</li>
</ul>
<br/>


<h4 id="👀-꿀팁-for-코테">👀 꿀팁 For 코테</h4>
<table>
<thead>
<tr>
<th>자료형</th>
<th>크기</th>
<th>최소값</th>
<th>최대값</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td><strong>byte</strong></td>
<td>8 bit</td>
<td>-128</td>
<td>127</td>
<td>(2⁸, 부호 있는 정수)</td>
</tr>
<tr>
<td><strong>short</strong></td>
<td>16 bit</td>
<td>-32,768</td>
<td>32,767</td>
<td>(2¹⁶)</td>
</tr>
<tr>
<td><strong>int</strong></td>
<td>32 bit</td>
<td>-2,147,483,648 (-2³¹)</td>
<td>2,147,483,647 (2³¹-1)</td>
<td>알고리즘 기본 정수형</td>
</tr>
<tr>
<td><strong>long</strong></td>
<td>64 bit</td>
<td>-9,223,372,036,854,775,808 (-2⁶³)</td>
<td>9,223,372,036,854,775,807 (2⁶³-1)</td>
<td>큰 수 계산 시 사용</td>
</tr>
<tr>
<td><strong>float</strong></td>
<td>32 bit</td>
<td>약 ±3.4 × 10³⁸</td>
<td>소수 7자리 정확도</td>
<td>부동소수점 (단정밀도)</td>
</tr>
<tr>
<td><strong>double</strong></td>
<td>64 bit</td>
<td>약 ±1.8 × 10³⁰⁸</td>
<td>소수 15~16자리 정확도</td>
<td>부동소수점 (배정밀도)</td>
</tr>
<tr>
<td><strong>char</strong></td>
<td>16 bit</td>
<td>0</td>
<td>65,535</td>
<td>유니코드 문자 표현</td>
</tr>
<tr>
<td><strong>boolean</strong></td>
<td>1 bit (표준 미정, JVM 구현 의존)</td>
<td>true / false</td>
<td>-</td>
<td>메모리 크기는 명확히 정의X</td>
</tr>
</tbody></table>
<ul>
<li>short는 32 다음 쉼표( , ) 1개</li>
<li>int는 2 다음 쉼표( , ) 3개</li>
<li>long은 9 다음 쉼표( , ) 6개</li>
<li>float / double은 각각 소수 7 / 15 자리</li>
</ul>
<br/>

<hr>
<br/>

<h4 id="🔹-non-primitive-data-structure-비기본-자료구조"><em>🔹 Non-Primitive Data Structure (비기본 자료구조)</em></h4>
<blockquote>
<p>기본 자료구조를 응용하여 만든 복합 자료구조. 데이터의 크기와 구조를 유연하게 다룰 수 있다.</p>
</blockquote>
<br/>

<ul>
<li><p>Arrays (배열)</p>
</li>
<li><p>Lists (리스트)</p>
</li>
<li><p>Files (파일)</p>
</li>
</ul>
<br/>

<ul>
<li><p><strong>Linear (선형 구조)</strong></p>
<ul>
<li>데이터가 일렬로 나열되는 구조.</li>
<li>예: <strong>스택(Stack), 큐(Queue), 배열(Array), 연결 리스트(Linked List)</strong></li>
</ul>
</li>
</ul>
<br/>

<ul>
<li><p><strong>Non-Linear (비선형 구조)</strong></p>
<ul>
<li>데이터가 계층적, 망 형태로 연결되는 구조.</li>
<li>예: <strong>트리(Tree), 그래프(Graph)</strong></li>
</ul>
</li>
</ul>
<br/>
<br/>
<br/>


<hr>
<br/>

<h2 id="3-비기본-자료구조-7개-⭐️⭐️⭐️">3. 비기본 자료구조 7개 ⭐️⭐️⭐️</h2>
<br/>

<h3 id="☝🏻-선형-자료구조">☝🏻 선형 자료구조</h3>
<blockquote>
<p>데이터가 <strong>일렬</strong>로 나열되는 구조.</p>
</blockquote>
<br/>

<h4 id="1️⃣-배열array">1️⃣ <strong>배열(Array)</strong></h4>
<ul>
<li>메모리에 연속적으로 저장.</li>
<li>인덱스를 통해 임의 접근 가능 → <code>O(1)</code>.</li>
<li>삽입/삭제는 전체 이동 필요 → <code>O(n)</code>.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/ad6e5aa7-7477-4471-b1a4-da663f5674fd/image.png" alt=""></p>
<pre><code class="language-java">int[] arr = {10, 20, 30};
System.out.println(arr[1]); // O(1) 접근</code></pre>
<p>→ 배열 목록, 힙, 해시 테이블, 벡터 및 행렬과 같은 기타 데이터 구조를 구축하기 위한 빌딩 블록으로 사용</p>
<p>→ 삽입 정렬, 빠른 정렬, 버블 정렬 및 병합 정렬과 같은 다양한 정렬 알고리즘에 사용</p>
<br/>

<hr>
<h4 id="2️⃣-연결-리스트linked-list">2️⃣ <strong>연결 리스트(Linked List)</strong></h4>
<ul>
<li>노드(Node) 단위로 데이터와 포인터(다음 노드 주소)를 저장. <strong>&quot;순차적&quot;</strong></li>
<li>동적인 데이터 삽입/삭제 빠름 → <code>O(1)</code> (포인터만 수정).</li>
<li>임의 접근 불가 → <code>O(n)</code>.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/e3fc34f5-67be-42b3-a10b-16e61c1e985d/image.png" alt=""></p>
<pre><code class="language-java">class Node {
    int data;
    Node next;
    Node(int data) { this.data = data; }
}

public class Main {
    public static void main(String[] args) {
        Node n1 = new Node(10);
        Node n2 = new Node(20);
        Node n3 = new Node(30);

        n1.next = n2;
        n2.next = n3;

        System.out.println(n1.data);       // 10
        System.out.println(n1.next.data);  // 20
        System.out.println(n2.next.data);  // 30
    }
}</code></pre>
<ul>
<li>각 요소는 Node</li>
<li>각 Node에는 key와 다음 노드를 가리키는 포인터인 next가 포함</li>
<li>첫 번째 요소는 Head</li>
<li>마지막 요소는 Tail</li>
</ul>
<p>종류:</p>
<ol>
<li>단일 연결 리스트(Singly Linked List)</li>
<li>이중 연결 리스트(Doubly Linked List)</li>
<li>원형 연결 리스트(Circular Linked List) ...</li>
</ol>
<br/>

<hr>
<p>🧐 배열과 연결 리스트는 아래의 자료 구조들을 구현하는 데 사용되는 기본 자료 구조 ⬇️⬇️⬇️</p>
<br/>

<hr>
<h4 id="3️⃣-스택stack">3️⃣ <strong>스택(Stack)</strong></h4>
<ul>
<li>LIFO (후입선출) (Last In First Out).</li>
<li>연산: <code>push()</code>, <code>pop()</code>.</li>
<li>활용: 함수 호출 스택(재귀 프로그래밍), 실행취소(Undo), 괄호 검사.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/610ec9c1-7b12-46fd-a62c-78d8cf42bec0/image.png" alt=""></p>
<pre><code class="language-java">Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();
stack.push(10);
stack.push(20);
System.out.println(stack.pop()); // 20</code></pre>
<br/>

<hr>
<h4 id="4️⃣-큐queue">4️⃣ <strong>큐(Queue)</strong></h4>
<ul>
<li>FIFO (선입선출) (First In First Out).</li>
<li>활용: 작업 대기열 시스템, 운영체제 스케줄링, 멀티스레딩 스레드관리.</li>
<li>변형: 원형 큐, Deque(양방향 큐), 우선순위 큐.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/c6acb905-3590-4a7b-92e7-e2f3d9c53b79/image.png" alt=""></p>
<pre><code class="language-java">Queue&lt;Integer&gt; queue = new LinkedList&lt;&gt;();
queue.add(1);
queue.add(2);
System.out.println(queue.poll()); // 1</code></pre>
<br/>

<hr>
<br/>

<h3 id="✌🏻-비선형-자료구조">✌🏻 비선형 자료구조</h3>
<blockquote>
<p>데이터가 <strong>계층적 또는 네트워크 구조</strong>를 이루는 형태.</p>
</blockquote>
<h4 id="5️⃣-트리tree">5️⃣ <strong>트리(Tree)</strong></h4>
<ul>
<li><p>루트(root)에서 시작해 자식(child) 노드로 확장되는 구조.</p>
<ul>
<li>최상위 노드(루트)를 가지고 있음</li>
<li>상위 노드를 부모(parent) 노드, 하위 노드를 자식(child) 노드라 한다.</li>
</ul>
</li>
<li><p><strong>계층적 관계</strong>를 표현.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/74133da9-3b5c-409e-9dae-bd7fcfe91cb6/image.png" alt=""></p>
<p>→ Binary Trees(이진트리)
→ Binary Search Tree(이진 검색 트리)
→ Heap(힙) ... </p>
<br/>

<hr>
<h4 id="6️⃣-그래프graph">6️⃣ <strong>그래프(Graph)</strong></h4>
<ul>
<li>정점(Vertex)과 간선(Edge)으로 구성.<ul>
<li>nodes/vertices(노드/정점) 사이에 edge(엣지)가 있는 collection</li>
</ul>
</li>
<li>네트워크, SNS 친구 관계, GPS 최단 경로 탐색 등에 활용.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/208d9f67-f882-4b5e-a103-baf721d2f080/image.png" alt=""></p>
<ul>
<li>directed(방향) 그래프는 일방통행</li>
<li>undirected(무방향) 그래프는 양방향</li>
</ul>
<br/>
<br/>
<br/>
<br/>


<hr>
<h3 id="🤞🏻-탐색search-자료구조">🤞🏻 탐색(Search) 자료구조</h3>
<p>지금까지는 ,,</p>
<ul>
<li><p><strong>선형 자료구조 (Linear)</strong>
데이터가 <strong>일렬</strong>로 저장 → 배열, 연결리스트, 스택, 큐</p>
</li>
<li><p><strong>비선형 자료구조 (Non-linear)</strong>
데이터가 <strong>계층적/망 형태</strong> → 트리, 그래프</p>
<br/>


</li>
</ul>
<p><strong>해시테이블</strong> : 해시 함수로 인덱스를 계산해 저장하는 자료구조</p>
<p>→ 데이터가 배열처럼 일렬로 저장되긴 하지만, 내부적으로 <strong>해시 함수를 통한 매핑</strong>에 의존
→ 충돌 처리 시 <strong>연결리스트나 트리 같은 다른 자료구조를 함께 사용</strong>.</p>
<p>전통적인 Linear / Non-linear 분류에는 딱 들어가지 않음.</p>
<blockquote>
<p>해시테이블은 배열과 유사하게 연속적인 메모리 공간을 기반으로 하지만, 해시 함수와 충돌 처리 방식(체이닝, 개방 주소법 등)을 사용하기 때문에 전통적인 <strong>선형/비선형 자료구조 분류에는 속하지 않는다.</strong>
대신 <strong>탐색(Search) 자료구조</strong> 또는 <strong>매핑(Map) 자료구조</strong>로 따로 분류하는 것이 일반적이다.</p>
</blockquote>
<br/>
<br/>




<h4 id="7️⃣-hash-table해시-테이블">7️⃣ <strong>Hash table(해시 테이블)</strong></h4>
<ul>
<li><p>해시함수를 사용하여 변환한 값을 색인(index)으로 삼기
→ 키(key)와 데이터(value)를 저장</p>
</li>
<li><p>데이터의 크기에 관계없이 삽입 및 검색에 매우 효율적
→ 데이터베이스 인덱스 구현
→ 사용자 로그인 인증
→ &quot;set&quot; 데이터 구조 구현</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/b6439ff6-0a43-4541-bde2-3c4902d8aff2/image.png" alt=""></p>
<p>보통 테이블 내에 더 작은 서브그룹인 버킷(bucket)에 키/값(key/value) 쌍(pair)을 저장</p>
<p>❗️❗️ <strong>해시값 충돌이 자주 일어날 수 있음</strong></p>
<p>→ 해시 충돌 개선 :
다양한 방법으로 해시 함수를 개선 Or 해시 테이블의 구조 개선(chaining, open addressing 등)의 방법이 사용.</p>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="3-해시테이블-해시-충돌">3. 해시테이블, 해시 충돌</h2>
<p><a href="https://baeharam.netlify.app/posts/data%20structure/hash-table">해시 테이블과 충돌 해결</a></p>
<h3 id="✏️-개념">✏️ 개념</h3>
<h4 id="1-해싱">1. 해싱</h4>
<ul>
<li><p>임의의 길이의 값을 해시함수(Hash Function)를 사용하여 고정된 크기의 값으로 변환하는 작업
<img src="https://velog.velcdn.com/images/twi_twi/post/ec238465-aead-46f5-8ac1-19a9cafe22f1/image.png" alt=""></p>
</li>
<li><p>암호화에 쓰이는 해시 알고리즘 사용하여 변환 (ex. MD5)</p>
</li>
<li><p>여기서는 암호화에 쓰인 방식이 아닌, 자료구조로 사용하고자 하는 해시 테이블을 다루기 때문에 <strong>정수값으로 변환되는 해시 알고리즘</strong> 사용</p>
</li>
</ul>
<h4 id="2-해시-테이블">2. 해시 테이블</h4>
<ul>
<li><strong>해싱을 사용하여 데이터를 저장하는 자료구조</strong> : 해시 테이블</li>
<li>해시함수를 사용하여 변환한 값을 색인(index)으로 삼아 키(key)와 데이터(value)를 저장하는 자료구조</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/1645dbc9-eacc-40c9-b392-4552f990de54/image.png" alt=""></p>
<ul>
<li><p><strong>Key → 해시 함수 → 인덱스 → Value 저장</strong></p>
</li>
<li><p>기본연산 : 탐색(Search), 삽입(Insert), 삭제(Delete)</p>
</li>
<li><p>평균 탐색/삽입/삭제 시간: <code>O(1)</code></p>
<ul>
<li>기존 자료구조인 이진탐색트리나 배열에 비해서 굉장히 빠른 속도</li>
</ul>
</li>
</ul>
<br/>
<br/>

<hr>
<h3 id="💥-해시-충돌-collision">💥 해시 충돌 (Collision)</h3>
<blockquote>
<p>서로 다른 키가 동일한 해시 값을 가질 때 발생.</p>
</blockquote>
<h4 id="❓-왜-일어나는가-">❓ 왜 일어나는가 ?</h4>
<ul>
<li><p><strong>적재율(Load Factor)</strong> 의 이해
적재율이란 해시 테이블의 크기 대비, 키의 개수를 말함.
즉, 키의 개수를 K, 해시 테이블의 크기를 N 이라고 했을 때 적재율은 K / N.
Direct Address Table은 키 값을 인덱스로 사용하는 구조이기 때문에 적재율이 1 이하이며 적재율이 1 초과인 해시 테이블의 경우는 반드시 충돌이 발생하게 됨.</p>
</li>
<li><p><strong>충돌이 발생할 경우와, 그렇지 않을 경우</strong>
만약, 충돌이 발생하지 않다고 할 경우 해시 테이블의 탐색, 삽입, 삭제 연산은 모두 O(1) 에 수행.
but, 충돌이 발생할 경우 탐색과 삭제 연산이 최악의 O(K) 만큼 걸리게 됨.
이는 같은 인덱스에 모든 키 값과 데이터가 저장된 경우로 충돌이 전부 발생했음을 말함.
따라서, 충돌을 최대한으로 줄여서 연산속도를 빠르게 하는 것이 해시 테이블의 핵심
→ 이에 중요하게 작용하는 것이 바로 해시함수를 구현하는 해시 알고리즘.</p>
</li>
</ul>
<blockquote>
<p>해시 알고리즘이 견고하지 못하게 되면 해시함수로 도출된 값들이 같은 경우가 빈번하게 발생하게 되므로 잦은 충돌로 이어지게 됨.</p>
</blockquote>
<br/>

<hr>
<br/>

<h3 id="💪🏻-충돌-해결-기법">💪🏻 충돌 해결 기법</h3>
<h4 id="1️⃣-테이블-구조-개선">1️⃣ 테이블 구조 개선</h4>
<ol>
<li><p><strong>체이닝(Chaining)</strong> : 테이블 구조 개선</p>
<ul>
<li>각 인덱스에 연결 리스트를 두어 충돌 데이터를 저장.</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/588bff71-434f-401b-9370-723df93a8a69/image.png" alt=""></p>
<ol start="2">
<li><p><strong>개방 주소법(Open Addressing)</strong> : 테이블 구조 개선</p>
<ul>
<li>빈 슬롯을 탐사하며 저장.<ul>
<li>원래라면 해시함수로 얻은 해시값에 따라서 데이터와 키값을 저장하지만 동일한 주소에 다른 데이터가 있을 경우 다른 주소도 이용할 수 있게 하는 기법</li>
</ul>
</li>
<li>선형 탐사
<img src="https://velog.velcdn.com/images/twi_twi/post/23e54c12-2f97-4d07-bfc9-fc31a6386070/image.png" alt="선형탐사"></li>
<li>제곱 탐사
<img src="https://velog.velcdn.com/images/twi_twi/post/3dc45903-5c77-4928-b5e3-fdf22ad72775/image.png" alt=""></li>
<li>이중 해싱
<img src="https://velog.velcdn.com/images/twi_twi/post/9b8fac8d-e301-4033-8c07-dd8ec0c66c9c/image.png" alt=""></li>
</ul>
</li>
</ol>
<ol start="3">
<li><p><strong>재해싱(Rehashing)</strong></p>
<ul>
<li>테이블 크기를 늘려 다시 해싱.</li>
</ul>
</li>
</ol>
<pre><code class="language-java">Map&lt;String, String&gt; map = new HashMap&lt;&gt;();
map.put(&quot;apple&quot;, &quot;사과&quot;);
map.put(&quot;banana&quot;, &quot;바나나&quot;);
System.out.println(map.get(&quot;apple&quot;)); // 사과</code></pre>
<hr>
<h4 id="2️⃣-해시-함수-개선">2️⃣ 해시 함수 개선</h4>
<ol>
<li>나눗셈법(Division Method)</li>
</ol>
<ul>
<li>아주 간단하게 해시값을 구하는 방법으로 미리 해시 테이블의 크기인 N 을 아는 경우에 사용
해시함수를 적용하고자 하는 값을 N 으로 나눈 나머지를 해시값으로 사용하는 방법
<img src="https://velog.velcdn.com/images/twi_twi/post/92204b53-c009-492f-9c3e-2b2e77d2d1fb/image.png" alt=""></li>
</ul>
<ol start="2">
<li>곱셈법(Multiplication Method)</li>
</ol>
<ul>
<li>kA mod 1 의 의미는 kA 의 소수점 이하 부분을 말하며 이를 N 에 곱하므로 0부터 N 사이의 값이 됨
이 방법의 장점은 N 이 어떤 값이더라도 잘 동작한다는 것이며 A 를 잘 잡는 것이 중요
<img src="https://velog.velcdn.com/images/twi_twi/post/f7699411-c38f-4afd-9881-9bf4c91e90e4/image.png" alt=""></li>
</ul>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="4-트리-tree-응용">4. 트리 (Tree) 응용</h2>
<h3 id="📌-기본-개념">📌 기본 개념</h3>
<blockquote>
<ul>
<li>그래프의 특수한 형태(사이클 없는 연결 구조).</li>
</ul>
</blockquote>
<ul>
<li>용어: 루트(root), 리프(leaf), 깊이(depth), 높이(height) 로 표현</li>
</ul>
<hr>
<h3 id="📌-주요-트리-종류">📌 주요 트리 종류</h3>
<ol>
<li><p><strong>편향 트리 (Skew Tree)</strong></p>
<ul>
<li>트리가 각 레벨에 대해 최소 개수의 노드를 가지면서, 하나의 자식 노드만을 가지는 트리</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/558f0a94-04f0-47e9-a432-50fc70e3dca5/image.png" alt=""></p>
</li>
<li><p><strong>이진 트리(Binary Tree)</strong> ⭐️⭐️⭐️</p>
<ul>
<li>모든 노드의 자식 ≤ 2.<ul>
<li>각 노드가 자식 노드를 최대 2개까지 가질 수 있는 트리</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/eb1645ea-db71-43a1-bce8-dbfad2714c91/image.png" alt=""></p>
</li>
</ol>
<p>2-1. 완전 이진 트리 (Complete Binary Tree)</p>
<ul>
<li><p>각 레벨의 왼쪽에서 오른쪽으로 빈 공간 없이 노드가 채워진 이진 트리</p>
<p>(마지막 레벨을 제외하고 모든 레벨의 노드가 채워져 있다.)</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/0bc6a4da-7271-466f-a08c-fd22061a7c76/image.png" alt=""></p>
</li>
</ul>
<p>2-2. 포화 이진 트리 (Perfect Binary Tree)</p>
<ul>
<li><p>모든 레벨의 노드가 포화 상태로 차있는 이진 트리</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/06c5ece4-788a-4a09-bb91-c5d3af96b767/image.png" alt=""></p>
</li>
</ul>
<p>2-3. 전 이진 트리 (Full Binary Tree)</p>
<ul>
<li>모든 노드가 0개 또는 2개의 자식 노드를 갖는 이진 트리</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/4239a603-f414-48ef-aa22-031567461dfb/image.png" alt=""></p>
<ol start="3">
<li><strong>이진 탐색 트리(BST)</strong> ⭐️⭐️⭐️</li>
</ol>
<ul>
<li>효율적인 탐색을 위해 각 노드의 왼쪽 서브 트리에는 노드보다 작은 값만, 오른쪽 서브 트리에는 노드보다 큰 값만 포함되도록 구성하는 이진 트리</li>
</ul>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/b4a34a37-3f95-4132-a46b-c89418585397/image.png" alt=""></p>
<ul>
<li>중복된 노드를 포함하지 않는다.</li>
<li>트리 순회 시 중위 순회 방식 : <strong>왼쪽 &lt; 부모 &lt; 오른쪽</strong> 규칙</li>
<li><strong>평균 O(log n), 최악 O(n)</strong></li>
</ul>
<ol start="3">
<li><p><strong>AVL 트리 / Red-Black 트리</strong></p>
<ul>
<li>균형을 유지하는 트리.</li>
</ul>
</li>
<li><p><strong>힙(Heap)</strong> ⭐️⭐️⭐️</p>
<ul>
<li><strong>완전 이진 트리 기반</strong></li>
<li><strong>우선순위 큐 구현</strong>, 힙 정렬 알고리즘 에 사용.</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/0645ec14-e087-437e-8d6f-1ad0a86b4126/image.png" alt=""></p>
<ul>
<li>최소 힙 : 부모의 키 값이 자식의 키 값보다 작거나 같다.
루트 노드의 키 값이 트리의 최솟값</li>
<li>최대 힙: 부모의 키 값이 자식의 키 값보다 크거나 같다.
루트 노드의 키 값이 트리의 최댓값</li>
</ul>
<ol start="5">
<li><p><strong>트라이(Trie)</strong></p>
<ul>
<li>문자열 검색 최적화.</li>
<li>검색 알고리즘</li>
</ul>
</li>
<li><p><strong>B-트리 / B+트리</strong></p>
<ul>
<li>데이터베이스 인덱스 구조.</li>
<li>파일 시스템</li>
</ul>
</li>
</ol>
<hr>
<br/>
<br/>

<h3 id="📌-트리-응용">📌 트리 응용</h3>
<ul>
<li><strong>검색 및 정렬</strong>: BST, AVL, Red-Black Tree</li>
<li><strong>우선순위 관리</strong>: 힙 → 우선순위 큐</li>
<li><strong>문자열 검색</strong>: Trie (자동완성, 사전)</li>
<li><strong>DB 인덱스</strong>: B-트리, B+트리</li>
<li><strong>컴파일러 파싱</strong>: 구문 트리</li>
<li><strong>네트워크 라우팅</strong>: 트리 기반 경로 탐색</li>
</ul>
<pre><code class="language-java">// 간단한 이진 탐색 트리 삽입
class Node {
    int key;
    Node left, right;
    Node(int key) { this.key = key; }
}

class BST {
    Node root;

    void insert(int key) {
        root = insertRec(root, key);
    }

    Node insertRec(Node root, int key) {
        if (root == null) return new Node(key);
        if (key &lt; root.key) root.left = insertRec(root.left, key);
        else if (key &gt; root.key) root.right = insertRec(root.right, key);
        return root;
    }
}</code></pre>
<br/>
<br/>
<br/>

<hr>
<br/>
<br/>

<h2 id="정리표-">정리표 ...</h2>
<table>
<thead>
<tr>
<th>자료구조</th>
<th>구조적 특징</th>
<th>장점</th>
<th>단점</th>
<th>대표적인 활용</th>
</tr>
</thead>
<tbody><tr>
<td><strong>배열(Array)</strong></td>
<td>메모리 연속 저장, 인덱스로 접근</td>
<td>접근 속도 빠름(O(1))</td>
<td>삽입/삭제 느림(O(n)), 크기 고정</td>
<td>인덱스 기반 탐색, 정렬</td>
</tr>
<tr>
<td><strong>연결 리스트(Linked List)</strong></td>
<td>노드 + 포인터로 연결</td>
<td>삽입/삭제 빠름</td>
<td>임의 접근 불가(O(n))</td>
<td>동적 메모리, 큐/스택 구현</td>
</tr>
<tr>
<td><strong>스택(Stack)</strong></td>
<td>LIFO (후입선출)</td>
<td>구현 간단, 함수 호출 관리</td>
<td>중간 요소 접근 불가</td>
<td>함수 호출 스택, Undo 기능</td>
</tr>
<tr>
<td><strong>큐(Queue)</strong></td>
<td>FIFO (선입선출)</td>
<td>처리 순서 보장</td>
<td>중간 요소 접근 불가</td>
<td>작업 대기열, 스케줄링</td>
</tr>
<tr>
<td><strong>해시 테이블(Hash Table)</strong></td>
<td>Key → 해시 함수 → Index</td>
<td>평균 O(1) 탐색</td>
<td>해시 충돌, 메모리 낭비 가능</td>
<td>DB 인덱스, 캐시</td>
</tr>
<tr>
<td><strong>트리(Tree)</strong></td>
<td>계층적 구조</td>
<td>빠른 탐색/정렬, 구조적 표현</td>
<td>불균형 시 성능 저하</td>
<td>파일 시스템, DB 인덱스</td>
</tr>
<tr>
<td><strong>그래프(Graph)</strong></td>
<td>정점과 간선</td>
<td>복잡한 관계 표현</td>
<td>구현/탐색 복잡</td>
<td>SNS 관계망, 최단 경로</td>
</tr>
</tbody></table>
<hr>
<table>
<thead>
<tr>
<th>연산 / 자료구조</th>
<th>배열(Array)</th>
<th>연결 리스트(Linked List)</th>
<th>스택/큐(Stack/Queue)</th>
<th>해시 테이블(Hash Table)</th>
<th>이진 탐색 트리(BST)</th>
<th>균형 트리(AVL, Red-Black)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>접근(Access)</strong></td>
<td>O(1)</td>
<td>O(n)</td>
<td>O(n) (탐색 시)</td>
<td>평균 O(1) / 최악 O(n)</td>
<td>O(log n) ~ O(n)</td>
<td>O(log n)</td>
</tr>
<tr>
<td><strong>탐색(Search)</strong></td>
<td>O(n)</td>
<td>O(n)</td>
<td>O(n)</td>
<td>평균 O(1) / 최악 O(n)</td>
<td>O(log n) ~ O(n)</td>
<td>O(log n)</td>
</tr>
<tr>
<td><strong>삽입(Insert)</strong></td>
<td>O(n)</td>
<td>O(1)</td>
<td>O(1)</td>
<td>평균 O(1) / 최악 O(n)</td>
<td>O(log n) ~ O(n)</td>
<td>O(log n)</td>
</tr>
<tr>
<td><strong>삭제(Delete)</strong></td>
<td>O(n)</td>
<td>O(1)</td>
<td>O(1)</td>
<td>평균 O(1) / 최악 O(n)</td>
<td>O(log n) ~ O(n)</td>
<td>O(log n)</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BE 면접 대비] 스파르타 면접문제은행 #1~5]]></title>
            <link>https://velog.io/@twi_twi/BE-%EB%A9%B4%EC%A0%91-%EB%8C%80%EB%B9%84-%EC%8A%A4%ED%8C%8C%EB%A5%B4%ED%83%80-%EB%A9%B4%EC%A0%91%EB%AC%B8%EC%A0%9C%EC%9D%80%ED%96%89-15</link>
            <guid>https://velog.io/@twi_twi/BE-%EB%A9%B4%EC%A0%91-%EB%8C%80%EB%B9%84-%EC%8A%A4%ED%8C%8C%EB%A5%B4%ED%83%80-%EB%A9%B4%EC%A0%91%EB%AC%B8%EC%A0%9C%EC%9D%80%ED%96%89-15</guid>
            <pubDate>Fri, 02 May 2025 08:31:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/6cb05994-ef81-4b9c-82ec-63f88f6fd697/image.png" alt=""></p>
<p>현재 다소 기 죽어있습니다.</p>
<p>눌린 기를 펴기 위해,
BE 개발자 취업 면접 대비 예상질문을 1일 5선씩 답해보기로 했다 !
맛있게 먹어보자, 옴냠냐 ~</p>
<p>문제 출처는 ➡️ <a href="https://nbcamp.spartacodingclub.kr/blog/2025-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A9%B4%EC%A0%91-%EB%AC%B8%EC%A0%9C%EC%9D%80%ED%96%89-%EB%A9%B4%EC%A0%91-%EA%BF%80%ED%8C%81%EA%B3%BC-%EC%98%88%EC%83%81-%EC%A7%88%EB%AC%B8-top-25-47008#2_%EB%B0%B1%EC%97%94%EB%93%9C_%EB%A9%B4%EC%A0%91_%EC%A7%88%EB%AC%B8_25%EA%B0%9C_5">스파르타코딩클럽 블로그(link !)</a></p>
</br>

<hr>
</br>

<h3 id="❗️-면접-유의사항-3종"><strong>❗️ 면접 유의사항 3종</strong></h3>
<p><strong>1️⃣ 개발자로서의 강점과 약점을 명확하게 구성하라.</strong></p>
<p>어떤 개발자이신가요? 어떤 개발자가 되고 싶으신가요?</p>
<pre><code>모든 지원자에게는 각자만의 스토리와 특징, 강점이 있습니다.
이를 이용해서 나의 강점과 약점을 인지하고, 강점은 더욱 부각하고 약점은 개선하는 모습을 보여줄 수 있는 답변들을 구성해 보세요.

백엔드 개발자는 완벽한 구성과 논리 구조 아래 돌아가고 구성되는 하나의 기계와 같은 부분을 작업하는 사람인 만큼, 한 가지에 푹 빠지는 집중도에 관한 역량이 중요한데요.
이처럼 백엔드 개발자가 가져야 하는 역량들 중 내가 가지고 있는 장점과 묶을 수 있는 게 어떤 것이 있을 지 떠올려 보시는 것을 추천합니다.

별 것 아닌 일들도, 나의 강점과 연결지어 극대화해 나라는 개발자를 소개하는 시간이니까요.
인성면접 질문으로도 단골 등장하는 부분이니, &#39;나&#39;는 &#39;어떤 개발자인가&#39; 에 대해 구체적으로 생각해 보세요.</code></pre></br>

<p><strong>2️⃣ 가시적으로 성과 또는 결과를 말하라</strong></p>
<p>&#39;좋은 결과가 있었습니다.&#39;, &#39;보람차게 배웠습니다&#39;, &#39;많이 개선되었습니다.&#39; ...
이는 추상적이고 정확한 결과를 예상할 수 없어 평가가 어렵다.
그에 따라 계속해서 뭘 배웠는지, 얼마나 개선했는지 꼬리 질문을 하게 된다.</p>
<pre><code>대단한 성과나 결과인지는 중요하지 않습니다.
중요한 것은, 소소하게라도 결과를 내기 위해 일해 본 경험이 있고 그를 자기 발전의 양분으로 잘 삼아 성장할 수 있는 개발자인지를 증명하는 것입니다.
속도가 얼마에서 얼마로 개선되었는지, 메모리가 얼마에서 얼마로 줄었는지 숫자를 이용해 이야기하세요.

백엔드의 개발자의 경우, 구현한 결과물이 당장 눈앞에 보이는 것은 아니기 때문에 어떻게 표현해야 할 지 어려울 수 있는데요.
이러한 경우, 포트폴리오에 준비했던 프로젝트의 아키텍처를 제시해 보는 것도 좋은 방법입니다.

실제로 포트폴리오에 아키텍처 등의 시각 자료를 포함하고 서류 합격률이 솟구친 사례를 참고하세요.</code></pre></br>



<p><strong>3️⃣ 포트폴리오의 프로젝트 경험과 회고에 대해 튼튼히 준비하라</strong></p>
<pre><code>최근에는 신입 지원자들도 다양한 프로젝트 경험을 통해 역량을 증명하는 경우가 많습니다.
실무와 가장 가까운 경험이기 때문에 나의 역량을 증명하기 위한 굉장히 좋은 방법 중 하나인데요.

하지만 이렇게 프로젝트 경험을 내보일 때는 이 경험의 내용과 그를 통해 배운 것,
회고를 탄탄히 준비해야 합니다.

특히 팀 프로젝트일 경우, 이 중 내가 진짜 한 일이 어디서부터 어디까지인지를 명확히 하는 것이 중요합니다.
면접관들은 지원자의 진짜 실력과 역량을 알아보기 위해 프로젝트 경험에 대해 자세한 질문을 할 수밖에 없다는 것을 기억하세요.

개발 과정에서 사용한 기술들에 대한 설명과 해당 기술을 선택한 이유, 트러블 슈팅 과정, 그를 통해 배운 것과 문제 해결 뒤 어떤 개선점이 있었는지를 상기 부분에 말한 것처럼 명확한 지표를 이용해 말하는 것을 유념하세요.</code></pre><p></br></br></p>
<hr>
<p></br></br></p>
<h3 id="🫵🏻-오늘의-질문-x-5">🫵🏻 오늘의 질문 x 5</h3>
<h4 id="span-stylebackground-color-rgba242179188051-call-by-reference란-무엇이고-보통-어떻게-쓰이는지-설명해-보세요span"><span style="background-color: rgba(242,179,188,0.5)">1) Call by reference란 무엇이고 보통 어떻게 쓰이는지 설명해 보세요.</span></h4>
<ul>
<li><p><code>Call by value (값에 의한 호출)</code>: 변수 값 자체를 복사해서 메서드에 전달.</p>
</li>
<li><p><code>Call by reference (참조에 의한 호출)</code>: 변수의 주소(참조)를 전달해서 메서드에서 원본 데이터에 영향을 줄 수 있음.</p>
</br>
Spring으로 개발을 할 때는 JAVA를 사용하고, JAVA는 `Call by value`를 따른다.
```java
// 기본형 변수

</li>
</ul>
<p>void change(int x) {
    x = 10;
}
public static void main(String[] args) {
    int a = 5;
    change(a);
    System.out.println(a); // 출력은 여전히 5
}</p>
<pre><code>위의 예제에서 a 값이 그대로인 이유는, change 함수로 값 5만 복사돼서 넘어갔기 때문이다.
하지만 여기서 중요한 건 기본형과 **참조형(객체)**의 차이가 된다.
&lt;/br&gt;

```java
// 참조형 변수(객체)를 넘겼을 때

class Person {
    String name;
}
void changeName(Person p) {
    p.name = &quot;민용&quot;;
}
public static void main(String[] args) {
    Person me = new Person();
    me.name = &quot;해미&quot;;
    changeName(me);
    System.out.println(me.name); // 출력은 &quot;민용&quot;
}</code></pre><p>me 객체의 name 필드는 바뀌게 된다.
me라는 <strong>객체의 주소(reference)</strong>가 복사되어 넘어갔기 때문에, 객체 안의 상태를 바꾸는 것은 가능하다.</p>
<p>주소 복사만 전달되므로, 객체 내부 상태 변경은 영향을 받으나, 참조 자체를 새로 할당하면 영향을 받지 않게 된다.
</br>
이처럼 Spring은 JAVA기반이므로, 기본적으로 Call by Value 방식을 따른다.</p>
<p>하지만 대부분의 데이터는 객체로 다루게 되기 때문에, 메서드에서 객체 속성을 변경하면 바깥에도 영향이 있다. 그래서 개발자 입장에서는 <strong>Call by reference처럼 보이는 효과</strong>를 경험하게 된다.
이러한 경우 <strong>객체의 상태 변경과 관리가 더 효율적이며 예측 가능해진다</strong>는 장점이 있다.
</br></p>
<p><code>+) Call by reference를 따르는 언어?</code>
C++이 있다. C의 경우 Call by Value이나, 포인터로 Call by Reference(주소 전달)처럼 구현이 가능하다
</br>
</br></p>
<h4 id="span-stylebackground-color-rgba242179188052-override와-overload-를-설명해-보세요span"><span style="background-color: rgba(242,179,188,0.5)">2) Override와 Overload 를 설명해 보세요.</span></h4>
<p>마찬가지로 JAVA와 같은 객체지향언어의 기본개념에 대한 질문이다.</p>
<ul>
<li><code>오버라이딩 (Overriding)</code> : 부모 클래스에서 정의한 메서드를 자식 클래스에서 재정의하는 것</li>
<li><code>오버로딩 (Overloading)</code> : 같은 이름의 메서드를 매개변수의 개수나 타입이 다르게 여러 개 정의하는 것.</li>
</ul>
<pre><code class="language-java">
// 오버라이딩 (Overriding)
class Parent {
    void sayHello() {
        System.out.println(&quot;Hello from Parent&quot;);
    }
}

class Child extends Parent {
    @Override
    void sayHello() {
        System.out.println(&quot;Hello from Child&quot;);
    }
}</code></pre>
<pre><code class="language-java">
// 오버로딩 (Overloading)
class Printer {
    void print(String s) {
        System.out.println(s);
    }

    void print(int n) {
        System.out.println(n);
    }

    void print(String s, int n) {
        System.out.println(s + &quot; &quot; + n);
    }
}</code></pre>
<p><strong><code>오버로딩</code></strong>은 메서드 이름은 같고, 매개변수가 달라야 한다. 리턴 타입은 같을 수도 있고, 다를 수도 있으나, 리턴타입만 다르면 오버로딩이 되지 않고 같은 메서드로 인식하여 중복선언 에러가 뜨게 된다. <strong>동일 메서드명으로, 다양한 입력을 처리할 수 있게 하기 위해 사용된다.</strong></p>
<p><strong><code>오버라이딩</code></strong>은 메서드 이름은 같고, 매개변수도 같아야 하고, 리턴타입도 동일하거나 하위타입이어야 한다.</p>
<p><strong><code>오버라이딩</code></strong>은 <strong><code>오버로딩</code></strong>과 달리, 상속관계가 필수적으로 요구된다. <strong>상위 클래스의 기능을 바꾸기 위한 목적으로 사용되기 때문이다.</strong> 이 때, Spring에서는 오버라이딩하는 메서드에 <code>@Override</code> 어노테이션을 붙여준다.</p>
<p>오버라이딩과 오버로딩은 이와같이 개념부터 다르고, 목적성의 분명한 차이를 보인다.</p>
</br>
</br>


<h4 id="span-stylebackground-color-rgba242179188053-jvm이란-무엇이고-왜-필요한지-설명해-보세요span"><span style="background-color: rgba(242,179,188,0.5)">3) JVM이란 무엇이고 왜 필요한지 설명해 보세요.</span></h4>
<p><a href="https://yummy0102.tistory.com/510">이 링크 정리 좋다 !</a></p>
<ul>
<li><code>JVM (Java Virtual Machine)</code> : <strong>Java 바이트코드(.class)</strong>를 실행하는 가상 머신</li>
</ul>
<p>즉, 자바로 작성된 코드를 실행할 수 있도록 만들어진 소프트웨어를 뜻한다.</p>
<p><strong>⬇️ JVM의 필요성</strong></p>
<table>
<thead>
<tr>
<th>이유</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>운영체제 독립성</strong></td>
<td>&quot;Write Once, Run Anywhere&quot; → 같은 코드가 <strong>Windows, Mac, Linux 등 어디서든 실행</strong> 가능</td>
</tr>
<tr>
<td><strong>바이트코드 실행</strong></td>
<td>자바 컴파일러는 <code>.java</code>를 <code>.class</code> 바이트코드로 바꾸고, JVM이 그걸 읽고 실행</td>
</tr>
<tr>
<td><strong>자동 메모리 관리 (GC)</strong></td>
<td>JVM이 <strong>Garbage Collection</strong>을 통해 <strong>더 이상 쓰지 않는 메모리 자동 해제</strong></td>
</tr>
<tr>
<td><strong>보안 기능</strong></td>
<td>클래스 로더, 바이트코드 검증기 등으로 <strong>안정성과 보안성 제공</strong></td>
</tr>
<tr>
<td><strong>스택 기반 구조</strong></td>
<td>내부적으로 스택 구조로 동작 → 간단하고 효율적인 연산 가능</td>
</tr>
</tbody></table>
<p>VM은 <strong>자바 프로그램을 운영체제(OS)와 독립적으로 실행할 수 있도록 도와주며</strong>,메모리 관리(GC)와 보안까지 책임지는 자바 실행의 핵심 엔진이다.</p>
<p>Spring도 JVM 위에서 동작하게 된다.
</br></p>
<p><code>+) JVM의 구성요소</code></p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>클래스 로더 (Class Loader)</td>
<td>.class 파일을 JVM으로 로딩</td>
</tr>
<tr>
<td>실행 엔진 (Execution Engine)</td>
<td>바이트코드를 해석해서 실행</td>
</tr>
<tr>
<td>가비지 컬렉터 (GC)</td>
<td>사용하지 않는 객체 메모리 자동 해제</td>
</tr>
<tr>
<td>런타임 메모리 영역 (Heap, Stack 등)</td>
<td>프로그램 실행 중 필요한 메모리 공간 관리</td>
</tr>
<tr>
<td>네이티브 인터페이스 (JNI)</td>
<td>C/C++ 등 다른 언어와 연결 가능</td>
</tr>
</tbody></table>
</br>
</br>

<h4 id="span-stylebackground-color-rgba242179188054-java가-컴파일되는-과정에-대해-설명해-보세요span"><span style="background-color: rgba(242,179,188,0.5)">4) Java가 컴파일되는 과정에 대해 설명해 보세요.</span></h4>
<ol start="0">
<li><p><code>컴파일 단계</code> : 
개발자가 .java 파일 작성,
javac 컴파일러가 실행되어 .class 바이트코드 파일 생성됨.
이 단계는 JVM 이전 단계, 즉 JDK(Java Development Kit)의 역할 !</p>
<p><strong>⬇️ (JVM) 클래스 로딩 5 단계</strong></p>
</li>
<li><p><code>로드 과정</code> : 클래스 파일을 가져와 JVM의 메모리에 로드하는 과정
자바 소스 코드(.Java)가 작성되면 자바 컴파일러가 이 소스 코드를 읽어 바이트 코드(.class)로 컴파일한 뒤 이를 클래스 로더에게 전달.
클래스 로더는 동적 로딩으로 필요한 클래스들을 링크하여 JVM 메모리에 업로드해 런타임 데이터를 구성.</p>
<ol start="2">
<li><code>검증 과정</code> : 자바 언어 명세와 JVM 명세에 구성이 맞는지 검증</li>
<li><code>준비 과정</code> : 클래스가 필요로 하는 메모리를 할당</li>
<li><code>분석 과정</code> : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경</li>
<li><code>초기화 과정</code> :  클래스 변수들을 적절한 값으로 초기화</li>
</ol>
</li>
</ol>
<p>이러한 과정이 모두 완료되면, <strong>실행 엔진은 JVM 메모리에 업로드된 바이트 코드를 명령어 단위로 가져와 실행</strong>
실행 엔진의 2가지 작동 방식</p>
<ul>
<li>인터프리터 방식 :
  바이트 코드 명령어를 한 개씩 읽고 실행하는 방식.
  명령어별 실행은 빠르지만 전체적인 속도는 느리다는 단점 !</li>
<li>JIT 컴파일러 사용 방식 :
인터프리터 방식의 단점을 개선하기 위해 등장한 것이 JIT 컴파일러 사용 방식.
JIT 컴파일러는 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고, 해당 메서드를 바로 바이너리 코드로 실행.
-&gt; 전체적인 실행 속도가 인터프리터 방식보다 훨씬 빠르다 !!</li>
</ul>
</br>
</br>

<h4 id="span-stylebackground-color-rgba242179188055-클래스와-인스턴스의-차이에-대해-설명해-보세요span"><span style="background-color: rgba(242,179,188,0.5)">5) 클래스와 인스턴스의 차이에 대해 설명해 보세요.</span></h4>
<p>클래스(Class)와 인스턴스(Instance)는 객체 지향 프로그래밍에서 매우 중요한 개념이다.</p>
<ul>
<li><p><strong>클래스(Class)</strong> :
객체의 속성(attribute)과 동작(behavior)을 정의하며, 객체를 생성하기 위한 <strong>템플릿 역할</strong>.
객체를 생성하기 위한 일종의 설계도로서 여러 객체가 공유하는 특성을 정의
예를 들어, ‘핸드폰’ 클래스는 핸드폰 객체가 가져야 할 속성(색상, 모양, 모델 등)과 동작(터치, 전원 등)을 정의함</p>
</li>
<li><p><strong>인스턴스(Instance)</strong> :
클래스를 기반으로 생성된 객체
클래스가 설계도라면, 실제로 사용되는 데이터와 객체는 인스턴스 생성을 통해 만들어짐.
클래스의 속성과 동작을 <strong>실제 값</strong>으로 가지고 있으며, 각각 독립적으로 작동
예를 들어, ‘핸드폰’ 클래스에서 ‘삼성 갤럭시 S24’, ‘애플 아이폰 15’ 와 같은 여러 인스턴스를 생성 가능.</p>
</li>
</ul>
<p>=&gt; 클래스가 객체의 설계를 정의 -&gt; 인스턴스는 클래스에 따라 생성된 실제 객체</p>
<p>클래스에서 여러 인스턴스를 생성할 수 있고, 각 인스턴스는 클래스의 정의에 따라 각각 독립적인 데이터와 동작을 가지고 있다.
클래스는 정의만으로는 메모리에 없고, 인스턴스는 new 키워드 등으로 생성 시 메모리에 존재하게 된다.</p>
</br>

<hr>
</br>

<p>아쟈.
~ !! 💪🏻</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 #10951] java 문자열 입출력 씹.뜯.맛🦑 4탄 : 입력을 알아서 끝낸다고? 너 누군데? (feat. EOF)]]></title>
            <link>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-10951-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-4%ED%83%84-%EC%9E%85%EB%A0%A5%EC%9D%84-%EC%95%8C%EC%95%84%EC%84%9C-%EB%81%9D%EB%82%B8%EB%8B%A4%EA%B3%A0-%EB%84%88-%EB%88%84%EA%B5%B0%EB%8D%B0-feat.-EOF</link>
            <guid>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-10951-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-4%ED%83%84-%EC%9E%85%EB%A0%A5%EC%9D%84-%EC%95%8C%EC%95%84%EC%84%9C-%EB%81%9D%EB%82%B8%EB%8B%A4%EA%B3%A0-%EB%84%88-%EB%88%84%EA%B5%B0%EB%8D%B0-feat.-EOF</guid>
            <pubDate>Mon, 20 Jan 2025 09:43:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/7590c00c-c6f2-493c-b30c-60a1a76936a1/image.png" alt=""></p>
<p>예.</p>
<p>백준 10951번입니다. 참으로 간단해보이죠?
근데 함정이 있음.</p>
<br>

<p>🤔 🤔 ...</p>
<p>1️⃣ <strong>입력값의 개수</strong>가 지정되지 않음
2️⃣ <strong>입력을 종료키</strong>가 지정되지 않음 ( ex. &quot;@&quot;가 입력되면 입력을 멈춘다</p>
<p>한 마디로 <code>입력이 더 들어오지 않으면 알아서 종료</code>시켜야 채점이 된다는 거다
<br>
위 문제는 <span style="color: blue"><code>EOF 개념</code></span> 을 알아야 풀 수 있다
<br>
<br></p>
<hr>
<hr>
<h1 id="eof--end-of-file-✋🏻">EOF : End Of File ✋🏻</h1>
<p><strong>EOF(End of File)</strong>는 파일이나 입력 스트림에서 <strong>더 이상 읽을 데이터가 없음을 나타내는 신호</strong>야.<br>즉, <strong>입력이 끝났음을 의미하는 특별한 상태</strong>야. </p>
<hr>
<h2 id="✅-eof는-언제-발생할까">✅ <strong>EOF는 언제 발생할까?</strong></h2>
<ol>
<li><p><strong>파일을 읽을 때</strong><br>→ 파일의 끝까지 읽고 나면 EOF 발생.</p>
</li>
<li><p><strong>키보드 입력을 받을 때</strong><br>→ 사용자가 <strong>입력 종료 키</strong>를 누르면 EOF 발생.</p>
</li>
<li><p><strong>스트림 데이터를 읽을 때</strong><br>→ <strong>네트워크 소켓, 표준 입력(System.in)</strong> 같은 입력 스트림이 <strong>더 이상 데이터를 제공하지 않을 때</strong> EOF가 발생.</p>
</li>
</ol>
<hr>
<h2 id="✅-자바에서-eof-감지하는-방법">✅ <strong>자바에서 EOF 감지하는 방법</strong></h2>
<p>자바에서는 <strong>EOF를 직접 감지하는 기능이 없고</strong>, 보통 <strong><code>readLine()</code>이 <code>null</code>을 반환하면 EOF로 간주</strong>해.</p>
<hr>
<h2 id="🔥-1️⃣-bufferedreader로-eof-처리하기">🔥 <strong>1️⃣ <code>BufferedReader</code>로 EOF 처리하기</strong></h2>
<h3 id="📌-기본적인-while문을-사용한-eof-처리">📌 <strong>기본적인 <code>while</code>문을 사용한 EOF 처리</strong></h3>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;

        while ((line = br.readLine()) != null) { // EOF가 발생하면 null 반환
            System.out.println(&quot;입력된 값: &quot; + line);
        }
    }
}</code></pre>
<h3 id="🛠-실행-방법-터미널에서"><strong>🛠 실행 방법 (터미널에서)</strong></h3>
<pre><code class="language-bash">java Main</code></pre>
<p><strong>EOF를 입력하는 방법:</strong></p>
<ul>
<li><strong>Windows:</strong> <code>Ctrl + Z</code> 누르고 <code>Enter</code></li>
<li><strong>Mac/Linux:</strong> <code>Ctrl + D</code></li>
</ul>
<hr>
<h2 id="🔥-2️⃣-scanner의-hasnext를-사용한-eof-처리">🔥 <strong>2️⃣ <code>Scanner</code>의 <code>hasNext()</code>를 사용한 EOF 처리</strong></h2>
<p><code>Scanner</code>를 사용하면 <code>hasNext()</code>를 이용해 EOF를 감지할 수 있어.</p>
<pre><code class="language-java">import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        while (sc.hasNext()) { // EOF가 발생하면 false 반환
            String input = sc.nextLine();
            System.out.println(&quot;입력된 값: &quot; + input);
        }

        sc.close();
    }
}</code></pre>
<h3 id="📌-scannerhasnext를-사용할-때의-장점"><strong>📌 <code>Scanner.hasNext()</code>를 사용할 때의 장점</strong></h3>
<ul>
<li><code>hasNext()</code>는 <strong>파일 입력, 키보드 입력</strong> 모두에서 사용할 수 있어.</li>
<li><code>Scanner</code>를 이용하면 <strong>공백 단위 입력도 쉽게 처리 가능</strong>.</li>
</ul>
<hr>
<h2 id="✅-eof를-사용하는-이유">✅ <strong>EOF를 사용하는 이유</strong></h2>
<h3 id="📌-eof는-어떤-상황에서-유용할까">📌 <strong>EOF는 어떤 상황에서 유용할까?</strong></h3>
<ol>
<li><p><strong>입력 개수가 정해지지 않은 문제를 해결할 때</strong></p>
<ul>
<li>입력 개수가 주어지지 않은 문제에서 <strong>EOF까지 계속 읽어야 하는 경우</strong>.</li>
</ul>
</li>
<li><p><strong>파일을 다 읽었는지 확인할 때</strong></p>
<ul>
<li>파일 끝까지 읽었는지 확인하려면 EOF를 감지해야 함.</li>
</ul>
</li>
<li><p><strong>네트워크 소켓에서 데이터 스트림이 종료되었는지 확인할 때</strong></p>
<ul>
<li>네트워크 연결이 닫혔는지 감지할 때 사용.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="🎯-정리">🎯 <strong>정리</strong></h2>
<table>
<thead>
<tr>
<th><strong>입력 방식</strong></th>
<th><strong>EOF 감지 방법</strong></th>
</tr>
</thead>
<tbody><tr>
<td><code>BufferedReader</code></td>
<td><code>readLine() == null</code></td>
</tr>
<tr>
<td><code>Scanner</code></td>
<td><code>hasNext() == false</code></td>
</tr>
</tbody></table>
<p>💡 <strong>결론:</strong><br>자바에서는 EOF를 직접 감지하는 방법이 없지만,  </p>
<ul>
<li><strong><code>BufferedReader.readLine() == null</code></strong> 또는  </li>
<li><strong><code>Scanner.hasNext() == false</code></strong>  </li>
</ul>
<p>를 사용해서 EOF를 확인할 수 있어! 🚀
<br>
<br></p>
<hr>
<hr>
<h1 id="문제풀이-✏️">문제풀이 ✏️</h1>
<p>그렇다고 함.
<br></p>
<p>다음은 활용해서 쓴 나의 코드다.</p>
<p>속도가 느린 느린 <code>scanner()</code>를 쓰고싶지 않아서...
<br></p>
<pre><code class="language-java">while ((line = br.readLine()) != null) { // EOF를 감지하여 종료</code></pre>
<p><strong><code>BufferedReader</code></strong>를 활용해서 이렇게 <strong><code>EOF 감지 처리</code></strong> 를 해줌
<br>
<br></p>
<p>⬇️ <em>완성된 나의 코드</em></p>
<hr>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;

        while ((line = br.readLine()) != null) { // EOF를 감지하여 종료
            StringTokenizer st = new StringTokenizer(line);
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            System.out.println(a + b);
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 #2438] java 문자열 입출력 씹.뜯.맛🦑 3탄 : (출력) StringBuilder, 근데 이제 java11 String클래스를 곁들인]]></title>
            <link>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-2438-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-3%ED%83%84-%EC%B6%9C%EB%A0%A5-StringBuilder-%EA%B7%BC%EB%8D%B0-%EC%9D%B4%EC%A0%9C-java11-String%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%EA%B3%81%EB%93%A4%EC%9D%B8</link>
            <guid>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-2438-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-3%ED%83%84-%EC%B6%9C%EB%A0%A5-StringBuilder-%EA%B7%BC%EB%8D%B0-%EC%9D%B4%EC%A0%9C-java11-String%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%EA%B3%81%EB%93%A4%EC%9D%B8</guid>
            <pubDate>Thu, 16 Jan 2025 11:15:40 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/5d08042b-72fd-4e5e-b9fe-2c9f610be599/image.png" alt=""></p>
<p>문제가 참 카와이하다
나도 별 보러가고 싶어</p>
<br>

<p>아무튼 <strong>이중 for문 문제입니다</strong></p>
<br>

<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        for (int i = 1; i &lt;= n; i++) {
            for (int j = 1; j &lt;= i; j++) {
                System.out.print(&quot;*&quot;);
            }
            System.out.println();
        }
    }
}</code></pre>
<br>
라고 생각하고, 이것이 제일 먼저 내놓은 답변이었다

<br>
<br>

<p>그런데...
<code>print()</code> 로 이중for문 돌리는 게 너무 조잡하다는(??) 생각이 들었다
(시간도 124ms로 뭔가 못마땅함. 이중for문 너무 느림.)</p>
<br>
<span style="color: gray">그래서 어김없이 여러분들의 코드를 호로록쨥 하러 갔습니다 ~ 야호 !!</span>

<br>
<br>
<br>

<hr>
<br>

<h1 id="💗-stringbuilder">💗 StringBuilder</h1>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder(); // &lt;- 얘다 !!!!!!!
        for(int i = 1; i &lt;= n; i++) {
            for(int j = 1; j &lt;= i; j++) {
                sb.append(&quot;*&quot;);
            }
            sb.append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<br>

<p><code>StringBuilder()</code> <strong>!!!!!</strong>
이 친구 참 좋다.
시간도 확 줄고 메모리도 확 줄었다.</p>
<p>출력 카테고리에 완전히 넣기는 좀 애매하지만,
<strong>출력 이전에 전처리로 해주면 성능이 확 좋아진다</strong></p>
<br>

<p><code>쉽게 말해서 String을 맘대로 주무를 수 있는 함수다</code></p>
<p>String은 새로 대입하는게 아니고선 부분 수정이 안되는 아이라서...
알고 있다가 정말 편하게 다용도로 써먹을 수 있다</p>
<br>
<br>

<hr>
<hr>
<br>


<p><code>StringBuilder</code>는 <strong>문자열(String) 연산을 효율적으로 처리하기 위해 사용하는 클래스</strong>야.<br>자바에서 <code>String</code>은 <strong>불변(immutable)</strong>이라서 문자열을 수정할 때마다 <strong>새로운 객체를 생성</strong>하지만,<br><code>StringBuilder</code>는 <strong>가변(mutable) 객체</strong>로 동작해서 <strong>문자열을 직접 수정</strong>할 수 있어 <strong>성능이 훨씬 좋음</strong>. 🚀</p>
<hr>
<h2 id="✅-string-vs-stringbuilder-비교">✅ <strong><code>String</code> vs <code>StringBuilder</code> 비교</strong></h2>
<table>
<thead>
<tr>
<th>🔥 특징</th>
<th><code>String</code></th>
<th><code>StringBuilder</code></th>
</tr>
</thead>
<tbody><tr>
<td><strong>불변성(Immutable)</strong></td>
<td>✅ 변경 불가 (새 객체 생성)</td>
<td>❌ 변경 가능 (메모리 재사용)</td>
</tr>
<tr>
<td><strong>연산 속도</strong></td>
<td>느림 (새로운 객체 생성)</td>
<td>빠름 (기존 객체 수정)</td>
</tr>
<tr>
<td><strong>메모리 효율</strong></td>
<td>비효율적 (많은 객체 생성)</td>
<td>효율적 (하나의 객체 사용)</td>
</tr>
<tr>
<td><strong>쓰레드 안전(Thread Safe)</strong></td>
<td>✅ <code>String</code>은 안전</td>
<td>❌ <code>StringBuilder</code>는 안전하지 않음</td>
</tr>
<tr>
<td><strong>멀티스레드 환경</strong></td>
<td>가능</td>
<td><code>StringBuffer</code>를 대신 사용</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-stringbuilder-기본-사용법">✅ <strong><code>StringBuilder</code> 기본 사용법</strong></h2>
<h3 id="📌-1️⃣-stringbuilder-생성-방법">📌 <strong>1️⃣ <code>StringBuilder</code> 생성 방법</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(); // 빈 문자열 생성
StringBuilder sb2 = new StringBuilder(&quot;Hello&quot;); // 초기값 설정</code></pre>
<h3 id="📌-2️⃣-문자열-추가-append">📌 <strong>2️⃣ 문자열 추가 (<code>append()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.append(&quot; World&quot;);
System.out.println(sb); // 출력: Hello World</code></pre>
<h3 id="📌-3️⃣-문자열-삽입-insert">📌 <strong>3️⃣ 문자열 삽입 (<code>insert()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.insert(5, &quot; Java&quot;); // 5번째 위치에 &quot; Java&quot; 삽입
System.out.println(sb); // 출력: Hello Java</code></pre>
<h3 id="📌-4️⃣-문자열-변경-setcharat">📌 <strong>4️⃣ 문자열 변경 (<code>setCharAt()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.setCharAt(0, &#39;Y&#39;); // 첫 번째 문자 &#39;H&#39;를 &#39;Y&#39;로 변경
System.out.println(sb); // 출력: Yello</code></pre>
<h3 id="📌-5️⃣-문자열-삭제-delete--deletecharat">📌 <strong>5️⃣ 문자열 삭제 (<code>delete()</code> / <code>deleteCharAt()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello World&quot;);
sb.delete(5, 11); // &quot; World&quot; 삭제
System.out.println(sb); // 출력: Hello</code></pre>
<h3 id="📌-6️⃣-문자열-반전-reverse">📌 <strong>6️⃣ 문자열 반전 (<code>reverse()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.reverse();
System.out.println(sb); // 출력: olleH</code></pre>
<h3 id="📌-7️⃣-문자열-길이-length--용량-capacity">📌 <strong>7️⃣ 문자열 길이 (<code>length()</code>) &amp; 용량 (<code>capacity()</code>)</strong></h3>
<pre><code class="language-java">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
System.out.println(sb.length()); // 문자열 길이: 5
System.out.println(sb.capacity()); // 기본 용량: 16 + 문자열 길이</code></pre>
<hr>
<h2 id="✅-성능-비교-string-vs-stringbuilder">✅ <strong>성능 비교: <code>String</code> vs <code>StringBuilder</code></strong></h2>
<h3 id="🔥-string-연산-비효율적">🔥 <strong><code>String</code> 연산 (비효율적)</strong></h3>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        String str = &quot;&quot;;
        for (int i = 0; i &lt; 10000; i++) {
            str += i; // 새로운 문자열 객체 생성 (비효율적)
        }
        System.out.println(str.length());
    }
}</code></pre>
<ul>
<li>매번 새로운 문자열 객체가 생성됨 → <strong>메모리 낭비, 속도 느림</strong></li>
</ul>
<h3 id="🚀-stringbuilder-연산-효율적">🚀 <strong><code>StringBuilder</code> 연산 (효율적)</strong></h3>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &lt; 10000; i++) {
            sb.append(i); // 기존 객체 수정 (빠름)
        }
        System.out.println(sb.length());
    }
}</code></pre>
<ul>
<li><strong>기존 객체를 수정</strong>하므로 <strong>메모리 사용량이 적고 속도가 훨씬 빠름</strong>.</li>
</ul>
<hr>
<h2 id="✅-멀티스레드-환경에서-stringbuffer-사용">✅ <strong>멀티스레드 환경에서 <code>StringBuffer</code> 사용</strong></h2>
<ul>
<li><code>StringBuilder</code>는 <strong>쓰레드 안전하지 않음</strong>.</li>
<li>멀티스레드 환경에서는 <strong><code>StringBuffer</code></strong>를 사용해야 함.</li>
</ul>
<pre><code class="language-java">StringBuffer sb = new StringBuffer(&quot;Hello&quot;);
sb.append(&quot; World&quot;);
System.out.println(sb); // Hello World</code></pre>
<ul>
<li><code>StringBuffer</code>는 <code>StringBuilder</code>와 기능이 거의 동일하지만, <strong>synchronized(동기화)</strong> 처리가 되어 있어 멀티스레드 환경에서 안전함.</li>
</ul>
<hr>
<hr>
<br>
<br>
...

<p>그렇다고 한다.</p>
<p>근데 이 코드를 인텔리제이에 치니까,
얘가 코드를 여기서도 살짝 더 수정해보지 않겠냐는 거다</p>
<p>궁금해서 수정해봤다</p>
<br>

<hr>
<h1 id="💗-string클래스의-repeat">💗 String클래스의 repeat()</h1>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();
        for(int i = 1; i &lt;= n; i++) {
            sb.append(&quot;*&quot;.repeat(Math.max(0, i))); &lt;- 얘다 !!!!!!!
            sb.append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }
}</code></pre>
<br>

<p><strong>그랬더니 이중 for문이 사라졌다</strong></p>
<br>

<p>근데 java8에 돌리니 컴파일에러가 떴다...가 설마해서 11로 돌리니 돌아갔다</p>
<p>다음은 여기에 대한 지피티의 설명</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/9dfdc07e-022f-402d-ac86-ede75181d30c/image.png" alt=""></p>
<blockquote>
<p><strong>java11부터 가능하니 참고하도록 하자 !!</strong></p>
</blockquote>
<br>
코드가 짧고 간결하고 이중for문이 사라져서 빠르다

<p>최고 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 #8393] 코테에서 수학적 사고가 지름길이 된다면 " 성능이 개쩐다 "💢 (feat. 아는 공식)]]></title>
            <link>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-8393-%EC%BD%94%ED%85%8C%EC%97%90%EC%84%9C-%EC%88%98%ED%95%99%EC%A0%81-%EC%82%AC%EA%B3%A0%EA%B0%80-%EC%A7%80%EB%A6%84%EA%B8%B8%EC%9D%B4-%EB%90%9C%EB%8B%A4%EB%A9%B4-%EC%84%B1%EB%8A%A5%EC%9D%B4-%EA%B0%9C%EC%A9%90%EB%8B%A4-feat.-%EC%95%84%EB%8A%94-%EA%B3%B5%EC%8B%9D</link>
            <guid>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-8393-%EC%BD%94%ED%85%8C%EC%97%90%EC%84%9C-%EC%88%98%ED%95%99%EC%A0%81-%EC%82%AC%EA%B3%A0%EA%B0%80-%EC%A7%80%EB%A6%84%EA%B8%B8%EC%9D%B4-%EB%90%9C%EB%8B%A4%EB%A9%B4-%EC%84%B1%EB%8A%A5%EC%9D%B4-%EA%B0%9C%EC%A9%90%EB%8B%A4-feat.-%EC%95%84%EB%8A%94-%EA%B3%B5%EC%8B%9D</guid>
            <pubDate>Thu, 16 Jan 2025 10:58:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/78a231f6-64f2-4225-bbb0-53af352344af/image.png" alt=""></p>
<p>어, 그냥 i값 합치는 문제구나 .</p>
<p>백준 3단계는 <code>반복문 chapter</code> 이고 그래서 당연히 그렇게 풀었다
<br></p>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int sum = 0;
        for (int i = 1; i &lt;= n; i++) {
           sum += i;
        }
        System.out.println(sum);
    }
}</code></pre>
<br>

<p><img src="https://velog.velcdn.com/images/twi_twi/post/9f171e50-4110-4d79-88b2-580328d0e8a1/image.png" alt=""></p>
<p>근데 이 문구가 자꾸 눈에 밟힌거다 ...
<span style="color: lightgray">어케 푸는데...</span>
<br></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/2bffe707-d65e-4463-8e83-7c3cb4899570/image.png" alt=""></p>
<p>그래서 혹시해서 지피티언니한테 물어봤는데</p>
<p>엥. 제공되는 함수는 따로 없댄다.
그래서 냅다 여러분이 풀어두신 코드를 보러 갔습니다 0.&lt;</p>
<br>

<hr>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/6ab5eacd-c421-4f0d-81ea-2e2bacef85cd/image.png" alt=""></p>
<p>저는 68ms 나왔는데 여러분 역시 짱이지 말입니다</p>
<br>

<hr>
<br>

<h2 id="💬-와-수학-공식으로-한-줄-계산하고-끝">💬 와. 수학 공식으로 한 줄 계산하고 끝.</h2>
<br>
<br>


<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        System.out.println(n * (n + 1) / 2);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/b7a6ad71-1e52-4056-b835-198259563446/image.png" alt=""></p>
<p>ㅋㅋ
이과였던 제가 이걸 까먹었다니요</p>
<p><strong>64ms 로 줄어든다</strong>
<br>
<br>
for문은 계속 조건 검사하고, 똑같은거 작동하고
시간+메모리를 기본적으로 잡아먹기 때문에 ...</p>
<p><em><strong>자신있게 외운 공식이 있으면 안 쓸 이유가 없다</strong></em> ... 정말임 .....</p>
<br>

<hr>
<hr>
<br>

<h3 id="💯-60ms-성공하신-분의-코드는-이렇다">💯 60ms 성공하신 분의 코드는 이렇다</h3>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/f385f377-14ba-4208-9675-d6bef80e7f7f/image.png" alt=""></p>
<p><span style="color: lightgray">존경의 의미로 코드복붙 안하고 캡쳐했음</span>
일단 <code>삼항연산자</code>로 <code>n==1일 때</code> 처리를 따로 기깔나게 하셨는데,
저게 무슨 공식인지는 펜 들고 적어보지 않으면 모르겠다 ...
<br>
근데<code>BufferedReader</code>가 왜 static으로 저기에 있지 ...?
<br>
어라라..</p>
<p>추가로 공부를 해야겠다 생각이 들었다</p>
<p>다음 시간에 봐요</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 #15552] java 문자열 입출력 씹.뜯.맛🦑 2탄 : (출력) BufferedWriter는 한번에 출력해 (feat. 문자열 결합 규칙)]]></title>
            <link>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-15552-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-2%ED%83%84-%EC%B6%9C%EB%A0%A5-BufferedWriter%EB%8A%94-%ED%95%9C%EB%B2%88%EC%97%90-%EC%B6%9C%EB%A0%A5%ED%95%B4-feat.-%EB%AC%B8%EC%9E%90%EC%97%B4-%EA%B2%B0%ED%95%A9-%EA%B7%9C%EC%B9%99</link>
            <guid>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-15552-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B-2%ED%83%84-%EC%B6%9C%EB%A0%A5-BufferedWriter%EB%8A%94-%ED%95%9C%EB%B2%88%EC%97%90-%EC%B6%9C%EB%A0%A5%ED%95%B4-feat.-%EB%AC%B8%EC%9E%90%EC%97%B4-%EA%B2%B0%ED%95%A9-%EA%B7%9C%EC%B9%99</guid>
            <pubDate>Thu, 16 Jan 2025 10:44:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/606ce8f1-a8db-45b0-9954-8f4148dc3955/image.png" alt=""></p>
<p>값을 한번에 출력하는 문제다</p>
<blockquote>
<p>Java를 사용하고 있다면, Scanner와 System.out.println 대신 BufferedReader와 BufferedWriter를 사용할 수 있다. BufferedWriter.flush는 맨 마지막에 한 번만 하면 된다.</p>
</blockquote>
<p>가이드를 문제에서 친절하게 알려준다
그래서 써봤다 ..!</p>
<br>

<hr>
<br>

<h1 id="bufferedwriter">BufferedWriter</h1>
<pre><code class="language-java">import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int T = Integer.parseInt(br.readLine());

        for (int i = 0; i &lt; T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            bw.write((a+b) + &quot;\n&quot;);
        bw.flush();
        bw.close();
    }
}</code></pre>
<br>

<p>어김없이 등장한 나의 친구 <code>BufferedReader</code> , <code>StringTokenizer</code> ,,</p>
<p>근데 이제 <code>BufferedWriter</code>가 저 BR과 형제라고 보면 된다
<br>
<code>BufferedReader</code> ➡️ 입력
<code>BufferedWriter</code> ➡️ 출력
<br>
그러니 이 친구도 <strong>버퍼를 이용해서 출력</strong>하는,
비슷한 매커니즘을 통해 출력하는 그런 친구인거다
<a href="https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-1000%EB%B2%88-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B">((클릭해서 BufferedReader 개념 한번 더 보고오자))</a>
<br>
근데 <strong>버퍼를 사용하여 한 줄씩 입력데이터를 받아오는 <code>BufferedReader</code></strong> 와 달리 ,
<strong><code>BufferedWriter</code>는 데이터를 버퍼에 저장</strong>하는 방식이라,
저장된 데이터를 다 썼으면 필요 없어진 버퍼메모리를 신경써야 한다는 점 !
<br>
<span style="color: lightgray">우리 챗지피티 선생님의 정리를 한 번 보자</span></p>
<br>

<hr>
<hr>
<br>

<h3 id="🔍-bufferedwriter란">🔍 <strong>BufferedWriter란?</strong></h3>
<p><code>BufferedWriter</code>는 <strong>자바에서 제공하는 출력 스트림</strong> 중 하나로, 데이터를 <strong>버퍼에 저장</strong>하여 출력 속도를 높이는 데 사용돼요. 출력할 내용을 <strong>임시로 메모리에 저장</strong>한 후, 필요 시 한꺼번에 출력하므로 성능이 향상돼요.</p>
<hr>
<h3 id="📌-bufferedwriter의-특징">📌 <strong>BufferedWriter의 특징</strong></h3>
<ol>
<li><strong>버퍼링 사용</strong>: 데이터를 한 번에 모아서 출력 → 효율적인 출력 가능.</li>
<li><strong>문자 기반 스트림</strong>: 텍스트 데이터를 처리할 때 사용.</li>
<li><strong><code>write()</code> 메서드로 출력</strong>: 데이터를 출력 스트림에 씀.</li>
<li><strong><code>flush()</code> 메서드로 강제 출력</strong>: 버퍼에 남아 있는 데이터를 강제로 출력.</li>
<li><strong><code>close()</code> 메서드</strong>: 스트림을 닫아 리소스를 해제.</li>
</ol>
<hr>
<h3 id="✅-bufferedwriter-사용법">✅ <strong>BufferedWriter 사용법</strong></h3>
<h4 id="기본-문법"><strong>기본 문법:</strong></h4>
<pre><code class="language-java">BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));</code></pre>
<ul>
<li><strong><code>System.out</code></strong>: 콘솔에 출력.</li>
<li><strong><code>OutputStreamWriter</code></strong>: 바이트 스트림 → 문자 스트림 변환.</li>
</ul>
<h4 id="예제-1-기본-사용"><strong>예제 1: 기본 사용</strong></h4>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        bw.write(&quot;Hello, World!&quot;);
        bw.newLine();  // 줄바꿈
        bw.write(&quot;BufferedWriter 예제입니다.&quot;);
        bw.flush();    // 버퍼의 내용을 강제 출력
        bw.close();    // 스트림 닫기
    }
}</code></pre>
<hr>
<h3 id="✅-파일-출력에-사용">✅ <strong>파일 출력에 사용</strong></h3>
<p><code>BufferedWriter</code>는 파일에도 데이터를 쓸 수 있어요. <strong><code>FileWriter</code></strong>와 함께 사용하면 파일에 효율적으로 텍스트를 저장할 수 있어요.</p>
<h4 id="예제-2-파일-출력"><strong>예제 2: 파일 출력</strong></h4>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(&quot;output.txt&quot;));

        bw.write(&quot;Hello, file!&quot;);
        bw.newLine();  // 줄바꿈
        bw.write(&quot;This is a BufferedWriter example.&quot;);
        bw.flush();    // 파일에 강제 쓰기
        bw.close();    // 스트림 닫기
    }
}</code></pre>
<ul>
<li><strong>파일 이름</strong>: <code>&quot;output.txt&quot;</code>  </li>
<li><strong>내용</strong>:<pre><code>Hello, file!
This is a BufferedWriter example.</code></pre></li>
</ul>
<hr>
<h3 id="✅-반복-출력">✅ <strong>반복 출력</strong></h3>
<p><code>BufferedWriter</code>는 반복적으로 데이터를 출력할 때도 효율적으로 사용할 수 있어요.</p>
<h4 id="예제-3-반복-출력"><strong>예제 3: 반복 출력</strong></h4>
<pre><code class="language-java">import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        for (int i = 1; i &lt;= 5; i++) {
            bw.write(&quot;Line &quot; + i);
            bw.newLine();
        }
        bw.flush();
        bw.close();
    }
}</code></pre>
<p><strong>출력 결과:</strong></p>
<pre><code>Line 1
Line 2
Line 3
Line 4
Line 5</code></pre><hr>
<h3 id="📝-bufferedwriter의-주요-메서드">📝 <strong>BufferedWriter의 주요 메서드</strong></h3>
<table>
<thead>
<tr>
<th><strong>메서드</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td><code>write(String)</code></td>
<td>문자열 출력.</td>
</tr>
<tr>
<td><code>write(char[])</code></td>
<td>문자 배열 출력.</td>
</tr>
<tr>
<td><code>newLine()</code></td>
<td>줄바꿈 문자 출력.</td>
</tr>
<tr>
<td><code>flush()</code></td>
<td>버퍼에 남아 있는 데이터를 강제로 출력.</td>
</tr>
<tr>
<td><code>close()</code></td>
<td>스트림을 닫고 리소스 해제.</td>
</tr>
</tbody></table>
<hr>
<h3 id="🎯-bufferedwriter-vs-systemoutprintln">🎯 <strong>BufferedWriter vs System.out.println</strong></h3>
<table>
<thead>
<tr>
<th><strong>항목</strong></th>
<th><strong>BufferedWriter</strong></th>
<th><strong>System.out.println</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>속도</strong></td>
<td>빠름 (버퍼 사용)</td>
<td>느림 (매번 즉시 출력)</td>
</tr>
<tr>
<td><strong>줄바꿈</strong></td>
<td><code>newLine()</code> 메서드 사용</td>
<td>자동 줄바꿈</td>
</tr>
<tr>
<td><strong>사용 목적</strong></td>
<td>대량의 데이터를 출력하거나 파일에 저장할 때</td>
<td>간단한 콘솔 출력</td>
</tr>
</tbody></table>
<hr>
<h3 id="💬-언니의-팁">💬 <strong>언니의 팁!</strong></h3>
<ul>
<li><strong>대량의 데이터 출력</strong>: <code>BufferedWriter</code>가 적합.</li>
<li>파일 출력 시 <strong>효율적</strong>으로 데이터를 저장하려면 <code>BufferedWriter</code>를 꼭 사용해 봐! 😊</li>
</ul>
<br>

<hr>
<hr>
<br>

<h2 id="추가로-씹뜯맛">추가로 씹뜯맛</h2>
<br>

<pre><code class="language-java">for (int i = 0; i &lt; T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            bw.write((a+b) + &quot;\n&quot;);   // &lt;- 여기 !!!!!!!!!</code></pre>
<br>
이 부분 말이다 ...
<br>
<br>
<br>

<p><code>int a, b</code> 를 합산한 값을 출력해야 하는데
<br></p>
<pre><code class="language-java">bw.write(a + b);</code></pre>
<p>그냥 이렇게 해버리면 a=1, b=1일 때 <strong><code>a + b =11</code></strong> 이라는 놀라운 결과가 나온다 !</p>
<br>
<br>

<blockquote>
<p><em>왜냐면 <code>BufferedWriter.write()</code> 메서드는 문자열만 출력하기 때문이다</em></p>
</blockquote>
<p>println()과 달리 !</p>
<p>자동 내부 연산을 한 게 아니라 a , b 를 각각 문자열로 출력한거다
그래서 <code>a + b</code> 의 <strong>Int 값이 String이 되기 전에 먼저 계산해야 함</strong>
<br></p>
<hr>
<h3 id="방법-1️⃣--java의-문자열-결합-규칙-쓰기-간단">방법 1️⃣ : java의 문자열 결합 규칙 쓰기 (간단)</h3>
<pre><code class="language-java">bw.write((a + b) + &quot;\n&quot;);</code></pre>
<p>내가 제출한 코드처럼 <code>괄호</code>를 사용하는 방법이 있다
제일 간단하고 쉽다 ...
<br>
java는 <strong>제일 속에 있는 괄호부터 계산</strong>하기 때문에,
속괄호 내에서 아직 int 상태인 a, b를 계산해두는 거다</p>
<p>그러면 출력값으로 2가 제대로 나온다 !
<br></p>
<hr>
<h3 id="방법-2️⃣-string-변환-함수-쓰기-명시적">방법 2️⃣ :String 변환 함수 쓰기 (명시적)</h3>
<pre><code class="language-java">bw.write(String.valueOf(a + b) + &quot;\n&quot;);</code></pre>
<p><strong>String 변환 함수</strong>를 써서
명시적으로 함수 인자는 계산된 Int, 결과는 String으로 바꿔서 나오도록 쓸 수 있다
<br>
하지만 굳이 싶다</p>
<p>문자열 결합 규칙을 알면 훨씬 간단하다 !
<br></p>
<p><span style="color: lightgray">기초를 잘 배우도록 하자 0.&lt;</span>
<br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 #1000] java 문자열 입출력 씹.뜯.맛🦑 1탄 : (입력) BufferedReader와 StringTokenizer는 친구입니다 (feat. split())]]></title>
            <link>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-1000%EB%B2%88-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B</link>
            <guid>https://velog.io/@twi_twi/%EB%B0%B1%EC%A4%80-1000%EB%B2%88-java-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%94%B9.%EB%9C%AF.%EB%A7%9B</guid>
            <pubDate>Tue, 14 Jan 2025 11:58:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/twi_twi/post/faa9198f-de40-4317-9bfe-ae67a5b16a51/image.png" alt=""></p>
<p>매우 기본적인 문자열 입력받아 콘솔창에 출력하기 문제이다
scanner가 익숙하다면 문제 보자마자
가장 먼저 생각나는 것은 아래 코드일 수 밖에 없다</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/6574de1d-abdf-4ab8-94e3-37cada978e58/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/f1d5d068-1b40-4291-ae15-921cfb6afb7a/image.png" alt=""></p>
<p>이렇게 나옴.</p>
<p>하지만 우리들은 저 <strong>[메모리]</strong>와 <strong>[시간]</strong>을 신경 안 쓸 수가 없다</p>
<p>그래서 다른 사람들이 먼저 <strong>시간</strong>을 어떻게 줄였는가.. 보면
바로 등장하는 게 BufferedReader와 StringTokenizer다</p>
<br>

<hr>
<h1 id="bufferedreader">BufferedReader</h1>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/a2f7d87b-a66c-4242-b5c3-b19089930eb0/image.png" alt=""></p>
<p>요약하자면 BufferedReader 클래스는 이렇다</p>
<ol>
<li>먼저 <strong>[입력 속도를 향상]</strong> 시킴으로서 시간 단축을 시켜주는 클래스</li>
<li>문자열로 입력 받고, 한 줄씩 받아오기 때문에 입력값에 후처리가 필요함</li>
</ol>
<p>그렇다면 후처리는 어떻게 들어가나요 ?
할 때 나오는 게 StringTokenizer다</p>
<br>

<h1 id="stringtokenizer">StringTokenizer</h1>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/b4919990-afa5-4114-8fb5-6041b7962423/image.png" alt=""></p>
<p>토큰 단위로 자잘자잘히 문자열을 나누는 클래스,
토큰 단위이기 때문에 <strong>[공백, 쉼표같은 한 글자짜리 구분자]</strong>로 나눈다</p>
<p>이 BufferedReader와 StringTokenizer를 함께 사용하면
<strong>입력값이 많고 복잡한 문제일 수록 속도가 눈에 띄게 향상된다</strong></p>
<br>
<br>
<br>
백준 1000번의 경우 둘을 써서 이렇게 코드를 짜볼 수 있음

<p><img src="https://velog.velcdn.com/images/twi_twi/post/b8a0ae5d-0b27-4bba-aa26-7101cc7c7e55/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/5796e8af-8b8b-4fb3-8eb3-848f126c714a/image.png" alt=""></p>
<p>시간이 훅 줄어드는 매직 ...
코테에서 이 둘은 그냥 외워뒀다가 일단 적어두고 시작하는 게 맞다</p>
<br>

<hr>
<h3 id="그런데-문자열-나누는-것-하면-생각나는-게-있다">그런데 문자열 나누는 것 하면 생각나는 게 있다</h3>
<p><code>우리... split 함수 좋아하지 않았나요 ?</code></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/fd159f4f-1032-4569-99d7-693a04262fd1/image.png" alt=""></p>
<p>얘는 토큰 단위가 아닌, 복잡한 문자열도 구분자로 설정이 된다
split()을 사용해서 문제를 풀어보자</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/df949c48-6a84-4946-b040-768a29e79a08/image.png" alt="">
<img src="https://velog.velcdn.com/images/twi_twi/post/360c2502-90da-422f-b7a2-66248a26db3a/image.png" alt=""></p>
<p>큰 차이가 없어 보이는 결과인데,
토큰 단위로 나누는 게 처리 속도가 더 빠르지 않을까 싶어서 물어봤더니</p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/35bba204-f1cb-49fb-af26-7c64da59f932/image.png" alt=""></p>
<p>(정말 최고애 GPT ...)</p>
<p><strong>속도</strong>면에서도, <strong>남은 토큰 수 계산</strong>해야 할 상황에서도
코테 상황에서 StringTokenizer를 안 쓸 이유가 없다</p>
<br>
다시 한 번 ...<br>

<blockquote>
<p>코테에서 BufferedReader와 StringTokenizer는 무조건 갖고 가자</p>
</blockquote>
<br>
<br>

<hr>
<hr>
<br>

<h2 id="추가로-씹뜯맛">추가로 씹뜯맛</h2>
<p>난 코테가 아주 익숙한 사람은 아니지만 ...
코테를 풀면 꼭 시간, 메모리 순위별로 다른 사람 코드를 훔쳐보는 버릇이 있음</p>
<p>왜냐면 정말 미친(positive) 사람 많기 때문이다
<span style="color:lightgray">그리고 그 사람들이 쓴 로직이나 꿀클래스를 흡수하면 짜릿하기 때문임</span>
<br>
백준 1000번 문제는 매우매우 간단하지만,
그렇기에 어떻게 시간과 메모리를 더 줄였는지가 더 궁금했다
<span style="color:lightgray"></span></p>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/9d8b27ec-483a-4dcc-a657-5bc9cd06be8f/image.png" alt=""></p>
<p>순위권 분들 다들 64ms 걸리시고, 메모리도 적으셔서 코드를 호록쟙쟙 보았다
<br></p>
<pre><code class="language-java">// 이것이 그 코드
import java.io.*;
import java.util.*;

public class Main{
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line = br.readLine();
        StringTokenizer st = new StringTokenizer(line, &quot; &quot;);
        int a = Integer.parseInt(st.nextToken());
        int b = Integer.parseInt(st.nextToken());
        System.out.println(a + b);
    }
}</code></pre>
<hr>
<h3 id="예외처리">예외처리</h3>
<p>얼핏 보기엔 내 코드와 비슷해보이지만 보고 아차 한 것이 있었다</p>
<blockquote>
<p>public static void main(String[] args) <strong>throws Exception</strong>{</p>
</blockquote>
<p><code>BufferedReader</code> ,  <code>Integer.parseInt()</code> 같은,
<strong>특히 입출력을 다루는 애들은</strong> 오류 발생을 대비해 예외처리가 필요하다는 점.. 을 까먹고 있었다</p>
<p>실전 코딩이라면 try-catch를 사용하겠지만,
코테와 같은 (실제 프로젝트가 아닌) 문제풀이에서는 예외 던지기를 활용하자</p>
<br>
<br>

<hr>
<h4 id="그-외에-시간-메모리가-적은-이유">그 외에 시간, 메모리가 적은 이유</h4>
<p><img src="https://velog.velcdn.com/images/twi_twi/post/44c648b4-2c04-4c2e-a894-665af72686c0/image.png" alt=""></p>
<p>예외처리를 넣은 해당 코드로 다시 돌려본 것이 아래의 104ms,
예외처리가 없을 때와 시간은 같다</p>
<p>68ms가 어떻게 나오나 했는데
java8과 java11의 차이였다</p>
<br>
일단 궁금하니까 물어봤다
...

<p><img src="https://velog.velcdn.com/images/twi_twi/post/1b0c07fe-cc44-4d95-830e-2642b3e591c9/image.png" alt=""></p>
<p>이 코드 한정인 것 같다</p>
<p>그만 알아보자 👌🏻</p>
]]></description>
        </item>
    </channel>
</rss>