<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이것저것</title>
    <link>https://loveall.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 13 Jul 2026 10:56:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>잠많은 늘보</managingEditor>
    <item>
      <title>같은 문제, 다른 언어, 다른 생각 (자바와 자바스크립트)</title>
      <link>https://loveall.tistory.com/entry/%EA%B0%99%EC%9D%80-%EB%AC%B8%EC%A0%9C-%EB%8B%A4%EB%A5%B8-%EC%96%B8%EC%96%B4-%EB%8B%A4%EB%A5%B8-%EC%83%9D%EA%B0%81-%EC%9E%90%EB%B0%94%EC%99%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 코드를 작성할 때와 JavaScript 코드를 작성할 때는 사고방식 자체가 달라진다.&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;JavaScript에서는 주로 데이터를 어떻게 변형할 것인가를 고민하게 된다. 배열을 필터링하고, 원하는 형태로 매핑하고, 그룹화하거나 집계하여 최종 결과를 만들어내는 과정이 자연스럽다. 타입 제약이 적고 자료의 형태를 유연하게 바꿀 수 있기 때문에, 문제를 해결할 때도 데이터의 흐름과 변환 과정에 집중하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 JavaScript로 문제를 풀 때는 자연스럽게 이런 생각을 하게 된다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;배열을 필터링하고&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;그 결과를 그룹핑하고&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;다시 매핑해서&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;최종 결과를 만들자&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 map, filter, reduce, flatMap 같은 함수들을 사용하는 것이 권장되고, 또 자연스럽다.&lt;/p&gt;
&lt;pre style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;return users
    .filter(user =&amp;gt; user.age &amp;gt;= 20)
    .map(user =&amp;gt; user.name)
    .sort();
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 알고리즘 난이도가 높아지면 시간 복잡도나 자료구조 선택을 고려해야 하지만, 일반적으로는 데이터를 어떻게 가공하여 원하는 결과를 얻을지 고민하는 비중이 큰 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Java에서는 다른 고민을 하게 된다. 데이터를 어떻게 변형할 것인가보다 어떤 자료구조를 사용할 것인가, 그리고 그 과정이 얼마나 효율적인가를 먼저 생각하게 된다. 배열을 사용할지, 리스트를 사용할지, 해시맵이 적절한지, 정렬이 필요한지 등을 고민하는 순간이 많다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;이 데이터를 어디에 저장하지?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;ArrayList가 좋나?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;HashMap이 좋나?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;정렬을 해야 하나?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;중복 제거는 Set으로 해야 하나?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 변환 이전에 자료구조 선택을 고민하게 되는 것이다.&lt;/p&gt;
&lt;pre style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Set&amp;lt;String&amp;gt; names = new HashSet&amp;lt;&amp;gt;();

