<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>과로사한 공돌이</title>
    <link>https://pirograming.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 6 Jun 2026 17:52:09 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>과로사한 공돌이</managingEditor>
    <image>
      <title>과로사한 공돌이</title>
      <url>https://tistory1.daumcdn.net/tistory/4357984/attach/311721c749b94f4e92068928663c9928</url>
      <link>https://pirograming.tistory.com</link>
    </image>
    <item>
      <title>[LeetCode] 124 Binary Tree Maximum Path Sum(Java)</title>
      <link>https://pirograming.tistory.com/78</link>
      <description>&lt;h2 data-path-to-node=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 설명&lt;/h2&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://leetcode.com/problems/binary-tree-maximum-path-sum/&quot; data-ved=&quot;0CAAQ_4QMahgKEwjcz9Sp-62UAxUAAAAAHQAAAAAQvgI&quot; data-hveid=&quot;0&quot;&gt;LeetCode 124 - Binary Tree Maximum Path Sum&lt;/a&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;51&quot; data-path-to-node=&quot;2&quot;&gt;요약&lt;/b&gt;: 이진 트리의 노드들을 연결하는 경로 중 &lt;b data-index-in-node=&quot;77&quot; data-path-to-node=&quot;2&quot;&gt;노드 값들의 합이 최대가 되는 경로&lt;/b&gt;를 찾는 문제입니다. 경로는 반드시 루트를 지날 필요가 없으며, 어느 노드에서 시작하여 어느 노드에서 끝나도 상관없지만 경로 내 노드는 중복될 수 없습니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 아이디어&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 트리의 각 노드를 '경로의 정점(Peak)'으로 가정하고 재귀(DFS)를 통해 상향식(Bottom-up)으로 해결합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;독립적인 경로 vs 연장 가능한 경로&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 노드에서 부모 노드로 전달할 값은 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;5,0,1,0,0&quot;&gt;'해당 노드와 왼쪽 자식 중 큰 값'&lt;/b&gt; 또는 '해당 노드와 오른쪽 자식 중 큰 값'이어야 합니다. (한 갈래로만 연결 가능)&lt;/li&gt;