for (User user : users) {
    if (user.getAge() &amp;gt;= 20) {
        names.add(user.getName());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Java Stream도 있긴 한데.. 어쩐지 사용하면 찜찜할 때가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중간 객체 생성 비용이나 메모리 사용량, 반복 횟수 등등. 자료를 변형할수록 코드가 복잡해지고, 새로운 컬렉션을 생성하는 과정에서 비용이 발생하기 때문이다.&lt;/p&gt;
&lt;pre style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;users.stream()
     .filter(user -&amp;gt; user.getAge() &amp;gt;= 20)
     .map(User::getName)
     .sorted()
     .toList();
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;color: #666666; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;이거 Stream 여러 번 타면 중간 객체 얼마나 생기지?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;ArrayList capacity 지정할까?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;HashMap 초기 크기 잡을까?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이런 생각이, 자연스레 따라 붙는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 JavaScript를 쓸 때는 &amp;lsquo;자료구조의 변형&amp;rsquo;자체가 즐겁다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 간결할 수록 우아해 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는 &amp;lsquo;자료구조의 효율&amp;rsquo;을 극대화 했을 때 즐겁다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄로 쓰지 못하더라도 자료구조의 크기와 효율성이 읽힐 때 우아하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어마다 개발자가 문제를 바라보는 방식까지 달라진다는 사실이 새삼 흥미롭게 느껴진다.&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/35</guid>
      <comments>https://loveall.tistory.com/entry/%EA%B0%99%EC%9D%80-%EB%AC%B8%EC%A0%9C-%EB%8B%A4%EB%A5%B8-%EC%96%B8%EC%96%B4-%EB%8B%A4%EB%A5%B8-%EC%83%9D%EA%B0%81-%EC%9E%90%EB%B0%94%EC%99%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8#entry35comment</comments>
      <pubDate>Tue, 16 Jun 2026 14:04:35 +0900</pubDate>
    </item>
    <item>
      <title>정처기 강의 수강 중간점검</title>
      <link>https://loveall.tistory.com/entry/%EC%A0%95%EC%B2%98%EA%B8%B0-%EA%B0%95%EC%9D%98-%EC%88%98%EA%B0%95-%EC%A4%91%EA%B0%84%EC%A0%90%EA%B2%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2934&quot; data-origin-height=&quot;1368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ofSNo/dJMcaglk0HZ/ku8JHtE7fGYHFp60M129Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ofSNo/dJMcaglk0HZ/ku8JHtE7fGYHFp60M129Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ofSNo/dJMcaglk0HZ/ku8JHtE7fGYHFp60M129Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FofSNo%2FdJMcaglk0HZ%2Fku8JHtE7fGYHFp60M129Tk%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;2934&quot; height=&quot;1368&quot; data-origin-width=&quot;2934&quot; data-origin-height=&quot;1368&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;p data-ke-size=&quot;size16&quot;&gt;수강을 시작한지 11일이 지났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템상 수강시작은 15일 이지만 교재가 16일에 도착했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 2강씩 듣기로 계획을 세웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 27일 - 시작일 16일 = 11일 동안 22강을 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제풀이는 우선 풀고나서 잘 모르는 문제만 들어서 수월했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 비전공자이고 풀스택 개발 경력이 5년 차에 접어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의 내용은 경험적으로 알고 있던 것들을 개념화 하는 느낌이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비전공자 대상 강의이다 보니 개념을 모른다는 가정하에 설명을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 좋기도 나쁘기도 한데, 내가 모르는 게 나올 때는 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 개념을 쉽게 외울 수 있는 두음 법칙 등으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내내 반복한다. 응집도 결합도는 듣고 그날 외운 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응집도: (우)리 (논)던 (시)(절)에 먹던 (통)통한 (순)대 있잖아, (기)가 막히게 맛있었지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응집도는 역순으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능적, 순차적, 통신적, 절차적, 시간적, 논리적, 우연적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결합도: (데)(스)형(제)(외)저래(공)유가(내) 닮았데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실은 결합도는 입에 잘 안붙어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데스노트로 외웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(데)(스)노트에서 (제)(외)해 (공)유랑 (내) 이름.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 그대로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터, 스탬프, 제어, 외부, 공통, 내용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이런게 꿀팁이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내내 반복하니까 강의만 들어도 외워져서 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꾸준히 하루 2~3강씩 편하게 듣고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 모의고사 풀면서 모르는 부분만 보강하면 될 것 같다.&lt;/p&gt;</description>
      <category>개발/강의</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/34</guid>
      <comments>https://loveall.tistory.com/entry/%EC%A0%95%EC%B2%98%EA%B8%B0-%EA%B0%95%EC%9D%98-%EC%88%98%EA%B0%95-%EC%A4%91%EA%B0%84%EC%A0%90%EA%B2%80#entry34comment</comments>
      <pubDate>Wed, 27 May 2026 18:36:00 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 파일명 정렬 문제</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%8C%8C%EC%9D%BC%EB%AA%85-%EC%A0%95%EB%A0%AC-%EB%AC%B8%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아주 단순하게 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명을 비교할 때마다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소문자로 바꾸고&lt;/li&gt;
&lt;li&gt;정규식으로 HEAD / NUMBER 분리하고&lt;/li&gt;
&lt;li&gt;숫자로 변환한 뒤&lt;/li&gt;
&lt;li&gt;비교&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 이런 느낌.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;function solution(files) {
    function parse(filename) {
        filename = filename.toLowerCase();
        const regex = /^([^0-9]+)([0-9]+)(.*)$/i;
        const [, head, number] = filename.match(regex);

        return [ head, Number(number) ];
    }

    return files.sort((a, b) =&amp;gt; {
        const [headA, numberA] = parse(a);
        const [headB, numberB] = parse(b);

        if (headA &amp;lt; headB) return -1;
        if (headA &amp;gt; headB) return 1;

        return numberA - numberB;
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 꽤 깔끔하다고 생각하고 제출했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트는 통과 했지만 테스트 케이스 중에 8ms 가 넘는 것이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명 배열은 1000개 이하기 때문에 문제가 있는 것 같아 코드를 다시 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 정렬을 잘 안다고 생각했지만 잘 모르고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 공부를 좀 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진의 Array.prototype.sort()는 현재 &lt;span style=&quot;color: #ee2323;&quot;&gt;Timsort*&lt;/span&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timsort는 stable sort이며, 최악의 경우 시간 복잡도는 O(N log N)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 원소가 1,000개라면 log₂(1000)이 약 10이므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 함수 호출도 대략 수천~1만 번 규모로 늘어날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;comparator 안에&lt;/b&gt; &lt;b&gt;toLowerCase, 정규식 매칭, 숫자 변환&lt;/b&gt;&amp;nbsp;등의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 넣으면&lt;b&gt; 그 비용이 반복해서 누적된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timsort 알고리즘의 평균 시간 복잡도는 (N log N)이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;비교해야 할 배열 원소가 1,000개라면, &lt;span data-index-in-node=&quot;58&quot; data-math=&quot;\log_2(1000) \approx 10&quot;&gt;log ₂ (1000) &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&amp;asymp;&lt;/span&gt; 10 이어서&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로 두 원소를 비교하는 행위(가 대략 10,000번 가량 일어날 수 있다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;* Timsort:&lt;/span&gt; &lt;a href=&quot;https://v8.dev/blog/array-sort&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://v8.dev/blog/array-sort&lt;/a&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;gt; sort 의 역사에 대해 알 수 있다. sort 춘추 전국시대&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정렬에서 중요한 건 비교 함수의 비용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort는 내부적으로 comparator를 생각보다 많이 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 개수가 많아질수록, 정렬비용 + 문자열 파싱비용이 함께 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 파싱은 한번만, 정렬은 가볍게 하는 코드로 바꿨다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;원본 문자열
&amp;rarr; 정렬용 메타데이터 생성
&amp;rarr; 정렬
&amp;rarr; 원래 문자열만 추출
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 꽤 유명한 정렬 패턴인데,&lt;br /&gt;슈바르츠 변환이라고 부른다는 것을 알게됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최종 코드&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;function solution(files) {
    return files
        .map((filename) =&amp;gt; {
            const lower = filename.toLowerCase();
            const [, head, number] = lower.match(/^([^0-9]+)([0-9]+)(.*)$/);

            return { original: filename, head, number: Number(number) };
   	    })
        .sort((a, b) =&amp;gt; {
            if (a.head &amp;lt; b.head) return -1;
            if (a.head &amp;gt; b.head) return 1;

            return a.number - b.number;
        })
        .map(file =&amp;gt; file.original);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;stable sort 덕분에 index는 넣지 않았다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 비교 대상 ( head, number)가 모두 같으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 입력 순서를 유지해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 index 를 같이 저장해서 비교할까 고민했다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;index
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 요즘 JS의 sort()는 stable sort&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt; 라서,&lt;br /&gt;비교 결과가 0이면 기존 순서를 유지해준다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;return a.number - b.number;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 ECMAScript 명세에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array.prototype.sort()가 stable sort임을 보장한다.&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 본 V8 엔진을 사용하는 크롬, 엣지, 노드 뿐 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사파리(JSC, JavaScriptCore), 파이어 폭스(Spider Monkey) 에서도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stable sort 가 보장된다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;And for all non-negative integers j and k such that j &amp;lt; k &amp;lt; itemCount, if ℝ(SortCompare(old[j], old[k])) = 0, then &amp;pi;(j) &amp;lt; &amp;pi;(k); i.e., the sort is stable. (출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://262.ecma-international.org/16.0/index.html?_gl=1*1yxzc8p*_ga*MTQ5NjIxNTQyNi4xNzc5NDQ4NDA4*_ga_TDCK4DWEPP*czE3Nzk0NDg0MDgkbzEkZzAkdDE3Nzk0NDg0MDgkajYwJGwwJGgw#sec-sortindexedproperties&quot;&gt;ECMA-262 16th edition&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;프로그래머스 실행환경은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교적 최신버전의 node&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt; 를&amp;nbsp; 사용하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index 비교는 생략하기로 했다.&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;mdn문서에 보면 'stable sort 가 아닐 수도 있다' 고 나오는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 여러 환경을 고려해야 하는 mdn 입장에선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구형 환경까지 고려해 표현한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구형환경까지 고려해야하는 상황이라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index 를 저장해 비교하는 방식이 더 안전할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt; 프로그래머스 에서 console.log(process)를 찍어보면 아래와 같이 출력된다. (불법인지 모르겠다...ㅋㅋ)&lt;/p&gt;
&lt;pre id=&quot;code_1779449986541&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;process {
    version: 'v16.17.0',
    versions: {
        node: '16.17.0',
        v8: '9.4.146.26-node.22',

        ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;comparator 안에서 무거운 작업 하지 않기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 풀면서 가장 크게 느낀 건 이거였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 자체보다 비교 함수 안에서 무엇을 하느냐가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 중요할 수 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열 변환&lt;/li&gt;
&lt;li&gt;정규식&lt;/li&gt;
&lt;li&gt;파싱&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 작업은 comparator 안에서 반복하기보다,&lt;br /&gt;미리 계산해두는 편이 훨씬 깔끔하고 효율적이라는 걸 배웠다.&lt;/p&gt;</description>
      <category>Array sort</category>
      <category>javascript sort</category>
      <category>stable sort</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/33</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%8C%8C%EC%9D%BC%EB%AA%85-%EC%A0%95%EB%A0%AC-%EB%AC%B8%EC%A0%9C#entry33comment</comments>
      <pubDate>Fri, 22 May 2026 20:44:47 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 주차 요금 계산</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트를 풀 때는 코드를 최대한 압축하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간결하게 쓰려고 노력하지만 이번 문제는 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 해결한 &amp;ldquo;주차 요금 계산&amp;rdquo; 문제는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀다보니 추상화 하고 싶은 욕구를 참을 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 좀 과한 것 같지만 class 를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코테를 이렇게 풀지 말라는 법은 없으니까....!!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 아이디어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 다시 보면 사실 필요한 상태는 단순하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;차량별 누적 주차 시간&lt;/li&gt;
&lt;li&gt;현재 입차 시간&lt;/li&gt;
&lt;li&gt;지금 주차 중인지 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나눴다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;this.records = new Map();
this.parkingLot = new Set();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;records는 차량별 기록 저장소, 아래와 같은 형태다:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// entered: 입차시간, accTime: 누적 주차시간
{ entered, accTime }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 parkingLot은 현재 주차 중인 차량 목록을 관리하는 Set이다.&lt;br /&gt;입차기록만 있고 출차 기록이 없는 차량을 따로 추척하는 용도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출차 기록이 없는 경우 출차시간을 23:59로 간주한다는 전제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 기록이 있는 전체 차량을 순회하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입차시간이 없는 경우(entered === null)를 판별할까 했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;현재 주차 중인 차량&amp;rdquo;을 따로 관리하는 게 깔끔할 것 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;IN / OUT 을 진짜 게이트처럼 처리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재밌었던 부분 중 하나는 이거다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;gate(record) {
    const [time, carNo, ioType] = this._recordParser(record);
    this[ioType](carNo, time);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 입력형태가 아래와 같아서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 메서드를 문자열로 동적으로 실행했다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// 05:34 5961 IN
// 07:59 5961 OUT

this[ioType](...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이런 코드는 실무에서는 조심해야 하지만,&lt;br /&gt;문제 조건상 입력이 고정돼 있으니 꽤 재밌는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력이 완전히 통제되고 검증할 수 있는 경우에는 편리한 패턴이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 입력이 섞인다면 사용을 지양해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상치 못한 메서드 호출로 이어질 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;남아있는 차량 한 번에 출차하기!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 기분이 좋았던 부분은 의외로 여기다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깔끔하게 현제 남아 있는 차들을 출차시키는 느낌...!&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;for (const carNo of this.parkingLot) {
    this.OUT(carNo);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set 순회 중 삭제가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 [...this.parkingLot] 처럼 배열로 복제한 뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순회하는 방법이&amp;nbsp;더 안전한 선택일 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 현재 코드에서는 순회 중 새로운 항목이 추가되지 않고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;순회 예정인 항목을 미리 삭제하지 않기 때문에&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;별도의 배열을 만들지 않고 그대로 순회하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현 된 주차 정산 클래스&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;class ParkingSettlementManager {
    constructor(fees) {
        this.baseTime = fees[0];
        this.baseFee = fees[1];
        this.unitTime = fees[2];
        this.unitFee = fees[3];

        this.closeTime = 23 * 60 + 59;

        this.records = new Map();
        this.parkingLot = new Set();
    }

    _recordParser(record) {
        const [time, carNo, ioType] = record.split(&quot; &quot;);
        const [h, m] = time.split(&quot;:&quot;);

        return [Number(h) * 60 + Number(m), carNo, ioType];
    }

    gate(record) {
        const [time, carNo, ioType] = this._recordParser(record);
        this[ioType](carNo, time);
    }

    IN(carNo, entered) {
        const record = this.records.get(carNo);

        if (!record) {
            this.records.set(carNo, { entered, accTime: 0 });
        } else {
            record.entered = entered;
        }

        this.parkingLot.add(carNo);
    }

    OUT(carNo, outAt = this.closeTime) {
        const record = this.records.get(carNo);

        record.accTime += outAt - record.entered;
        record.entered = null;

        this.parkingLot.delete(carNo);
    }

    settle() {
        for (const carNo of this.parkingLot) {
            this.OUT(carNo);
        }

        const cars = [...this.records.keys()].sort();

        return cars.map(carNo =&amp;gt; {
            const { accTime } = this.records.get(carNo);

            const addedFee =
                accTime &amp;gt; this.baseTime
                    ? Math.ceil((accTime - this.baseTime) / this.unitTime) * this.unitFee
                    : 0;

            return this.baseFee + addedFee;
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;호출이 아주 깔끔해서 기분 좋았다!&lt;/h2&gt;
&lt;pre id=&quot;code_1779355767028&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(fees, records) {
    const psm = new ParkingSettlementManager(fees);

    for (const record of records) {
        psm.gate(record);
    }

    return psm.settle();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return psm.settle()으로 코드를 마무리 할 때 짜릿했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 정말 재미있게 풀었다.&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/32</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0#entry32comment</comments>
      <pubDate>Thu, 21 May 2026 18:43:42 +0900</pubDate>
    </item>
    <item>
      <title>LZW 압축 문제: &amp;ldquo;가장 긴 문자열 찾기&amp;rdquo;</title>
      <link>https://loveall.tistory.com/entry/LZW-%EC%95%95%EC%B6%95-%EB%AC%B8%EC%A0%9C-%E2%80%9C%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B0%BE%EA%B8%B0%E2%80%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스의 압축(LZW) 문제를 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 문제 이해를 잘못해서 삽질을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질 1: 현재 문자열과 다음 문자열의 범위&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 사전에 등록된 것이 있는지 확인하는 대상은 &quot;현재 문자열&quot;에만 한하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;처음에는 현재 문자열 뒤에 붙일 대상도 사전에 등록된 문자열 단위라고 착각했다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령, 아래의 문자열이 사전에 등록되어 있다면:&lt;/p&gt;
&lt;pre id=&quot;code_1779247877555&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A
K
AO
KA&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;착각했을 때는 &quot;KAKAO&quot;에서 &quot;KAK&quot; 다음에 &quot;AO&quot;를 붙여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;KAKAO&quot;를 사전에 등록하는 식으로 이해했다.&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;하지만 문제에서 말하는 w + c의 c는 사전에 있는 다음 문자열이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 문자열 바로 뒤의 한 글자였다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질2: 길이 상관없이 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;현재 문자열이 사전에 있는지 검사하고, 있으면 그걸 쓰면 되잖아?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 처음엔 이런 식으로 생각했다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;for (let key of dict.keys()) {
    if (msg.startsWith(key)) {
        return key;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 바로 문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LZW는 단순히 &amp;ldquo;시작하는 문자열&amp;rdquo;을 찾는 게 아니라,&lt;br /&gt;&lt;b&gt;사전에 존재하는 가장 긴 문자열&lt;/b&gt;을 찾아야 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 사전이 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;K
KA
KAK
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 확인해야 하는 문자열이 &quot;KAKAO&quot;라면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 문자열과 &lt;u&gt;&lt;b&gt;일치되는 가장 긴 문자열&lt;/b&gt;&lt;/u&gt;을 사전에서 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;K&quot;, &quot;KA&quot;도 일치하지만, &quot;KAK&quot;를 찾아야 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 이걸 어떻게 처리해야 하나 고민했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;길이순 정렬?&lt;/li&gt;
&lt;li&gt;긴 문자열 우선 탐색?&lt;/li&gt;
&lt;li&gt;startsWith 여러 번?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식은 모두 비효율적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 위치에서 문자열을 하나씩 늘려 확인하는 것이 가장 나아보였다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;K
KA
KAK
KAKA
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전에 존재할 때까지 확인하고 없으면 멈추면 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 문자열을 확인한 부분까지 잘라 재할당 하는 방식으로 했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slice 후 재할당 하는 경우 비용이 클 것 같아,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터를 이동시키는 방식으로 변경했다.&lt;/p&gt;
&lt;pre id=&quot;code_1779245346418&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;msg = msg.slice(w.length);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1779245504737&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let pos = 0;

반복문:
	next = pos + 1;

	반복문:
   		msg[next++];

	pos = next;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질3: 알파벳 dict 와 추가 dict 를 따로 관리한다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전에 추가된 문자열만 골라 조회한다는 생각에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dict, addedDict 를 따로 관리하려 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addedDict 의 key 를 선회하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 문자열이 해당 key로 시작되는지 조회하려고 했기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1779245852354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let w = &quot;&quot;;

for (const key of addedDict.keys()) {
	if (msg.startsWith(key)) {
		w = key;
		break;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map을 사용하는 목적은 빠른 조회이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 비용이 O(1)인데 이 방식은 그 이점을 활용하지 못하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가되는 항목이 많을수록 효율이 떨어지는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회방식을 변경하면서 자연히 dict 를 하나로 통합했다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;let w = msg[pos];
let next = pos + 1;

while (next &amp;lt; msg.length &amp;amp;&amp;amp; dict.has(w + msg[next])) {
    w += msg[next++];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 이런 형태가 됐다:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;function solution(msg) {
    const alphabet = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;;
    const dict = new Map([...alphabet].map((ch, i) =&amp;gt; [ch, i + 1]));
    
    const result = [];
    let pos = 0;
    let addIdx = dict.size + 1;
  
    while (pos &amp;lt; msg.length) {
        let w = msg[pos];
        let next = pos + 1;

        while (next &amp;lt; msg.length &amp;amp;&amp;amp; dict.has(w + msg[next])) {
            w += msg[next++];
        }
        
        result.push(dict.get(w));
        
        if (next &amp;lt; msg.length) {
            dict.set(w + msg[next], addIdx++);
        }
        
        pos = next;
    }
    
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addIndex 는 다음에 추가될 index 이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;26을 바로 써도 좋지만, 숫자 하드코딩은 되도록 피하는 게 좋다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;숫자의 의미를 이해하는 데 시간이 걸리기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 26이라고 써도 알파벳 길이라는 의미가 명확하기도 하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼에도, A-Z 까지 추가된 길이에 1을 더하는 것이 더 의미상 더 잘 읽힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;생각해 볼 것 : 초기 알파벳 dict 등록 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에 알파벳을 dict 에 등록하는 방식도 여러가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 &quot;A&quot;.fromCharCode() 로 해서 반복하는 방식을 쓰려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1779246246770&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    const ACode = &quot;A&quot;.charCodeAt();
    const dict = new Map(); 

    for (let i = 0; i &amp;lt; 26; i++) {
        const ch = String.fromCharCode(i + ACode);
        dict.set(ch, i + 1);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 가독성이 그리 좋지 않다.&lt;br /&gt;인덱스에 ACode 를 더하는 행위도 한눈에 이해하기 어려울 수 있고,&lt;br /&gt;알파벳 길이 26을 하드코딩하는 것도 마음에 안 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 숫자를 문자로 전환하는 연산을 하는 것도&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;비용은 크지 않지만 별로 좋은 방식은 아닌 것 같았다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1779246463556&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const alphabet = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;;
const dict = new Map([...alphabet].map((ch, i) =&amp;gt; [ch, i + 1]));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 물론 알파벳을 map으로 1회 순회한 뒤, 전개하기 때문에&lt;br /&gt;아주 미세한 차이로 시간이 더 걸리지만 0.03 ms 정도로 무시해도 될 수준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도가 명확하고 가독성이 훨씬 좋아지기 때문에 이 코드가 더 낫다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알파벳 타이핑 실수는 쉽게 발견하고 수정할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 아래와 같은 것은 지양하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vim 에디터를 쓰더라도 타이핑이 오래 걸리기도 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Map 으로 변환하는 목적으로만 사용할 데이터이기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1779247035384&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

const dict = {
	 A: 1, B: 2, C: 3, D: 4, 
     E: 5, F: 6, G: 7, H: 8, 
     I: 9, J: 10, K: 11, L: 12, 
     M: 13, N: 14, O: 15, P: 16, 
     Q: 17, R: 18, S: 19, T: 20, 
     U: 21, V: 22, W: 23, 
     X: 24, Y: 25, Z: 26
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/31</guid>
      <comments>https://loveall.tistory.com/entry/LZW-%EC%95%95%EC%B6%95-%EB%AC%B8%EC%A0%9C-%E2%80%9C%EA%B0%80%EC%9E%A5-%EA%B8%B4-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%B0%BE%EA%B8%B0%E2%80%9D#entry31comment</comments>
      <pubDate>Wed, 20 May 2026 12:40:05 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] n진수 게임 JavaScript 풀이 &amp;mdash; &amp;ldquo;누구 차례인가&amp;rdquo;만 생각하기</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-n%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84-JavaScript-%ED%92%80%EC%9D%B4-%E2%80%94-%E2%80%9C%EB%88%84%EA%B5%AC-%EC%B0%A8%EB%A1%80%EC%9D%B8%EA%B0%80%E2%80%9D%EB%A7%8C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;처음 문제를 읽었을 때는&lt;br /&gt;순서대로 숫자를 말하는 과정을 어떻게 구현해야 할지 조금 복잡하게 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;독특한 규칙이 있었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 자리수 이상이면 서로 1, 0, 1, 1, 1, 2 이렇게 자리수를 나눠 번갈아 말해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참가자 수가 있고,&lt;br /&gt;각자 자신의 차례에 숫자를 말하며,&lt;br /&gt;튜브가 말해야 하는 문자만 추출해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;턴관리를 어떻게 해야할까 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상화 하려면 실제로 어떻게 작동하는 지 보는게 도움이 된다.&lt;br /&gt;index를 순서대로 적고, 말해야 할 숫자와 프로토의 턴을 적었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 index 가 주어졌을 때 프로도의 턴인지 확인하는 공식을 도출했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령 숫자를 하나씩 10진수를 3명이 순서대로 말한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index, 말해야하는 문자, P는 프로도 이다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;index 0 &amp;rarr; char 1 &amp;rarr; X
index 1 &amp;rarr; char 2 &amp;rarr; P
index 2 &amp;rarr; char 3 &amp;rarr; Z
index 3 &amp;rarr; char 4 &amp;rarr; X
index 4 &amp;rarr; char 5 &amp;rarr; P
index 5 &amp;rarr; char 6 &amp;rarr; Z
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 index 가 프로도의 턴인지 확인하려면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index를 참가자 수로 나눈 나머지가,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(프로도의 순서 - 1)과 같은지 확인하면 된다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;idx % m === (순서 - 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 전체 발화 순서를 의미하는 idx 하나만 관리하기로 했다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;if (idx % m === p - 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 문자가 튜브 차례라면 결과에 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;function solution(n, t, m, p) {
  let result = '';
  let num = 0;
  let idx = 0;

  while (result.length &amp;lt; t) {
    for (const ch of num.toString(n)) {
      if (idx % m === p - 1) result += ch;
      if (result.length === t) break;
      idx++;
    }

    num++;
  }

  return result.toUpperCase();
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 문자열을 모두 저장하지 않고,&lt;br /&gt;필요한 문자만 바로 결과에 추가하도록 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 배열에 push 후 join 할까 고민했지만,&lt;br /&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;또, 처음에는 toUpperCase()를 매번 호출하는 것으로 코드를 짰는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 사람의 풀이를 참고하니 호출하지 않고 마지막에 한 번만 처리하는 게 나을 것 같았다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;return result.toUpperCase();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 차이지만,&lt;br /&gt;&amp;ldquo;변환은 가능한 한 번에&amp;rdquo; 처리하는 것이 효율적이고 읽기도 쉬울 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 깔끔하고 세련되게 나와서 기쁘다.&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/30</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-n%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84-JavaScript-%ED%92%80%EC%9D%B4-%E2%80%94-%E2%80%9C%EB%88%84%EA%B5%AC-%EC%B0%A8%EB%A1%80%EC%9D%B8%EA%B0%80%E2%80%9D%EB%A7%8C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0#entry30comment</comments>
      <pubDate>Tue, 19 May 2026 10:49:30 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 더맵게 문제</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94%EB%A7%B5%EA%B2%8C-%EB%AC%B8%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스 더 맵게 문제는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수인 스코빌 지수가 담긴 배열이 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자가 클수록 더 매운 음식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 덜매운 지수 2개를 뽑아 주어진 공식으로 더 맵게 만들어야 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 맵게 계산 된 새로운 지수와 남아 있는 지수들에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 가장 덜 매운 2개의 지수를 뽑아야 하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 처음 봤을 때는 계산 된 결과도 포함하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 가장 덜 매운 두 개를 고르는 것이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차적으로 계산하는 것이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우선 배열을 오름차순 정렬한 뒤,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;섞은 결과와 다음 작은 값을 골라 섞으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행하면 되겠다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 문제와 예시를 더 자세히 보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 맵게 만든 지수도 포함해서 작은 것을 골라야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래 배열이 주어진다면,&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[2, 3, 4, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 두 수를 뽑아 예시의 공식에 따라 더 맵게 만든다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;2 + (3 * 2) = 8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8은 남아있는 원소들 [4, 6] 중 가장 크므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[8, 4, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 계산은 4와 6을 뽑아 계산해야 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 단순히 한 번 정렬하고 앞에서부터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순회하며 계산하는 방식으로는 해결할 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 최소값 두 개를 새롭게 찾기 위해&lt;br /&gt;새로운 값을 추가하고 배열에 추가할 때마다 매번 다시 정렬해야 하나 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그렇게 하면 정렬 비용이 반복적으로 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 제한사항을 보니&lt;br /&gt;최대 길이가 1,000,000 이었기 때문에,&lt;br /&gt;단순 반복 정렬 방식은 비용이 너무 컸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;문제의 최대 규모가 1,000,000건으로 매우 크고,&lt;br /&gt;각 단계마다 가장 작은 두 값을 꺼낸 뒤,&lt;br /&gt;새로운 값을 다시 추가해야 한다.&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span&gt;즉 단순히 한 번 정렬하는 것이 아니라,&lt;/span&gt;&lt;br /&gt;&lt;span&gt;최소값 추출 &amp;rarr; 새 값 삽입 &amp;rarr; 다시 최소값 추출&lt;/span&gt;&lt;br /&gt;&lt;span&gt;과정을 반복적으로 수행해야 하는 구조였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;최소값 유지 및 추출 방식을 알아보다가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heap 방식이 최소값 또는 최대값을 유지하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 값 삽입과 추출을 하는 효율적인 알고리즘인 것을 알게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heap을 설명한 블로그 게시물과 영상을 하나 찾아서 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차원 배열을 2진 트리 처럼 활용하는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0은 1, 2 번으로 갈라지고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 3, 4번, 2번은 5, 6번&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번은 7, 8번, 4번은 9, 10번, 5번은 11, 12번, 6번은 13, 14번....&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;이런 식으로 어떤 인덱스 i가 주어지면 자식 노드는 이렇게 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i * 2 + 1&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i * 2 + 2&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 어떤 인덱스 i가 주어졌을 때 부모 노드는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식노드 인덱스를 구할 때 공식을 역산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Math.floor((i - 1) / 2);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 값을 반영하는 한 번의 작업 기준으로 보면,&lt;br /&gt;&lt;br /&gt;처음 생각한 방식으로 하는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에 새 값을 추가한 뒤에 다시 정렬해야 하므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최악의&amp;nbsp;경우&amp;nbsp;O(n&amp;nbsp;log&amp;nbsp;n)&amp;nbsp;비용이&amp;nbsp;발생한다.&lt;br /&gt;&lt;br /&gt;반면&amp;nbsp;heap은&amp;nbsp;push/pop&amp;nbsp;과정에서&lt;br /&gt;부모와&amp;nbsp;자식&amp;nbsp;노드만&amp;nbsp;비교하며&amp;nbsp;이동하므로&lt;br /&gt;O(log&amp;nbsp;n)&amp;nbsp;비용으로&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;배열 길이가 100만이라면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열에 새 값 추가 후 전체 정렬: 100만 * 20&lt;/li&gt;
&lt;li&gt;heap push/pop: 20&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;* 2를 대략 20번 거듭제곱 하면 100만이 된다. &lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;log₂(x)는 2를 몇 번 거듭제곱해야 x가 되는지 계산하는 것이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬&amp;nbsp;상태를&amp;nbsp;다시&amp;nbsp;유지하는&amp;nbsp;비용만&amp;nbsp;놓고&amp;nbsp;보면&lt;br /&gt;대략&amp;nbsp;n배&amp;nbsp;차이가&amp;nbsp;나는&amp;nbsp;셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 scoville 배열의 원소를 하나씩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 배열에 push 해서 heap을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 heapify 방식을 알게 돼서 최종 코드를 수정했다.&lt;br /&gt;&lt;br /&gt;heapify는 새 배열을 만들지 않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 배열을 heap 구조로 재배치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;자식이 있는 마지막 부모의 index 를 찾아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역으로 순회하며 각&amp;nbsp;노드가&amp;nbsp;자신의&amp;nbsp;자식보다&amp;nbsp;크다면&amp;nbsp;아래로&amp;nbsp;내려보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리프 노드는 자식이 없으므로 비교대상이 없기 때문에 배제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식이 있는 마지막 부모노드를 구할 때는:&lt;/p&gt;
&lt;pre id=&quot;code_1778931516521&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Math.floor(this.heap.length / 2) - 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 방식으로 초기 heap을 구성하면,&lt;br /&gt;원소를 하나씩 push 하는 방식보다 효율적으로 heap을 만들 수 있다.&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;초기 heap 구성 비용:&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원소를 하나씩 push: O(n log n)&lt;/li&gt;
&lt;li&gt;heapify: O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heapify 방식은 push를 반복하는 방식보다 대략 log n 배 정도 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100만건이라면 20배정도 효율적인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드는 이렇다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;class MinHeap {
    constructor(arr = []) {
        this.heap = arr;
        this.heapify();
    }

    heapify() {
        const start = Math.floor(this.heap.length / 2) - 1;

        for (let i = start; i &amp;gt;= 0; i--) {
            this.bubbleDown(i);
        }
    }
    
    swap(i, j) {
    	/*
        [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
        */
        const temp = this.heap[i];
        this.heap[i] = this.heap[j];
        this.heap[j] = temp;
    }

    bubbleDown(i) {
        while (true) {
            const left = i * 2 + 1;
            const right = i * 2 + 2;

            let smallest = i;

            if (
                left &amp;lt; this.heap.length &amp;amp;&amp;amp;
                this.heap[left] &amp;lt; this.heap[smallest]
            ) {
                smallest = left;
            }

            if (
                right &amp;lt; this.heap.length &amp;amp;&amp;amp;
                this.heap[right] &amp;lt; this.heap[smallest]
            ) {
                smallest = right;
            }

            if (smallest === i) break;
			this.swap(i, smallest);


            i = smallest;
        }
    }

    push(value) {
        this.heap.push(value);

        let i = this.heap.length - 1;

        while (i &amp;gt; 0) {
            const parent = Math.floor((i - 1) / 2);

            if (this.heap[parent] &amp;lt;= this.heap[i]) break;
            this.swap(i, parent);

            i = parent;

        }
    }

    pop() {
        if (this.heap.length === 0) return undefined;
        if (this.heap.length === 1) return this.heap.pop();
        const min = this.heap[0];
        this.heap[0] = this.heap.pop();
        let i = 0;

        this.bubbleDown(i);
        return min;
    }

    size() {
        return this.heap.length;
    }

    peek() {
        return this.heap[0];
    }
}

function solution(scoville, K) {
    const heap = new MinHeap(scoville);
    let count = 0;

    while(heap.size() &amp;gt;= 2 &amp;amp;&amp;amp; heap.peek() &amp;lt; K) {
        const first = heap.pop();
        const second = heap.pop();

        heap.push(first + second * 2);
        count++;
    }

    return heap.peek() &amp;gt;= K ? count : -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/29</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%94%EB%A7%B5%EA%B2%8C-%EB%AC%B8%EC%A0%9C#entry29comment</comments>
      <pubDate>Sun, 17 May 2026 00:55:31 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 뒤에 있는 큰 수 찾기 문제</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%92%A4%EC%97%90-%EC%9E%88%EB%8A%94-%ED%81%B0-%EC%88%98-%EC%B0%BE%EA%B8%B0-%EB%AC%B8%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 처음 봤을 때는 단순 반복 탐색 문제처럼 보였다.&lt;/p&gt;
&lt;p data-end=&quot;79&quot; data-start=&quot;33&quot; data-ke-size=&quot;size16&quot;&gt;각 숫자마다 오른쪽을 보면서&lt;br /&gt;자기보다 큰 수가 나오면 반환하는 로직을 떠올렸다.&lt;/p&gt;
&lt;p data-end=&quot;79&quot; data-start=&quot;33&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;79&quot; data-start=&quot;33&quot; data-ke-size=&quot;size16&quot;&gt;예를들면:&lt;/p&gt;
&lt;pre id=&quot;code_1778913438327&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(numbers) {
  	return numbers.map((n, i) =&amp;gt; {
        let closestGtNum = -1;

        for (let j = i + 1; j &amp;lt; numbers.length; j++) {
       		if (n &amp;lt; numbers[j]) {
                closestGtNum = numbers[j];
                break;
            }
        }

        return closestGtNum;
    }) 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방식은 각 숫자마다 뒤를 다시 탐색하기 때문에&lt;br /&gt;최악의 경우 O(n&amp;sup2;) 시간이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 제한사항을 보니 numbers의 최대길이는 1,000,000개 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 1,000,000개의 테스트가 주어지면 위 로직의 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략 1,000,000 * 1,000,000 / 2, 대략 5,000억번의 비교가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(정확히는 length(length - 1) / 2 다. --- 순회 횟수가&amp;nbsp;등차수열이라서)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국&amp;nbsp;배열을&amp;nbsp;오른쪽부터&amp;nbsp;거꾸로&amp;nbsp;순회하면서,&lt;br /&gt;현재&amp;nbsp;숫자보다&amp;nbsp;작거나&amp;nbsp;같은&amp;nbsp;수들을&amp;nbsp;stack에서&amp;nbsp;제거하는&amp;nbsp;방식으로&amp;nbsp;해결했다.&lt;br /&gt;&lt;br /&gt;문제에서 필요한 것은 &quot;가장 가까운 큰 수&quot; 이기 때문에,&lt;br /&gt;현재&amp;nbsp;숫자보다&amp;nbsp;작거나&amp;nbsp;같은&amp;nbsp;뒤쪽&amp;nbsp;숫자들은&amp;nbsp;더&amp;nbsp;이상&amp;nbsp;의미가&amp;nbsp;없다.&lt;br /&gt;&lt;br /&gt;현재 숫자 보다 &quot;더 큰 수&quot;가 될 수 없고,&lt;br /&gt;앞쪽 숫자와 비교할 때는 현재 숫자에게 밀리기 때문이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;즉, stack에는 현재 위치 기준으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로도 유효한 &quot;뒤 큰수 후보&quot;만 남기면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드는 이렇다.:&lt;/p&gt;
&lt;pre id=&quot;code_1778913388776&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(numbers) {
    const stack = [];
    const result = Array(numbers.length).fill(-1);
    
    for (let i = numbers.length - 1; i &amp;gt;= 0; i--) {
        while(stack.length &amp;amp;&amp;amp; stack.at(-1) &amp;lt;= numbers[i]) {
            stack.pop();
        }
        
        if (stack.at(-1) &amp;gt; numbers[i]) result[i] = stack.at(-1);
        stack.push(numbers[i]);
    }
    
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미&amp;nbsp;확인한&amp;nbsp;정보를&amp;nbsp;어떻게&amp;nbsp;재사용할지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한&amp;nbsp;탐색을&amp;nbsp;어떻게&amp;nbsp;제거할지&amp;nbsp;고민하며&lt;br /&gt;최적화하는 과정이 아직은 완전히 익숙하지 않은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래도 stack 을 활용을 조금 더 체화할 수 있었던 문제였다.&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/28</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%92%A4%EC%97%90-%EC%9E%88%EB%8A%94-%ED%81%B0-%EC%88%98-%EC%B0%BE%EA%B8%B0-%EB%AC%B8%EC%A0%9C#entry28comment</comments>
      <pubDate>Sat, 16 May 2026 15:57:58 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 방문 길이 문제</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B8%EC%9D%B4-%EB%AC%B8%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스의 방문 길이 문제를 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 문제를 읽었을 때는 단순 좌표 이동 문제처럼 보였지만,&lt;br /&gt;지나간 길(path)을 식별해야 하는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지나간 길을 어떻게 식별해야 할까 고민하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 좌표를 적어보았다.&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;A: [1, 0] &amp;lt;-&amp;gt; B: [1, -1]&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 A, B 두 좌표를 서로 오갈 수 있는 길은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A to B, B to A 는 모두 같은 길을 지난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서 무관하게 두 좌표를 관리하면 된다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 두 좌표를 하나의 &amp;ldquo;경로 key&amp;rdquo;로 만들어 관리하기로 했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const pathKey = (a, b) =&amp;gt; {
  const p1 = `${a[0]},${a[1]}`;
  const p2 = `${b[0]},${b[1]}`;

  return p1 &amp;lt; p2 ? `${p1}|${p2}` : `${p2}|${p1}`;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 방향성을 제거하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A: &quot;1,0&quot; 과 B: &quot;1,-1&quot; 을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 문자열 순서로 비교해 오름 차순으로 결합하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key처럼 관리하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향과 상관 없이 A B 좌표를 오가는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key는 이렇게 통일된다: &quot;1,-1|1,0&quot;&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;이후에는 Set을 이용해 방문한 경로를 저장하고 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Set 의 has, add, delete 는 평균 O(1)의 시간복잡도로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 조회, 추가, 삭제보다 유리하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;if (!visited.has(path)) count++;

visited.add(path);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 처음 지나간 길만 카운트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드는 다음과 같다.:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function solution(dirs) {
    const directions = {
        U: [-1, 0],
        D: [1, 0],
        R: [0, 1],
        L: [0, -1]
    };

    const pathKey = (a, b) =&amp;gt; {
        const p1 = `${a[0]},${a[1]}`;
        const p2 = `${b[0]},${b[1]}`;

        return p1 &amp;lt; p2 ? `${p1}|${p2}` : `${p2}|${p1}`;
    };

    const visited = new Set();

    let count = 0;

    let x = 0;
    let y = 0;

    for (const dir of dirs) {
        const [dx, dy] = directions[dir];

        const nx = x + dx;
        const ny = y + dy;

        if (nx &amp;lt; -5 || nx &amp;gt; 5 || ny &amp;lt; -5 || ny &amp;gt; 5) {
            continue;
        }

        const path = pathKey([x, y], [nx, ny]);

        if (!visited.has(path)) {
            count++;
        }

        visited.add(path);

        x = nx;
        y = ny;
    }

    return count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 같았으면 좌표 저장 방식이나 중복 처리 방법을 꽤 오래 고민했을 것 같은데,&lt;br /&gt;프로그래머스 알고리즘 문제를 입문 부터 300문제 이상 풀다보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 익숙한 패턴이어서 비교적 쉽게 푼 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DVvqD/dJMcafNrsXk/YFmIwEQzQuk2ekubrsYxq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DVvqD/dJMcafNrsXk/YFmIwEQzQuk2ekubrsYxq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DVvqD/dJMcafNrsXk/YFmIwEQzQuk2ekubrsYxq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDVvqD%2FdJMcafNrsXk%2FYFmIwEQzQuk2ekubrsYxq0%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;336&quot; height=&quot;136&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/27</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B8%EC%9D%B4-%EB%AC%B8%EC%A0%9C#entry27comment</comments>
      <pubDate>Sat, 16 May 2026 14:46:32 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 뉴스 클러스터링</title>
      <link>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스의 뉴스 클러스터링 문제를 풀었다.&lt;br /&gt;처음엔 집합 문제라고 생각했는데, 풀다 보니 실제로는 다중집합(multiset)을 구현하는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 두 글자씩 끊어서 배열로 만든 뒤, includes를 이용해 교집합을 구하려고 했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const intersection = set1.filter(el =&amp;gt; str2.includes(el));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 곧 문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;aa&quot;, &quot;aa&quot;]
[&quot;aa&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 교집합은 &quot;aa&quot; 하나만 있어야 한다.&lt;br /&gt;하지만 includes는 &amp;ldquo;존재 여부&amp;rdquo;만 확인하기 때문에 &quot;aa&quot; 두 개가 모두 교집합으로 들어가 버렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그제서야 문제를 다시 읽어보니, 이 문제는 일반 집합이 아니라 &amp;ldquo;다중집합&amp;rdquo;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 핵심은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원소가 존재하는가?&lt;br /&gt;가 아니라&lt;/li&gt;
&lt;li&gt;원소가 몇 개 존재하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 결국 각 원소의 등장 횟수를 세는 방식으로 접근을 바꿨다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;const counter = new Map();

counter.set(el, (counter.get(el) ?? 0) + 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 두 문자열에서 등장한 모든 원소를 순회하면서:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;교집합은 Math.min&lt;/li&gt;
&lt;li&gt;합집합은 Math.max&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 이용해 계산했다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;intersection += Math.min(e1, e2);
union += Math.max(e1, e2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 예시를 들면:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;A = [&quot;aa&quot;, &quot;aa&quot;, &quot;bb&quot;]
B = [&quot;aa&quot;, &quot;cc&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라면 &quot;aa&quot;처럼 같은 원소지만 개수가 다른 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교집합은 집합 B의 개수인 1개를, 합집합은 집합 A의 개수인 2개를 포함해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최종코드는 이렇다:&lt;/p&gt;
&lt;pre id=&quot;code_1778846485374&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(str1, str2) {
    const countElements = (str) =&amp;gt; {
        const counter = new Map();
        str = str.toLowerCase();

       for (let i = 0; i &amp;lt; str.length - 1; i++) {
            const el = str[i] + str[i + 1];
            if (!/[^a-z]/.test(el)) {
                counter.set(el, (counter.get(el) ?? 0) + 1);
            }
        } 

        return counter;
    }

    const A = countElements(str1);
    const B = countElements(str2);

    const elements = new Set([...A.keys(), ...B.keys()]);

    let union = 0;
    let intersection = 0;

    for (const el of elements) {
        const a = A.get(el) ?? 0;
        const b = B.get(el) ?? 0;

        intersection += Math.min(a, b);
        union += Math.max(a, b);
    }

    const multiplier = 65536;

    return union !== 0
        ? Math.floor(intersection / union * multiplier)
        : multiplier;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;countElements 는 처음 주어진 문자열을 소문자로(대문자로도 가능하다) 통일하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개씩 자른 원소에 알파벳이 아닌 것이 포함됐는 지 확인한 뒤에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원소의 개수를 세는 공통 로직이다.&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;처음에는 반복해서 썼지만, str1, str2 등 복붙하다 보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;str2를 str1 의 길이로 순회한다거나 하는 실수가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 같은 로직이기 때문에 별도의 함수로 분리해주었다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;const A = countElements(str1);
const B = countElements(str2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하니 이후 로직도 훨씬 읽기 편해져서 만족스러웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수명은 고민하다가 원소의 개수를 세는 것에 집중해 countElements 라고 이름 지었다.&lt;br /&gt;그리고 두 집합의 구분도 같은 이름에 1, 2와 같이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 붙이는 것 대신 A, B 로 바꾼 것도 깔끔했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 구현 문제라고 생각했는데,&lt;br /&gt;&amp;ldquo;집합&amp;rdquo;과 &amp;ldquo;다중집합&amp;rdquo;의 차이를 제대로 이해하게 된 문제였다.&lt;/p&gt;</description>
      <category>개발/알고리즘</category>
      <author>잠많은 늘보</author>
      <guid isPermaLink="true">https://loveall.tistory.com/26</guid>
      <comments>https://loveall.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81#entry26comment</comments>
      <pubDate>Fri, 15 May 2026 21:05:33 +0900</pubDate>
    </item>
  </channel>
</rss>