&lt;li&gt;반면, 현재 노드를 정점으로 삼는 최대 경로는 '왼쪽 자식 + 현재 노드 + 오른쪽 자식'의 합으로 계산됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;음수 값 처리&lt;/b&gt;: 만약 자식 노드로부터 올라온 경로의 합이 음수라면, 차라리 해당 경로를 포함하지 않는 것이 이득입니다. 따라서 Math.max(sum, 0) 처리를 통해 음수 합을 차단합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;전역 변수 활용&lt;/b&gt;: 재귀적으로 모든 노드를 방문하면서, 각 노드를 정점으로 하는 최대 경로 합을 전역 변수 max에 계속 갱신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjcz9Sp-62UAxUAAAAAHQAAAAAQvwI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;Java&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;class Solution {
    int max = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        calcSum(root);
        return max;
    }

    private int calcSum(TreeNode node) {
        if (node == null) return 0;

        int leftSum = calcSum(node.left);
        int rightSum = calcSum(node.right);

        int currMax = Math.max(leftSum, rightSum) + node.val;
        max = Math.max(max, leftSum + node.val + rightSum);

        return Math.max(currMax, 0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;Math.max(calcSum, 0)&lt;/b&gt;: 트리의 노드 값에 음수가 포함될 수 있기 때문에 매우 중요한 로직입니다. 자식 노드의 경로 합이 -5라면 이를 더하는 것보다 무시하는 것이 전체 합을 키우는 방법입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;역할의 분리&lt;/b&gt;: calcSum 함수는 두 가지 일을 동시에 수행합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;9,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전역 변수 max에는 현재 노드를 중심으로 하는 &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;9,1,1,0,0&quot;&gt;완성된 형태의 경로 합&lt;/b&gt;을 갱신합니다.&lt;/li&gt;
&lt;li&gt;함수 반환값으로는 부모 노드와 연결될 수 있는 &lt;b data-index-in-node=&quot;26&quot; data-path-to-node=&quot;9,1,1,1,0&quot;&gt;최대 한 갈래의 합&lt;/b&gt;을 전달합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;초기값 Integer.MIN_VALUE&lt;/b&gt;: 모든 노드의 값이 음수일 수 있으므로 max를 0이 아닌 가장 작은 정수 값으로 설정해야 정확한 비교가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size26&quot;&gt;5. 성능 및 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 모든 노드를 한 번씩 방문하므로 &lt;span data-index-in-node=&quot;26&quot; data-math=&quot;O(N)&quot;&gt;$O(N)$&lt;/span&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 트리의 높이를 &lt;span data-index-in-node=&quot;16&quot; data-math=&quot;H&quot;&gt;$H$&lt;/span&gt;라고 할 때, 재귀 호출 스택의 깊이에 따라 &lt;span data-index-in-node=&quot;42&quot; data-math=&quot;O(H)&quot;&gt;$O(H)$&lt;/span&gt;를 사용합니다. 최악의 경우(편향 트리) $O(N)$이 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;6. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;이진 트리에서의 최대 경로는 &quot;현재 노드를 경로에 포함할 것인가?&quot;와 &quot;포함한다면 부모로 무엇을 넘겨줄 것인가?&quot;를 결정하는 것이 핵심입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;이번 문제는 하드로 분류되어 있지만 문제 수준은 이지로 봐도 무방할듯 합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;궁금한 점이 있다면 언제든 알려주세요!&lt;/p&gt;</description>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/78</guid>
      <comments>https://pirograming.tistory.com/78#entry78comment</comments>
      <pubDate>Sun, 10 May 2026 14:20:07 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘 기법] 백트랙킹(Back-Tracking)</title>
      <link>https://pirograming.tistory.com/77</link>
      <description>&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;1. 백트래킹이란?&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백트래킹(Back-Tracking)&lt;/b&gt;은 모든 경우의 수를 탐색하는 브루트포스(Brute Force)와 유사하지만, 탐색 과정에서 '답이 될 가능성'을 실시간으로 판단한다는 점에서 차이가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;순차적 생성&lt;/b&gt;: 답을 한 번에 완성하는 것이 아니라, 여러 단계(Step)에 걸쳐 순차적으로 답을 만들어 나갑니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;가지치기(Pruning)&lt;/b&gt;: 현재 단계에서 다음 단계로 넘어갈 때, 해당 경로가 답으로 갈 수 없다고 판단되면 더 이상 탐색하지 않고 즉시 뒤로 돌아갑니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;효율성&lt;/b&gt;: 가지치기를 통해 오답 경로에 소모되는 리소스 낭비를 획기적으로 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;2. 구현 방식&lt;/h2&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;백트래킹은 주로 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;7&quot;&gt;DFS &lt;/b&gt;형식을 빌려 재귀적으로 구현합니다. 유효한 상태를 찾아 깊게 탐색하다가, 막다른 길에 다다르면 이전 상태로 복귀(Back)하는 것이 핵심입니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드 (N-Queens 예시)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;가장 대표적인 백트래킹 문제인 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;9&quot;&gt;N-Queens&lt;/b&gt;를 통해 실제 구현 모습을 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://pirograming.tistory.com/76&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pirograming.tistory.com/76&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1773902305848&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[LeetCode] 51 N-Queens(Java)&quot; data-og-description=&quot;1. 문제 설명문제 링크: LeetCode 51 - N-Queens요약: $n \times n$ 체스판에 $n$개의 퀸을 서로 공격할 수 없도록 배치하는 모든 경우의 수를 찾는 문제입니다. 퀸은 같은 행, 열, 그리고 양방향 대각선 위에&quot; data-og-host=&quot;pirograming.tistory.com&quot; data-og-source-url=&quot;https://pirograming.tistory.com/76&quot; data-og-url=&quot;https://pirograming.tistory.com/76&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Sx3MN/dJMb8VNurJa/WmGKb8u1DIkS99Iup4vlPk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bnQEpT/dJMb8QelgSp/JewwHwKi5KX1D4A3RaK5Zk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://pirograming.tistory.com/76&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pirograming.tistory.com/76&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Sx3MN/dJMb8VNurJa/WmGKb8u1DIkS99Iup4vlPk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bnQEpT/dJMb8QelgSp/JewwHwKi5KX1D4A3RaK5Zk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[LeetCode] 51 N-Queens(Java)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 문제 설명문제 링크: LeetCode 51 - N-Queens요약: $n \times n$ 체스판에 $n$개의 퀸을 서로 공격할 수 없도록 배치하는 모든 경우의 수를 찾는 문제입니다. 퀸은 같은 행, 열, 그리고 양방향 대각선 위에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pirograming.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot; data-hveid=&quot;0&quot; data-ved=&quot;0CAAQhtANahcKEwjGlsq0iqmTAxUAAAAAHQAAAAAQcw&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;class Solution {
    List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; ret;
    public List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; solveNQueens(int n) {
        ret = new ArrayList&amp;lt;&amp;gt;();
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens = new ArrayList&amp;lt;&amp;gt;();
        recall(n, queens, 0);

        return ret;
    }

    private void recall(int n, List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens, int row) {
        // 1. 기저 조건: 모든 행에 퀸을 다 배치했을 때
        if (row &amp;gt;= n &amp;amp;&amp;amp; queens.size() == n) {
            List&amp;lt;String&amp;gt; sl = getStrings(n, queens);
            ret.add(sl);
            return;
        }
        
        // 2. 현재 행(row)에서 각 열(col)에 퀸을 놓아보기
        for (int col = 0; col &amp;lt; n; col++) {
            if (isPosable(queens, row, col)) {
                queens.add(List.of(row, col)); // 퀸 배치
                recall(n, queens, row + 1);    // 다음 행으로 이동
                queens.removeLast();           // 백트래킹 (원태 복귀)
            }
        }
    }

    // 퀸의 위치 리스트를 문제에서 요구하는 문자열 리스트 포맷으로 변환
    private static List&amp;lt;String&amp;gt; getStrings(int n, List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens) {
        List&amp;lt;String&amp;gt; sl = new ArrayList&amp;lt;&amp;gt;();
        for (int row = 0; row &amp;lt; queens.size(); row++) {
            List&amp;lt;Integer&amp;gt; queen = queens.get(row);
            StringBuilder sb = new StringBuilder();
            for (int col = 0; col &amp;lt; n; col++) {
                if (queen.get(0) == row &amp;amp;&amp;amp; queen.get(1) == col) {
                    sb.append('Q');
                } else {
                    sb.append('.');
                }
            }
            sl.add(sb.toString());
        }
        return sl;
    }

    // 현재 위치 (row, col)에 퀸을 놓을 수 있는지 체크
    private boolean isPosable(List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens, int row, int col) {
        for (List&amp;lt;Integer&amp;gt; queen : queens) {
            if (queen.get(1) == col || // 같은 열 체크
                    queen.get(0) + queen.get(1) == row + col || // 대각선 / 체크
                    queen.get(0) - queen.get(1) == row - col)   // 대각선 \ 체크
                return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;recall 메서드&lt;/b&gt;: 재귀적으로 호출되며 답을 찾아 나가는 핵심 엔진입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;기저 조건(Base Case)&lt;/b&gt;: 모든 행(row)에 퀸 배치를 완료하여 queens.size() == n이 되면, 유효한 정답을 하나 찾은 것으로 간주하고 결과 리스트에 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,2,0&quot;&gt;의사 결정과 복구&lt;/b&gt;: queens.add()를 통해 퀸을 배치해보고, 다음 행으로 넘어갔다가 돌아온 후에는 queens.removeLast()를 호출합니다. 이는 이전의 선택을 취소하고 &lt;b&gt;'깨끗한 상태'&lt;/b&gt;에서 다음 열을 시도하기 위함입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,3,0&quot;&gt;탐색의 흐름&lt;/b&gt;: 한 행에서 어떤 열에도 퀸을 놓을 수 없는 상황이 오면 재귀가 자연스럽게 종료되며 이전 행으로 돌아갑니다. 이것이 바로 백트래킹의 &lt;b data-index-in-node=&quot;81&quot; data-path-to-node=&quot;12,3,0&quot;&gt;자동 가지치기&lt;/b&gt; 메커니즘입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;5. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;백트래킹은 &lt;b&gt;&quot;가능성이 있는 길만 가고, 아니면 빨리 돌아온다&quot;&lt;/b&gt;는 전략을 코드로 구현한 것입니다. 재귀 호출 직후에 상태를 원래대로 돌려놓는 과정만 잘 이해한다면, 복잡한 완전 탐색 문제도 효율적으로 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/77</guid>
      <comments>https://pirograming.tistory.com/77#entry77comment</comments>
      <pubDate>Thu, 19 Mar 2026 15:35:16 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 51 N-Queens(Java)</title>
      <link>https://pirograming.tistory.com/76</link>
      <description>&lt;h2 data-path-to-node=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 설명&lt;/h2&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://leetcode.com/problems/n-queens/&quot; data-ved=&quot;0CAAQ_4QMahcKEwjGlsq0iqmTAxUAAAAAHQAAAAAQcg&quot; data-hveid=&quot;0&quot;&gt;LeetCode 51 - N-Queens&lt;/a&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;2&quot;&gt;요약&lt;/b&gt;: &lt;span data-index-in-node=&quot;34&quot; data-math=&quot;n \times n&quot;&gt;$n \times n$&lt;/span&gt; 체스판에 &lt;span data-index-in-node=&quot;50&quot; data-math=&quot;n&quot;&gt;$n$&lt;/span&gt;개의 퀸을 서로 공격할 수 없도록 배치하는 모든 경우의 수를 찾는 문제입니다. 퀸은 같은 &lt;b data-index-in-node=&quot;101&quot; data-path-to-node=&quot;2&quot;&gt;행, 열, 그리고 양방향 대각선&lt;/b&gt; 위에 있는 기물을 공격할 수 있습니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 아이디어&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 대표적인 &lt;b data-index-in-node=&quot;11&quot; data-path-to-node=&quot;4&quot;&gt;백트래킹(Backtracking)&lt;/b&gt; 문제입니다. 모든 위치를 다 시도해보되, 퀸을 놓을 수 없는 조건이 되면 즉시 되돌아가서 다른 경로를 탐색합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;행별 배치&lt;/b&gt;: 퀸은 같은 행에 존재할 수 없으므로, 0번 행부터 &lt;span data-index-in-node=&quot;35&quot; data-math=&quot;n-1&quot;&gt;$n-1$&lt;/span&gt;번 행까지 한 행에 하나씩 퀸을 배치하며 내려갑니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;공격 가능 여부 체크&lt;/b&gt;: 현재 위치(row, col)에 퀸을 놓을 수 있는지 판단하기 위해 기존에 배치된 퀸들과 비교합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 열: queen.col == col&lt;/li&gt;
&lt;li&gt;우상향 대각선 (&lt;span data-index-in-node=&quot;9&quot; data-math=&quot;/&quot;&gt;$/$&lt;/span&gt;): 두 좌표의 합이 같음 (row + col)&lt;/li&gt;
&lt;li&gt;우하향 대각선 (&lt;span data-index-in-node=&quot;9&quot; data-math=&quot;\backslash&quot;&gt;$\backslash$&lt;/span&gt;): 두 좌표의 차가 같음 (row - col)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;상태 복구&lt;/b&gt;: 특정 위치에 퀸을 놓고 재귀 호출(recall)을 한 뒤에는, 반드시 놓았던 퀸을 제거(queens.removeLast())하여 다음 선택지를 깨끗한 상태에서 탐색할 수 있도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwjGlsq0iqmTAxUAAAAAHQAAAAAQcw&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;class Solution {
    List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; ret;
    public List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; solveNQueens(int n) {
        ret = new ArrayList&amp;lt;&amp;gt;();
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens = new ArrayList&amp;lt;&amp;gt;();
        recall(n, queens, 0);

        return ret;
    }

    private void recall(int n, List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens, int row) {
        // 1. 기저 조건: 모든 행에 퀸을 다 배치했을 때
        if (row &amp;gt;= n &amp;amp;&amp;amp; queens.size() == n) {
            List&amp;lt;String&amp;gt; sl = getStrings(n, queens);
            ret.add(sl);
            return;
        }
        
        // 2. 현재 행(row)에서 각 열(col)에 퀸을 놓아보기
        for (int col = 0; col &amp;lt; n; col++) {
            if (isPosable(queens, row, col)) {
                queens.add(List.of(row, col)); // 퀸 배치
                recall(n, queens, row + 1);    // 다음 행으로 이동
                queens.removeLast();           // 백트래킹 (원태 복귀)
            }
        }
    }

    // 퀸의 위치 리스트를 문제에서 요구하는 문자열 리스트 포맷으로 변환
    private static List&amp;lt;String&amp;gt; getStrings(int n, List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens) {
        List&amp;lt;String&amp;gt; sl = new ArrayList&amp;lt;&amp;gt;();
        for (int row = 0; row &amp;lt; queens.size(); row++) {
            List&amp;lt;Integer&amp;gt; queen = queens.get(row);
            StringBuilder sb = new StringBuilder();
            for (int col = 0; col &amp;lt; n; col++) {
                if (queen.get(0) == row &amp;amp;&amp;amp; queen.get(1) == col) {
                    sb.append('Q');
                } else {
                    sb.append('.');
                }
            }
            sl.add(sb.toString());
        }
        return sl;
    }

    // 현재 위치 (row, col)에 퀸을 놓을 수 있는지 체크
    private boolean isPosable(List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queens, int row, int col) {
        for (List&amp;lt;Integer&amp;gt; queen : queens) {
            if (queen.get(1) == col || // 같은 열 체크
                    queen.get(0) + queen.get(1) == row + col || // 대각선 / 체크
                    queen.get(0) - queen.get(1) == row - col)   // 대각선 \ 체크
                return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;isPosable 로직&lt;/b&gt;: 퀸의 공격 범위를 수학적으로 정의했습니다. 특히 대각선 체크 시 행과 열의 &lt;b data-index-in-node=&quot;56&quot; data-path-to-node=&quot;9,0,0&quot;&gt;합&lt;/b&gt;과 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;9,0,0&quot;&gt;차&lt;/b&gt;가 일정하다는 성질을 이용하면 $O(1)$의 수식으로 간결하게 표현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;queens.removeLast()&lt;/b&gt;: 재귀 호출이 끝난 후 마지막에 추가된 요소를 제거함으로써 스택 구조처럼 현재 경로를 취소하고 이전 상태로 돌아가는 백트래킹의 핵심을 구현했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;유연한 포맷팅&lt;/b&gt;: 퀸의 좌표값만 관리하다가, 정답을 찾은 순간에만 getStrings 메서드를 통해 요구하는 List&amp;lt;String&amp;gt; 형태로 변환하여 연산의 효율성을 높였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size26&quot;&gt;5. 성능 및 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 퀸을 배치할 수 있는 경우의 수는 최대 **&lt;span data-index-in-node=&quot;32&quot; data-math=&quot;O(N!)&quot;&gt;$O(N!)$&lt;/span&gt;**에 비례합니다. 각 배치 시마다 isPosable 검사를 수행하므로 전체적인 탐색 속도는 &lt;span data-index-in-node=&quot;89&quot; data-math=&quot;N&quot;&gt;$N$&lt;/span&gt;이 커짐에 따라 급격히 증가하지만, 문제의 범위인 &lt;span data-index-in-node=&quot;118&quot; data-math=&quot;N=9&quot;&gt;$N=9$&lt;/span&gt; 내외에서는 매우 빠르게 동작합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 재귀 호출의 깊이가 &lt;span data-index-in-node=&quot;19&quot; data-math=&quot;N&quot;&gt;$N$&lt;/span&gt;이며, 현재 경로의 퀸 위치를 저장하므로 **&lt;span data-index-in-node=&quot;45&quot; data-math=&quot;O(N)&quot;&gt;$O(N)$&lt;/span&gt;**의 추가 공간을 사용합니다. (결과 리스트 제외)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;6. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;N-Queens는 백트래킹의 교과서적인 문제로, &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;13&quot;&gt;재귀 구조와 상태 복구&lt;/b&gt;라는 개념을 익히기에 최적입니다. 대각선 조건을 수식으로 처리하는 부분과 removeLast()를 통한 상태 관리 부분을 잘 기억해두면 유사한 완전 탐색 문제들을 쉽게 해결할 수 있습니다.&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/76</guid>
      <comments>https://pirograming.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 18 Mar 2026 18:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 402 Remove K Digits(Java)</title>
      <link>https://pirograming.tistory.com/75</link>
      <description>&lt;h2 data-path-to-node=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 설명&lt;/h2&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://leetcode.com/problems/remove-k-digits/&quot; data-ved=&quot;0CAAQ_4QMahcKEwijmpmi26aTAxUAAAAAHQAAAAAQag&quot; data-hveid=&quot;0&quot;&gt;LeetCode 402 - Remove K Digits&lt;/a&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;2&quot;&gt;요약&lt;/b&gt;: 주어진 숫자 문자열 num에서 k개의 숫자를 제거하여 만들 수 있는 가장 작은 수를 구하는 문제입니다. 결과값에서 앞자리에 오는 0은 제거해야 하며, 빈 문자열이 될 경우 &quot;0&quot;을 리턴해야 합니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 아이디어&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;가장 작은 수를 만들기 위해서는 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;4&quot;&gt;앞자리에 최대한 작은 숫자&lt;/b&gt;가 위치해야 합니다. 이를 위해 &lt;b data-index-in-node=&quot;50&quot; data-path-to-node=&quot;4&quot;&gt;그리디(Greedy)&lt;/b&gt; 알고리즘과 &lt;b data-index-in-node=&quot;68&quot; data-path-to-node=&quot;4&quot;&gt;단조 증가 스택(Monotonic Stack)&lt;/b&gt; 논리를 활용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;비교와 제거&lt;/b&gt;: 숫자를 순차적으로 확인하며, 현재 숫자보다 바로 앞의 숫자가 더 크다면 앞의 숫자를 제거합니다. 이 과정을 k번 반복하여 큰 숫자가 앞자리에 오지 못하게 막습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;스택의 활용&lt;/b&gt;: StringBuilder를 스택처럼 사용하여 마지막에 추가된 숫자와 현재 숫자를 비교합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;잔여 k 처리&lt;/b&gt;: 만약 숫자가 &quot;1234&quot;와 같이 계속 증가하는 형태라 루프 내에서 제거되지 않았다면, 남은 k만큼 뒤에서부터 잘라냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0&quot;&gt;예외 처리&lt;/b&gt;: &quot;0012&quot;와 같은 결과에서 앞의 '0'들을 제거하고, 모든 숫자가 사라진 경우 &quot;0&quot;을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwijmpmi26aTAxUAAAAAHQAAAAAQaw&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;Java&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;class Solution {
    public String removeKdigits(String num, int k) {
        StringBuilder sb = new StringBuilder();

        // 1. 그리디 탐색: 현재 숫자보다 큰 이전 숫자는 제거
        for (char c : num.toCharArray()) {
            while (k &amp;gt; 0 &amp;amp;&amp;amp; sb.length() &amp;gt; 0 &amp;amp;&amp;amp; sb.charAt(sb.length() - 1) &amp;gt; c) {
                sb.deleteCharAt(sb.length() - 1);
                --k;
            }
            sb.append(c);
        }

        // 2. 남은 k 처리: 숫자가 계속 증가하는 형태일 경우 뒤에서 제거
        while (k &amp;gt; 0) {
            sb.deleteCharAt(sb.length() - 1);
            k--;
        }

        // 3. 앞자리 0 제거
        while (sb.length() &amp;gt; 0 &amp;amp;&amp;amp; sb.charAt(0) == '0') {
            sb.deleteCharAt(0);
        }

        // 4. 빈 문자열 처리
        if (sb.length() == 0) {
            sb.append('0');
        }

        return sb.toString();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;sb.charAt(sb.length() - 1) &amp;gt; c&lt;/b&gt;: 현재 문자 c가 이전 문자보다 작다면, 이전 문자를 제거하는 것이 전체 수의 크기를 줄이는 최선의 선택입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;StringBuilder의 효율성&lt;/b&gt;: 별도의 Stack&amp;lt;Character&amp;gt; 클래스를 사용하는 것보다 StringBuilder를 직접 조작하는 것이 객체 생성 비용을 줄이고 마지막에 문자열로 변환하기에 훨씬 유리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;선행 0 제거&lt;/b&gt;: while (sb.charAt(0) == '0') 루프를 통해 숫자의 유효성을 확보합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size26&quot;&gt;5. 성능 및 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 문자열의 길이를 &lt;span data-index-in-node=&quot;17&quot; data-math=&quot;N&quot;&gt;$N$&lt;/span&gt;이라 할 때, 각 문자는 최대 한 번 스택에 추가되고 한 번 제거됩니다. 따라서 **&lt;span data-index-in-node=&quot;65&quot; data-math=&quot;O(N)&quot;&gt;$O(N)$&lt;/span&gt;**의 선형 시간에 해결됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 결과를 저장하고 조작하기 위한 공간으로 **&lt;span data-index-in-node=&quot;32&quot; data-math=&quot;O(N)&quot;&gt;$O(N)$&lt;/span&gt;**을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;6. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 단순히 숫자를 제거하는 것이 아니라, **&quot;어떤 시점에 제거해야 최적인가&quot;**를 결정하는 그리디 전략이 핵심입니다. 단조 스택의 원리를 이해하고 있다면 다양한 유사 문제(예: 가장 큰 수 만들기 등)에 응용할 수 있는 좋은 예제였습니다.&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/75</guid>
      <comments>https://pirograming.tistory.com/75#entry75comment</comments>
      <pubDate>Tue, 17 Mar 2026 19:24:50 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 1775 Closest Dessert Cost(Java)</title>
      <link>https://pirograming.tistory.com/73</link>
      <description>&lt;h2 data-path-to-node=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 설명&lt;/h2&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://leetcode.com/problems/closest-dessert-cost/&quot; data-ved=&quot;0CAAQ_4QMahgKEwi7yNGGgKSTAxUAAAAAHQAAAAAQkAI&quot; data-hveid=&quot;0&quot;&gt;LeetCode 1775 - Closest Dessert Cost&lt;/a&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;2&quot;&gt;요약&lt;/b&gt;: 주어진 예산 내에서 target 금액에 가장 가까운 디저트 비용을 만드는 문제입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;3,0,0&quot;&gt;Base&lt;/b&gt;(필수)를 선택해야 합니다.&lt;/li&gt;
&lt;li&gt;여러 종류의 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;3,1,0&quot;&gt;Topping&lt;/b&gt;(선택)을 추가할 수 있으며, 각 토핑은 &lt;b data-index-in-node=&quot;37&quot; data-path-to-node=&quot;3,1,0&quot;&gt;최대 2개&lt;/b&gt;까지 사용 가능합니다.&lt;/li&gt;
&lt;li&gt;target과의 차이가 동일한 비용이 여러 개라면, &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;3,2,0&quot;&gt;더 낮은 비용&lt;/b&gt;을 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 아이디어&lt;/h2&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 선택의 가짓수가 제한적이고(토핑당 0, 1, 2개), 모든 경우를 탐색해도 시간 내에 해결 가능한 &lt;b data-index-in-node=&quot;61&quot; data-path-to-node=&quot;5&quot;&gt;백트래킹(Backtracking)&lt;/b&gt; 문제입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;완전 탐색&lt;/b&gt;: 모든 베이스 비용에 대해 각각 토핑을 추가하는 모든 경우의 수를 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;상태 정의&lt;/b&gt;: dfs(index, currentCost) 함수를 통해 현재 고려 중인 토핑의 인덱스와 지금까지 쌓인 비용을 관리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,2,0&quot;&gt;가지치기(Pruning)&lt;/b&gt;: 이미 현재 비용이 target을 넘어섰다면, 토핑을 더 추가하는 것은 target과의 거리를 멀어지게만 할 뿐이므로 탐색을 중단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwi7yNGGgKSTAxUAAAAAHQAAAAAQkQI&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;Java&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;class Solution {
    int ret;

    public int closestCost(int[] baseCosts, int[] toppingCosts, int target) {
        // 초기값 설정 (첫 번째 베이스 비용으로 시작)
        ret = baseCosts[0];

        // 1. 모든 Base에 대해 DFS 수행
        for (int base : baseCosts) {
            dfs(0, base, toppingCosts, target);
        }

        return ret;
    }

    private void dfs(int index, int currentCost, int[] toppingCosts, int target) {
        int currentDiff = Math.abs(currentCost - target);
        int bestDiff = Math.abs(ret - target);

        // 2. 최적의 결과(ret) 갱신 로직
        if (currentDiff &amp;lt; bestDiff) {
            ret = currentCost;
        } else if (currentDiff == bestDiff) {
            // 차이가 같다면 더 작은 값을 선택
            ret = Math.min(ret, currentCost);
        }

        // 3. 기저 조건 및 가지치기
        // 모든 토핑을 고려했거나, 이미 target을 초과한 경우 중단
        if (index == toppingCosts.length || currentCost &amp;gt; target) {
            return;
        }

        // 4. 재귀 호출 (토핑을 0개, 1개, 2개 추가하는 경우)
        for (int i = 0; i &amp;lt;= 2; i++) {
            dfs(index + 1, currentCost + (toppingCosts[index] * i), toppingCosts, target);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;ret 갱신 조건&lt;/b&gt;: currentDiff &amp;lt; bestDiff일 때뿐만 아니라, 거리가 같을 때 Math.min(ret, currentCost)를 사용하여 문제의 요구 조건(가장 작은 비용)을 충족했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;currentCost &amp;gt; target 가지치기&lt;/b&gt;: 토핑의 비용은 모두 양수이므로, 이미 target을 넘은 상태에서 토핑을 더하는 것은 의미가 없습니다. 이를 통해 탐색 효율을 높였습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;3진 트리 구조&lt;/b&gt;: 각 토핑 인덱스에서 선택지가 3개(0, 1, 2개)이므로, DFS는 최대 $3^{\text{topping length}}$의 깊이로 전개됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;5. 성능 및 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 베이스의 개수를 &lt;span data-index-in-node=&quot;17&quot; data-math=&quot;B&quot;&gt;$B$&lt;/span&gt;, 토핑의 개수를 &lt;span data-index-in-node=&quot;28&quot; data-math=&quot;T&quot;&gt;$T$&lt;/span&gt;라고 할 때, **&lt;span data-index-in-node=&quot;39&quot; data-math=&quot;O(B \times 3^T)&quot;&gt;$O(B \times 3^T)$&lt;/span&gt;**입니다. &lt;span data-index-in-node=&quot;61&quot; data-math=&quot;B, T \le 10&quot;&gt;$B, T \le 10$&lt;/span&gt;이므로 최대 연산 횟수는 약 &lt;span data-index-in-node=&quot;88&quot; data-math=&quot;10 \times 3^{10} = 590,490&quot;&gt;$10 \times 3^{10} = 590,490$&lt;/span&gt;번으로 매우 안전하게 통과됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 재귀 호출의 깊이가 토핑의 개수 &lt;span data-index-in-node=&quot;26&quot; data-math=&quot;T&quot;&gt;$T$&lt;/span&gt;에 비례하므로 **&lt;span data-index-in-node=&quot;37&quot; data-math=&quot;O(T)&quot;&gt;$O(T)$&lt;/span&gt;**의 스택 공간을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;6. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;조합 가능한 경우의 수가 적절히 제한되어 있을 때 &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;14&quot;&gt;DFS/백트래킹&lt;/b&gt;이 얼마나 강력한지 보여주는 문제입니다. 특히 &quot;차이가 같을 때 작은 값을 선택한다&quot;는 디테일한 조건을 조건문에 녹여내는 것이 포인트였습니다.&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/73</guid>
      <comments>https://pirograming.tistory.com/73#entry73comment</comments>
      <pubDate>Mon, 16 Mar 2026 17:35:13 +0900</pubDate>
    </item>
    <item>
      <title>[백준] BOJ 2979 트럭 주차(Java)</title>
      <link>https://pirograming.tistory.com/72</link>
      <description>&lt;h2 data-path-to-node=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;1. 문제 설명&lt;/h2&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://www.acmicpc.net/problem/2979&quot; data-ved=&quot;0CAAQ_4QMahgKEwim6r7Kwp-TAxUAAAAAHQAAAAAQ0wE&quot; data-hveid=&quot;0&quot;&gt;백준 2979번 - 트럭 주차&lt;/a&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;2&quot;&gt;요약&lt;/b&gt;: 세 대의 트럭이 주차된 시간대에 따라 주차 요금을 계산하는 문제입니다. 주차된 트럭의 대수(&lt;b data-index-in-node=&quot;79&quot; data-path-to-node=&quot;2&quot;&gt;1대, 2대, 3대&lt;/b&gt;)에 따라 한 대당 부과되는 요금이 달라지며, 각 트럭의 &lt;b data-index-in-node=&quot;121&quot; data-path-to-node=&quot;2&quot;&gt;입차 시간&lt;/b&gt;과 &lt;b data-index-in-node=&quot;128&quot; data-path-to-node=&quot;2&quot;&gt;출차 시간&lt;/b&gt;이 주어질 때 총 주차 요금을 구해야 합니다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;2. 핵심 아이디어&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 처음 봤을때 인터벌 그래프 문제로 생각하고 봤는데 구간도 노드도 작아 시뮬레이션으로 접근했습니다. 핵심은 &lt;b data-index-in-node=&quot;10&quot; data-path-to-node=&quot;4&quot;&gt;특정 시각에 주차장에 차가 몇 대 있는지&lt;/b&gt; 파악하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;요금 처리&lt;/b&gt;: 1대일 때 A, 2대일 때 B, 3대일 때 C원이며 이는 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;5,0,0&quot;&gt;'한 대당'&lt;/b&gt; 요금입니다. 코드에서는 계산의 편의를 위해 **(요금 * 대수)**를 미리 계산하여 배열에 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;시점 관리&lt;/b&gt;: 각 트럭의 입차 시간과 출차 시간을 기록합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;시뮬레이션&lt;/b&gt;: 가장 빠른 입차 시간부터 마지막 출차 시간까지 &lt;b data-index-in-node=&quot;33&quot; data-path-to-node=&quot;5,2,0&quot;&gt;1분 단위&lt;/b&gt;로 시간을 흐르게 하면서, 해당 시각에 들어온 차와 나간 차를 체크하여 현재 주차된 차량 수(&lt;b data-index-in-node=&quot;90&quot; data-path-to-node=&quot;5,2,0&quot;&gt;cnt&lt;/b&gt;)를 갱신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwim6r7Kwp-TAxUAAAAAHQAAAAAQ1AE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int[] price = new int[3], inTime = new int[3], outTime = new int[3];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i &amp;lt; 3; i++) {
            // 대수별 총 요금을 미리 계산 (i+1은 차량 대수)
            price[i] = Integer.valueOf(st.nextToken()) * (i + 1);
        }
        for (int i = 0; i &amp;lt; 3; i++) {
            st = new StringTokenizer(br.readLine());
            inTime[i] = Integer.valueOf(st.nextToken());
            outTime[i] = Integer.valueOf(st.nextToken());
        }

        // 시간 순서대로 이벤트를 처리하기 위해 정렬
        Arrays.sort(inTime);
        Arrays.sort(outTime);
        int inIdx = 0, outIdx = 0, cnt = 0, ret = 0;

        // 첫 입차 시각부터 마지막 출차 시각까지 1분 단위로 탐색
        for (int i = inTime[0]; i &amp;lt; outTime[2]; i++) {
            // 현재 시각(i)에 입차한 차량이 있다면 cnt 증가
            while (inIdx &amp;lt; 3 &amp;amp;&amp;amp; inTime[inIdx] &amp;lt;= i) {
                cnt++;
                inIdx++;
            }
            // 현재 시각(i)에 출차한 차량이 있다면 cnt 감소
            while (outIdx &amp;lt; 3 &amp;amp;&amp;amp; outTime[outIdx] &amp;lt;= i) {
                cnt--;
                outIdx++;
            }
            // 주차된 차량이 있다면 해당 대수에 맞는 미리 계산된 요금 합산
            if (cnt != 0) {
                ret += price[cnt - 1];
            }
        }

        System.out.println(ret);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;4. 코드 포인트 해설&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;9&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;price[i] = 요금 * (i + 1)&lt;/b&gt;: 1대일 때(A&lt;i data-index-in-node=&quot;32&quot; data-path-to-node=&quot;9,0,0&quot;&gt;1), 2대일 때(B&lt;/i&gt;2), 3대일 때(C*3)의 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;9,0,0&quot;&gt;합산 요금&lt;/b&gt;을 미리 구해두어 조건분기 없이 인덱스 접근만으로 요금을 계산했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;투 포인터 활용&lt;/b&gt;: inTime과 outTime을 정렬한 뒤 inIdx, outIdx를 사용하여 현재 시간 i에 발생하는 &lt;b data-index-in-node=&quot;67&quot; data-path-to-node=&quot;9,1,0&quot;&gt;입/출차 이벤트&lt;/b&gt;를 효율적으로 처리했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;while 루프&lt;/b&gt;: 같은 시각에 여러 대가 들어오거나 나가는 경우를 대비하여 if 대신 while을 사용하여 정확하게 cnt를 갱신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size26&quot;&gt;5. 성능 및 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 트럭의 수가 3대로 고정되어 있습니다. 전체 시간 범위를 &lt;span data-index-in-node=&quot;40&quot; data-math=&quot;T&quot;&gt;$T$&lt;/span&gt;라고 할 때 **&lt;span data-index-in-node=&quot;50&quot; data-math=&quot;O(T)&quot;&gt;$O(T)$&lt;/span&gt;**의 복잡도를 가지며, &lt;span data-index-in-node=&quot;68&quot; data-math=&quot;T&quot;&gt;$T$&lt;/span&gt;는 최대 100이므로 매우 효율적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 입력 데이터를 저장하는 고정 크기 배열 외에 추가 메모리를 거의 사용하지 않으므로 **&lt;span data-index-in-node=&quot;56&quot; data-math=&quot;O(1)&quot;&gt;$O(1)$&lt;/span&gt;**입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;6. 마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;단순히 시간 배열을 100까지 만들어 체크하는 방법도 있지만, 위와 같이 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;13&quot;&gt;정렬과 인덱스 포인터&lt;/b&gt;를 활용한 방식은 시간 범위가 매우 커지는 상황에서도 유연하게 대응할 수 있는 좋은 접근법입니다. 1분 단위의 &lt;b data-index-in-node=&quot;114&quot; data-path-to-node=&quot;13&quot;&gt;상태 변화&lt;/b&gt;를 정확히 포착하는 것이 이 문제의 핵심이었습니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/72</guid>
      <comments>https://pirograming.tistory.com/72#entry72comment</comments>
      <pubDate>Sat, 14 Mar 2026 22:53:35 +0900</pubDate>
    </item>
    <item>
      <title>[백준] BOJ 10808 알파벳 개수(Java)</title>
      <link>https://pirograming.tistory.com/71</link>
      <description>&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. 문제 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://www.acmicpc.net/problem/10808&quot; data-ved=&quot;0CAAQ_4QMahgKEwjyj6eduJyTAxUAAAAAHQAAAAAQnwE&quot; data-hveid=&quot;0&quot;&gt;백준 10808번 - 알파벳 개수&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;요약&lt;/b&gt;: 알파벳 소문자로만 이루어진 단어 &lt;span data-index-in-node=&quot;22&quot; data-math=&quot;S&quot;&gt;S&lt;/span&gt;가 주어졌을 때, 각 알파벳이 단어에 몇 개 포함되어 있는지 구하는 프로그램을 작성하세요. 출력은 'a'부터 'z'까지의 개수를 공백으로 구분하여 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;2. 핵심 아이디어&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;이 문제의 핵심은 알파벳 문자를 &lt;b data-index-in-node=&quot;18&quot; data-path-to-node=&quot;7&quot;&gt;배열의 인덱스&lt;/b&gt;로 변환하는 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;배열 생성&lt;/b&gt;: 알파벳 소문자는 총 26개입니다. 따라서 크기가 26인 정수 배열 int[] cnt = new int[26]을 생성합니다. (0번 인덱스는 'a', 25번 인덱스는 'z'를 의미하게 됩니다.)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;아스키코드 활용&lt;/b&gt;: 컴퓨터는 문자를 숫자로 처리합니다. 소문자 'a'의 아스키코드 값을 문자에서 빼주면 해당 문자의 순서를 0~25 사이의 숫자로 얻을 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'a' - 'a' = 0&lt;/li&gt;
&lt;li&gt;'b' - 'a' = 1&lt;/li&gt;
&lt;li&gt;'z' - 'a' = 25&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;순회 및 카운팅&lt;/b&gt;: 문자열의 각 문자를 확인하며 해당 인덱스의 값을 1씩 증가시킵니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;3. 코드&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;전체 코드는 매우 간결합니다. BufferedReader를 사용하여 성능을 높인 점이 인상적이네요.&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjyj6eduJyTAxUAAAAAHQAAAAAQoAE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;&lt;span&gt;Java&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. 입력 효율을 위해 BufferedReader 사용
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        
        // 2. 알파벳 26개를 담을 카운팅 배열 선언
        int[] cnt = new int[26];

        // 3. 문자열을 문자 배열로 변환하여 하나씩 확인
        for (char c : s.toCharArray()) {
            // 핵심 로직: 현재 문자에서 'a'를 빼서 0~25 인덱스를 구함
            cnt[c - 'a']++;
        }

        // 4. 결과 출력
        for (int i : cnt) {
            System.out.print(i + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;4. 코드 포인트 해설&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;c - 'a'&lt;/b&gt;: 이 수식 하나로 if문이나 switch문 없이 모든 알파벳을 제자리에 찾아 보낼 수 있습니다. 매우 효율적인 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;toCharArray()&lt;/b&gt;: 문자열의 각 문자에 접근할 때 s.charAt(i)를 사용하는 방법도 있지만, toCharArray()를 사용하면 향상된 for문(for-each)을 쓸 수 있어 가독성이 좋아집니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;배열 자동 초기화&lt;/b&gt;: Java에서 int 배열은 생성 시 모든 요소가 0으로 자동 초기화됩니다. 따라서 별도로 0을 채워 넣을 필요가 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;16&quot; data-ke-size=&quot;size23&quot;&gt;5. 성능 및 복잡도&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;시간 복잡도&lt;/b&gt;: 문자열의 길이를 &lt;span data-index-in-node=&quot;17&quot; data-math=&quot;N&quot;&gt;$N$&lt;/span&gt;이라고 할 때, 문자열을 한 번 훑으므로 **&lt;span data-index-in-node=&quot;43&quot; data-math=&quot;O(N)&quot;&gt;$O(N)$&lt;/span&gt;**입니다. 이후 크기가 26인 배열을 한 번 더 출력하므로 최종적으로는 선형 시간에 해결됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;공간 복잡도&lt;/b&gt;: 입력과 상관없이 항상 크기 26인 배열을 사용하므로 &lt;b data-index-in-node=&quot;37&quot; data-path-to-node=&quot;17,1,0&quot;&gt;&lt;span data-index-in-node=&quot;37&quot; data-math=&quot;O(1)&quot;&gt;$O(1)$&lt;/span&gt;&lt;/b&gt;(상수 공간)이라고 볼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size23&quot;&gt;6. 마치며&amp;nbsp;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;단순히 숫자를 세는 문제 같지만, &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;19&quot;&gt;문자를 인덱스로 매핑&lt;/b&gt;하는 기술은 해시 테이블(Hash Table)의 원리와도 맞닿아 있습니다. 기초적인 문자열 문제를 통해 이 테크닉을 익혀두면 나중에 더 복잡한 문자열 알고리즘(예: 팰린드롬, 아나그램 확인 등)을 풀 때 큰 도움이 됩니다.&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/71</guid>
      <comments>https://pirograming.tistory.com/71#entry71comment</comments>
      <pubDate>Fri, 13 Mar 2026 17:50:38 +0900</pubDate>
    </item>
    <item>
      <title>[백준] BOJ 2309 일곱 난쟁이(Java)</title>
      <link>https://pirograming.tistory.com/70</link>
      <description>&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;1. 문제 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;문제 링크&lt;/b&gt;: &lt;a href=&quot;https://www.acmicpc.net/problem/2309&quot; data-ved=&quot;0CAAQ_4QMahgKEwjyj6eduJyTAxUAAAAAHQAAAAAQgwE&quot; data-hveid=&quot;0&quot;&gt;백준 2309번 - 일곱 난쟁이&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;요약&lt;/b&gt;: 아홉 명의 난쟁이 중 진짜 일곱 명의 난쟁이를 찾아야 합니다. 일곱 명의 키의 합은 정확히 &lt;b data-index-in-node=&quot;55&quot; data-path-to-node=&quot;5,1,0&quot;&gt;100&lt;/b&gt;입니다. 아홉 명의 키가 주어졌을 때, 진짜 일곱 명을 찾아 키를 오름차순으로 출력하는 프로그램을 작성하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;2. 핵심 아이디어&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;이 문제의 핵심은 &lt;b&gt;전체 아홉 명 중 가짜 두 명을 골라내는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;발상의 전환&lt;/b&gt;: 아홉 명 중 일곱 명을 골라 합을 구하는 조합(Combination)을 생각할 수도 있지만, 반대로 &lt;b data-index-in-node=&quot;64&quot; data-path-to-node=&quot;8,0,0&quot;&gt;(전체 9명의 합) - (가짜 2명의 합) = 100&lt;/b&gt;이 되는 두 명을 찾는 것이 훨씬 효율적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;전체 탐색(Brute Force)&lt;/b&gt;: 아홉 명 중 두 명을 고르는 경우의 수는 대략 36가지(9C2) 정도로 매우 적습니다. 따라서 2중 반복문을 사용해 모든 경우를 확인해도 시간 복잡도 내에 충분히 해결 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;정렬&lt;/b&gt;: 출력 조건이 오름차순이므로, 탐색 전이나 후에 정렬을 수행해 줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;3. 코드&lt;/h3&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjyj6eduJyTAxUAAAAAHQAAAAAQhAE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    private static int[] tall;
    private static int[] fakeDwarf = new int[2]; // 가짜 난쟁이 인덱스를 저장할 배열
    private static int sum; // 아홉 난쟁이 키의 총합

    // 가짜 난쟁이 두 명을 찾는 메서드
    private static void findFakeDwarf() {
        for (int a = 0; a &amp;lt; 9; a++) {
            for (int b = a + 1; b &amp;lt; 9; b++) {
                // 전체 합에서 두 명의 키를 뺐을 때 100이 되면 정답!
                if (sum - tall[a] - tall[b] == 100) {
                    fakeDwarf[0] = a;
                    fakeDwarf[1] = b;
                    return; // 찾자마자 종료
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        tall = new int[9];
        sum = 0;
        
        // 1. 입력 받기 및 전체 합 구하기
        for (int i = 0; i &amp;lt; 9; i++) {
            tall[i] = Integer.parseInt(br.readLine());
            sum += tall[i];
        }
        
        // 2. 오름차순 출력을 위해 미리 정렬
        Arrays.sort(tall);
        
        // 3. 가짜 난쟁이 찾기 로직 수행
        findFakeDwarf();
        
        // 4. 결과 출력 (가짜 두 명을 제외한 나머지 출력)
        for (int i = 0; i &amp;lt; 9; i++) {
            if (i != fakeDwarf[0] &amp;amp;&amp;amp; i != fakeDwarf[1]) {
                System.out.println(tall[i]);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size23&quot;&gt;4. 코드 포인트 해설&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;sum - tall[a] - tall[b] == 100&lt;/b&gt;: 이 문제의 가장 핵심적인 로직입니다. 7명을 더하는 것보다 전체에서 2명을 빼는 방식이 코드가 간결하고 직관적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;Arrays.sort(tall)&lt;/b&gt;: 정답 출력 시 오름차순으로 보여줘야 하므로, 탐색 전에 미리 정렬을 해두면 출력할 때 순서대로 출력하기만 하면 되어 편리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;Early Return&lt;/b&gt;: findFakeDwarf() 메서드에서 정답을 찾자마자 return을 사용해 불필요한 반복을 방지했습니다. 문제에서 가능한 정답이 여러 개일 경우 아무거나 하나만 출력하라고 했기 때문에 효율적인 처리가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;16&quot; data-ke-size=&quot;size23&quot;&gt;5. 시간 복잡도 분석&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;입력 및 합계 계산&lt;/b&gt;: &lt;span data-index-in-node=&quot;12&quot; data-math=&quot;O(9)&quot;&gt;$O(9)$&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;정렬&lt;/b&gt;: &lt;span data-index-in-node=&quot;4&quot; data-math=&quot;O(9 \log 9)&quot;&gt;$O(9 \log 9)$&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0&quot;&gt;이중 루프(가짜 찾기)&lt;/b&gt;: &lt;span data-index-in-node=&quot;14&quot; data-math=&quot;O(9 \times 8 / 2) = 36&quot;&gt;$O(9 \times 8 / 2) = 36$&lt;/span&gt;번 연산&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,3,0&quot;&gt;최종 출력&lt;/b&gt;: &lt;span data-index-in-node=&quot;7&quot; data-math=&quot;O(9)&quot;&gt;$O(9)$&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;데이터의 개수가 9개로 고정되어 있어 시간 복잡도는 사실상 상수 시간(&lt;span data-index-in-node=&quot;39&quot; data-math=&quot;O(1)&quot;&gt;$O(1)$&lt;/span&gt;)에 가깝습니다. 브루트포스 알고리즘의 전형적인 예시라고 할 수 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;6. 마치며&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;이번 문제는 전체 경우의 수가 적을 때 사용할 수 있는 &lt;b data-index-in-node=&quot;31&quot; data-path-to-node=&quot;20&quot;&gt;완전 탐색&lt;/b&gt; 기법을 익히기에 좋은 문제였습니다. 특히 &lt;b&gt;&quot;7명을 찾기 위해 2명을 버린다&quot;&lt;/b&gt;는 역발상이 구현을 훨씬 단순하게 만들어 주었습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;대회였다면 언어를 cpp로 바꿔서 next_permutation을 사용했겠지만 java는 next_permutation이 메모리 구조적 문제로 없다보니 이렇게 구현을 해봤습니다.&lt;/p&gt;</description>
      <category>Algorithm</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/70</guid>
      <comments>https://pirograming.tistory.com/70#entry70comment</comments>
      <pubDate>Fri, 13 Mar 2026 17:27:44 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 427 Construct Quad Tree</title>
      <link>https://pirograming.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/construct-quad-tree/description/?envType=study-plan-v2&amp;amp;envId=top-interview-150&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;427.&amp;nbsp;Construct&amp;nbsp;Quad&amp;nbsp;Tree&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 보자마자 전형적인 분할 정복 문제라는 것이 보였고, 4개 분면으로 나눠가며 grid의 특정 범위 내의 모든 수가 같은 케이스에는 isLeaf 값을 참으로 갖는 node를 반환하는 식으로 한다면, size가 1x1인 grid 범위가 될 때 같은 베이스 케이스도 필요 없이 통과할 수 있다 생각해 아래 코드와 같이 작성했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751881096232&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public Node construct(int[][] grid) {
        return construct(grid, 0, 0, grid.length, grid[grid.length-1].length);
    }

    public Node construct(int[][] grid, int topLeft_x, int topLeft_y, int bottomRight_x, int bottomRight_y) {

        Node node = new Node(grid[topLeft_x][topLeft_y] == 1, true);

        for (int i = topLeft_x; i &amp;lt; bottomRight_x; i++) {
            for (int j = topLeft_y; j &amp;lt; bottomRight_y; j++) {
                if (grid[topLeft_x][topLeft_y] != grid[i][j]) {
                    node.isLeaf = false;
                }
            }
        }

        if (!node.isLeaf) {
            node.topLeft = construct(grid,
                            topLeft_x,
                            topLeft_y,
                            (topLeft_x + bottomRight_x) / 2,
                            (topLeft_y + bottomRight_y) / 2);
            node.topRight = construct(grid,
                            topLeft_x,
                            (topLeft_y + bottomRight_y) / 2,
                            (topLeft_x + bottomRight_x) / 2,
                            bottomRight_y);
            node.bottomLeft = construct(grid,
                            (topLeft_x + bottomRight_x) / 2,
                            topLeft_y,
                            bottomRight_x,
                            (topLeft_y + bottomRight_y) / 2);
            node.bottomRight = construct(grid,
                            (topLeft_x + bottomRight_x) / 2,
                            (topLeft_y + bottomRight_y) / 2,
                            bottomRight_x,
                            bottomRight_y);
        }

        return node;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>divide and conquer</category>
      <category>LeetCode</category>
      <category>분할정복</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/69</guid>
      <comments>https://pirograming.tistory.com/69#entry69comment</comments>
      <pubDate>Mon, 7 Jul 2025 18:39:14 +0900</pubDate>
    </item>
    <item>
      <title>JDK / JRE / JVM 개념 &amp;amp; 구성</title>
      <link>https://pirograming.tistory.com/65</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;JDK(Java Development Kit)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK는 Java Development Kit의 약자로 자바로 개발하는데 있어 필요한 SDK라고 생각하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 JDK에는 자바 개발에 있어서 필요한 개발 도구들(java, javac, javap, javadoc...)과 이어서 설명할 실행에 있어 필요한 JRE(Java Runtime Enviroment)를 포함합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;SDK란?&lt;br /&gt;&lt;/b&gt;SDK(Software Development Kit)란,&amp;nbsp;개발자를 위한 플랫폼별 구축 도구 세트입니다. 특정 플랫폼, 운영 체제 또는 프로그래밍 언어에서 실행되는 코드를 만들려면 디버거, 컴파일러 및 라이브러리와 같은 구성 요소가 필요합니다. SDK는 소프트웨어를 개발하고 실행하는 데 필요한 모든 것을 한 곳에서 제공합니다. 또한 SDK에는 문서, 튜토리얼 및 가이드와 같은 리소스와 더 빠른 애플리케이션 개발을 위한 API 및 프레임워크가 포함됩니다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qqhA/btsIu6DaqT6/OPWKMdBkLy99qH3v0xD9b1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qqhA/btsIu6DaqT6/OPWKMdBkLy99qH3v0xD9b1/img.png&quot; data-alt=&quot;JDK의 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qqhA/btsIu6DaqT6/OPWKMdBkLy99qH3v0xD9b1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qqhA%2FbtsIu6DaqT6%2FOPWKMdBkLy99qH3v0xD9b1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;334&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDK의 구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JDK 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK는 오라클의 Java SE(Java Standard Edition), Java ME(Java Micro Edition) 2개의 버전과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이클립스 제단의 Jakarta EE(Jakarta Enterprise Edition)가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 버전은 과거 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;썬 마이크로시스템즈가&lt;/span&gt;&amp;nbsp;각각 J2SE, J2ME, J2EE라는 이름으로 시작하여 개발하였습니다.&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;2010년 오라클이 썬을 인수했고 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;2017년 오라클이 이클립스 제단에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Java EE 8릴리즈를 마지막으로 이관하였고 공식 명칭 Jakarta EE, 프로젝트명 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;EE4J(Eclipse Enterprise for Java)로 운영해오고 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;자바 11버전 같이 일반적으로 말하는 버전은 오라클의 Java SE의 메이저 버전입니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl2G4D/btsIvAcGVVj/CXkTUCmUKklYKXohvNzXtK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl2G4D/btsIvAcGVVj/CXkTUCmUKklYKXohvNzXtK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl2G4D/btsIvAcGVVj/CXkTUCmUKklYKXohvNzXtK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl2G4D%2FbtsIvAcGVVj%2FCXkTUCmUKklYKXohvNzXtK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;141&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;Oracle JDK는 NFTC라이센스를 적용하고 있습니다. 때문에 &lt;/span&gt;기업에서 사용 할때에는 유료로 사용합니다. 그러나 여러 회사들이 JDK를 만들고 있고 그중 많이 쓰는 것을 다음과 같고 사용하실때에 라이센스를 확인하고 사용하시기 바랍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 46.1628%; height: 177px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;이름&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;개발사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Oracle JDK&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Oracle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Open JDK&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Oracle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Amazon Corretto&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Amazon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;Temurin&amp;nbsp;(AdoptOpenJDK)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373e48; text-align: left;&quot;&gt;Eclipse&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JRE(Java Runtime Enviroment)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JRE는 Java Runtime Enviroment의 약자로 컴퓨터의 운영체제 소프트웨어 상에서 실행되고&amp;nbsp;클래스&amp;nbsp;라이브러리&amp;nbsp;및 특정&amp;nbsp;Java&amp;nbsp;프로그램이 실행해야 하는 기타 리소스를 제공하는 소프트웨어 계층입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;JRE는 기본적으로 JDK에 포함되어 있으며, &lt;/span&gt;&lt;span&gt;기존에는 개별적으로 설치가 가능했지만 JDK11 버전부터는 따로 제공되지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JVM(Java Virtual Machine)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM은 Java Virtual Machine의 약자로 한국어로는 자바 가상 머신이라고 하며 OS를 가상화 하여 실행하는 Virtual Machine처럼 Java로 작성된 소프트웨어를 가상화 하여 하드웨어에 족속적이지 않게 실행 할 수 있도록 해주는 미들웨어 소프트웨어입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 JVM이 필요한가?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;C언어의 실행(WOCA)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM의 필요성을 알기 위해 C언어의 컴파일 방식을 먼저 알아 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어는 컴파일을 하게 되면 그 결과물로 즉시 실행 가능한 응용프로그램 파일을 반환하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 반환된 별과물을 &lt;b&gt;Binary code&lt;/b&gt;라고 하며 이는&amp;nbsp;OS와 CPU에 종속적이게에 MAC OS를 위해 컴파일 되면 Linux, Windows에서, ARM을 위해 컴파일 되면 Risc-V, MIPS, X86에서 사용이 불가능 하기에 각각의 환경에 맞는 컴파일러로 다시 컴파일을 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 C언어의 컴파일 방식을 WOCA, Write Once, Compile Anywhere, 번역하면 &quot;한번 작성하고 컴파일하면 어디서든 사용가능하다&quot;이고 여기서 어디서든의 의미는 환경이 동일한 곳이면 어디든이라는 의미입니다. 때문에 C언어를 이식성이 낮다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Binary code란?&lt;br /&gt;&lt;/b&gt;한국어로 이진 코드라고 하며, 컴퓨터가 직접 이해하고 실행할 수 있는 언어로, 0과 1로만 구성된 코드를 말합니다. 이는 소프트웨어 또는 프로그램이 컴퓨터의 프로세서에 의해 직접 실행될 수 있게 하는 최종적인 형태입니다. 컴파일러나 어셈블러는 고수준 언어나 어셈블리 언어를 이진 코드로 변환하여, 컴퓨터가 실행할 수 있게 합니다. 이진 코드는 특정 하드웨어 아키텍처에 특화되어 있어 다른 하드웨어에서는 실행이 어려울 수 있습니다. Machine Code(기계어)역시 Binary code의 일종입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Java의 실행(WORA)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 C언어와 달리 Java는 컴파일 하면 그 결과물로&amp;nbsp;&lt;b&gt;Byte Code&lt;/b&gt;를 반환해 줍니다. 그리고 이 Byte Code가 실행되기 위해 JVM을 거쳐 실행이 되며, JVM이 각각의 하드웨어에 맞는 Binary code로 바꾸어 실행을 시키기 때문에 실행 속도가 느리다는 단점도 존재 하지만 하드웨어, OS에 대해 독립적으로 작동 할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Byte Code란?&lt;/b&gt;&lt;br /&gt;바이트 코드는 일반적으로 컴파일러나 인터프리터에 의해 소스 코드가 중간 단계의 코드로 변환된 것을 말합니다. 바이트 코드는 하드웨어에 독립적이기 때문에 여러 운영 체제에서 호환될 수 있는 큰 장점이 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 자세하게 Java의 실행 과정을 살펴 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Java로 작성된 Source Code를 javac 라는 Java Compiler합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 컴파일 결과물인 .class 확장자의 Byte Code를 JVM인 java로 실행 시키면 각 환경에 맞는 Binary code로 바꾸어 실행됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0MTO/btsIwNKgtcg/jS2JwLdQxFxlvkmIvNBwB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0MTO/btsIwNKgtcg/jS2JwLdQxFxlvkmIvNBwB0/img.png&quot; data-alt=&quot;Java의 실행 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0MTO/btsIwNKgtcg/jS2JwLdQxFxlvkmIvNBwB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0MTO%2FbtsIwNKgtcg%2FjS2JwLdQxFxlvkmIvNBwB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;312&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Java의 실행 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Java의 컴파일 방식을 WORA, Write Once, Read Anywhere, 번역하면 &quot;한번 작성하면 어디서든 읽을수 있다&quot;이고 여기서 읽다의 주체는 JVM을 말합니다. 때문에 Java를 이식성이 높다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 JVM은 Java뿐 아니라 Kotlin, Groovy, Closure등의 언어들 역시 사용라고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 이렇게 C언어 보다 한층 진보한 언어로 보여지는 Java를 두고 왜 여전히 C, C++등의 고전적인 방식의 컴파일을 하는 언어를 사용할까요? 레거시라서? 개발자들이 멍청해서? 당연히 아닙니다. 이로 인한 비효율과 문제점들이 있기에 고전적인 방식의 컴파일을 여전히 사용 하는 것 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는 두번의 컴파일을 하기에 속도의 문제가 발생하는데, 이를 보완하기 위해 내부적으로&amp;nbsp;&lt;b&gt;JIT Compiler(Just-In-Time compiler)&lt;/b&gt;를 사용해서 필요한 부분만을 Binary code로 바꾸어 줌으로써 성능 향상을 가져오도록 했지만 그럼에도 C언어의 실행 속도를 따라잡지는 못합니다. 때문에 게임에서 C++, C#을 주로 사용하고, 임베디드에서 C, C++을 사용하는 이유가 이렇게 무겁게 JVM + 소프트웨어를 띄워야하고 속도도 느린 단점이 있기 때문 입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java 실행 실습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Java문법에 맞게 Java  Source Code를 작성해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.01.14.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S7W2U/btsIx40fWzB/bSznxyI5CSlT4s4QihWTk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S7W2U/btsIx40fWzB/bSznxyI5CSlT4s4QihWTk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S7W2U/btsIx40fWzB/bSznxyI5CSlT4s4QihWTk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS7W2U%2FbtsIx40fWzB%2FbSznxyI5CSlT4s4QihWTk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.01.14.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2. Javac로 컴파일 하여 Byte Code로 컴파일 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.04.59.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lZvgt/btsIx2gSt6g/0xLVLCEIuZ0dyzlKMCWKQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lZvgt/btsIx2gSt6g/0xLVLCEIuZ0dyzlKMCWKQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lZvgt/btsIx2gSt6g/0xLVLCEIuZ0dyzlKMCWKQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlZvgt%2FbtsIx2gSt6g%2F0xLVLCEIuZ0dyzlKMCWKQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.04.59.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3. Java로 실행 하여&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Binary code로 변환되어 실행되도록 합니다&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.05.06.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/paeqh/btsIwTSut0a/MmHficwPyrSk0RFKKilci0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/paeqh/btsIwTSut0a/MmHficwPyrSk0RFKKilci0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/paeqh/btsIwTSut0a/MmHficwPyrSk0RFKKilci0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpaeqh%2FbtsIwTSut0a%2FMmHficwPyrSk0RFKKilci0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2024-07-12 오후 8.05.06.png&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;https://www.ibm.com/kr-ko/topics/java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ibm.com/kr-ko/topics/java&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://www.ibm.com/kr-ko/topics/jre&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ibm.com/kr-ko/topics/jre&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://aws.amazon.com/ko/what-is/java-runtime-environment/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aws.amazon.com/ko/what-is/java-runtime-environment/&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://www.ibm.com/docs/ko/sdk-java-technology/8?topic=introduction-java-virtual-machine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ibm.com/docs/ko/sdk-java-technology/8?topic=introduction-java-virtual-machine&lt;/a&gt;&lt;/blockquote&gt;</description>
      <category>Java</category>
      <author>과로사한 공돌이</author>
      <guid isPermaLink="true">https://pirograming.tistory.com/65</guid>
      <comments>https://pirograming.tistory.com/65#entry65comment</comments>
      <pubDate>Fri, 12 Jul 2024 20:14:28 +0900</pubDate>
    </item>
  </channel>
</rss>