<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>parixscm.log</title>
        <link>https://velog.io/</link>
        <description>성장지향형 프론트엔드 개발자</description>
        <lastBuildDate>Tue, 11 Apr 2023 11:14:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>parixscm.log</title>
            <url>https://velog.velcdn.com/images/jason_kim/profile/953ac9cf-2ae1-432c-9e2d-718e9f5e1749/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. parixscm.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jason_kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 숫자카드나누기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%88%AB%EC%9E%90%EC%B9%B4%EB%93%9C%EB%82%98%EB%88%84%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%88%AB%EC%9E%90%EC%B9%B4%EB%93%9C%EB%82%98%EB%88%84%EA%B8%B0</guid>
            <pubDate>Tue, 11 Apr 2023 11:14:59 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(arrayA, arrayB) {
    const arrAMin = arrayA.reduce((prev, curr) =&gt; Math.min(prev, curr), 100000000);
    const arrBMin = arrayB.reduce((prev, curr) =&gt; Math.min(prev, curr), 100000000);

    function findA(criterion, compare, minNum) {
        for (let i = minNum; i &gt; 0; i--) {
            if (criterion.every(n =&gt; n % i === 0) &amp;&amp; !compare.some(n =&gt; n % i === 0)) return i;
        }
        return 0;
    }

    return Math.max(findA(arrayA, arrayB, arrAMin), findA(arrayB, arrayA, arrBMin));
}</code></pre><p>입력값의 제한은 다음과 같습니다.</p>
<ul>
<li>1 &lt;= 배열 arrayA, arrayB의 길이 &lt;= 500,000</li>
</ul>
<p>각 배열에 대해 이중 for문을 적용하는 건 어렵다고 생각했습니다.
가장 큰 양의 정수 a를 찾기 위해서는 각 배열이 비교 기준이 되는 총 2가지 경우를 따져야 합니다.
가장 큰 양의 정수를 찾는 로직은 다음과 같습니다.</p>
<ol>
<li>우선 각 배열에 대한 최소값을 구합니다. 최소값을 구한 이유는 최소값보다 큰 수로는 최소값을 나눌 수 없기 때문입니다.</li>
<li>구한 최소값을 시작점으로 1씩 감소시키면서 가장 큰 양의 정수를 찾기 위한 조건문을 돌립니다.</li>
<li>조건을 만족한다면 해당 값을 리턴하고 for문을 종료합니다. 해당 값보다 더 작은 수를 볼 필요는 없기 때문입니다.</li>
</ol>
<p>위 로직을 담은 fidnA 함수를 생성했습니다.
마지막으로, 비교 기준을 arrayA로 삼았을 때 findA 함수가 리턴하는 값과 비교 기준을 arrayB로 삼았을 때 findA 함수가 리턴하는 값 중 큰 값을 리턴하면 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 연속부분수열합의개수]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%97%B0%EC%86%8D%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B4%ED%95%A9%EC%9D%98%EA%B0%9C%EC%88%98</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%97%B0%EC%86%8D%EB%B6%80%EB%B6%84%EC%88%98%EC%97%B4%ED%95%A9%EC%9D%98%EA%B0%9C%EC%88%98</guid>
            <pubDate>Tue, 11 Apr 2023 10:38:39 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(elems) {
    const extendedElems = [ ...elems, ...elems.slice(0, elems.length - 1)];
    const sumSet = new Set();
    for (let i = 0; i &lt; elems.length; i++) {
        let sum = 0;
        for (let j = 0; j &lt; elems.length; j++) {
            sum += extendedElems[i + j];
            sumSet.add(sum);
        }
    }
    return sumSet.size;
}</code></pre><p>입력값의 제한은 다음과 같습니다.</p>
<ul>
<li>3 &lt;= elements 배열의 길이 &lt;= 1,000</li>
</ul>
<p>배열 elements에 대해 이중 for문을 돌려도 무방하겠다고 생각했습니다.
Set 자료형을 사용한 이유는 당연하지만 중복되는 값을 제거하기 위해서 입니다.
문제의 핵심은 extendedElems 배열을 설정하는 것입니다.
[7, 9, 1, 1, 4, 7, 9, 1, 1] 값을 갖는데 마지막에 4를 추가하지 않은 이유는 연속 부분 수열의 합을 구할 때 [7, 9, 1, 1, 4]가 불필요하게 중복되기 때문입니다.
참고로 sum 변수에 대해 누적값을 할당하는 이유는 다음과 같습니다.</p>
<ul>
<li>시작점 인덱스가 0인 경우<ul>
<li>인덱스 0에 해당하는 수를 세트에 넣고(길이가 1인 부분 수열의 합을 구하고), 여기에 인덱스 1에 해당하는 수를 더해서 세트에 넣고(길이가 2인 부분 수열의 합을 구하고), ..., 여기에 인덱스 4에 해당 하는 수를 더해서 세트에 넣고 (길이가 5인 부분 수열의 합을 구하고)</li>
</ul>
</li>
<li>시작점 인덱스가 1인 경우<ul>
<li>인덱스 0에 해당하는 수를 세트에 넣고(길이가 1인 부분 수열의 합을 구하고), 여기에 인덱스 1에 해당하는 수를 더해서 세트에 넣고(길이가 2인 부분 수열의 합을 구하고), ..., 여기에 인덱스 4에 해당 하는 수를 더해서 세트에 넣고 (길이가 5인 부분 수열의 합을 구하고)</li>
</ul>
</li>
</ul>
<p>...</p>
<ul>
<li>시작점 인덱스가 4인 경우<ul>
<li>(반복)</li>
</ul>
</li>
</ul>
<p>각 과정을 거치면 길이가 0인 부분 수열의 합, 길이가 1인 부분 수열의 합, ..., 길이가 5인 부분 수열의 합의 모든 경우의 수를 따져줄 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 문자열과 숫자 메서드 정리⛵️]]></title>
            <link>https://velog.io/@jason_kim/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%AC%B8%EC%9E%90%EC%97%B4%EA%B3%BC-%EC%88%AB%EC%9E%90-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@jason_kim/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%AC%B8%EC%9E%90%EC%97%B4%EA%B3%BC-%EC%88%AB%EC%9E%90-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 08 Apr 2023 05:10:43 GMT</pubDate>
            <description><![CDATA[<h2 id="문자열-메서드">문자열 메서드</h2>
<blockquote>
<p>💁🏻 <strong>배열 메서드와 중복되는 메서드</strong>
includes(), slice(), indexOf()</p>
</blockquote>
<h4 id="indexof">indexOf</h4>
<ul>
<li><strong>리턴값</strong><ul>
<li>특정 요소의 인덱스 (2개 이상이면 첫 번째 인덱스)</li>
<li>(해당하는 요소가 존재하지 않는 경우) -1<h4 id="includes">includes</h4>
</li>
</ul>
</li>
<li><strong>리턴값 👉🏻 특정 요소가 포함됐는지 여부의 불리언 값</strong><h4 id="startswith">startsWith</h4>
</li>
<li><strong>리턴값 👉🏻 인수로 전달받은 문자열로 시작하는지 여부의 불리언 값</strong><h4 id="endswith">endsWith</h4>
</li>
<li><strong>리턴값 👉🏻 인수로 전달받은 문자열로 끝나는지 여부의 불리언 값</strong><h4 id="chartat">chartAt</h4>
</li>
<li><strong>리턴값</strong><ul>
<li>인수로 전달받은 인덱스에 위치한 문자열</li>
<li>(인덱스가 문자열 길이를 초과한 경우) 빈 문자열<h4 id="substring-slice">substring, slice</h4>
</li>
</ul>
</li>
<li>인자: (startIndex, endIndex)</li>
<li><strong>리턴값 👉🏻 startIndex 이상 endIndex - 1 이하의 부분문자열</strong><h4 id="touppercase-tolowercase">toUpperCase, toLowerCase</h4>
</li>
<li>원본 문자열을 변경하지 않는다.</li>
<li><strong>리턴값</strong><ul>
<li>(toUpperCase) 대문자로 변경된 문자열</li>
<li>(toLowerCase) 소문자로 변경된 문자열<h4 id="trim">trim</h4>
</li>
</ul>
</li>
<li>원본 문자열을 변경하지 않는다.</li>
<li><strong>리턴값 👉🏻 (문자열 앞뒤에 공백 문자가 존재하는 경우) 공백을 제거한 문자열</strong><h4 id="repeat">repeat</h4>
</li>
<li><strong>리턴값</strong><ul>
<li>인수로 전달받은 정수 만큼 반복 연결한 문자열</li>
<li>(인수가 0인 경우 = 디폴트) 빈 문자열</li>
<li>(인수가 음수인 경우) RangeError<h4 id="replace">replace</h4>
</li>
</ul>
</li>
<li>원본 문자열을 변경하지 않는다.</li>
<li><strong>리턴값</strong><ul>
<li>(여러 개인 경우) 검색된 문자열 중 첫 번째만 치환한 문자열</li>
</ul>
</li>
<li>첫 번째 인수로 정규 표현식, 두 번째 인수로 문자열이 올 수 있다.<h4 id="split">split</h4>
</li>
<li><strong>리턴값 👉🏻 분리된 문자열을 담은 배열</strong></li>
<li>인수로 문자열 또는 정규표현식이 올 수 있다.</li>
<li>예제 코드) 문자열을 역순으로 바꾸기<pre><code>function reverseString(str) {
   return str.split(&#39;&#39;).reverse().join(&#39;&#39;)
}</code></pre><h2 id="숫자-메서드">숫자 메서드</h2>
<h4 id="isinteger">isInteger</h4>
</li>
<li><strong>리턴값 👉🏻 전달받은 인수가 정수인지 여부의 불리언 값</strong><h4 id="isnan">isNaN</h4>
</li>
<li><strong>리턴값</strong><ul>
<li>전달받은 숫자가 NaN인지 여부의 불리언 값</li>
<li>(숫자 외 타입인 인수를 전달받은 경우) false<h4 id="tofixed">toFixed</h4>
</li>
</ul>
</li>
<li><strong>리턴값 👉🏻 (전달받은 숫자 자리에서) 반올림 한 문자열</strong><pre><code>(12345.6789).toFixed();   // &quot;12346&quot;
(12345.6789).toFixed(1);  // &quot;12345.7&quot;
(12345.6789).toFixed(2);  // &quot;12345.68&quot;</code></pre><h4 id="tostring">toString</h4>
</li>
<li>디폴트는 10진법을 따른다.</li>
<li><strong>리턴값 👉🏻 (숫자가 문자열로 바뀐 형태) 문자열</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 택배상자]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</guid>
            <pubDate>Tue, 04 Apr 2023 15:20:07 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(order) {
    // ✅ order 배열의 현재 인덱스 정보를 갖는 변수를 추가로 선언한다
    let curIndex = 0;
    let stack = [];    

    for (let i = 1; i &lt;= order.length; i++) {
        stack.push(i);
        while (stack.length &gt; 0 &amp;&amp; order[curIndex] === stack.at(-1)) {
          curIndex++;
          stack.pop();
        }
    }
    return curIndex;
}</code></pre><p>입력값의 제한은 다음과 같습니다.</p>
<ul>
<li>1 &lt;= 배열 order의 길이 &lt;= 1,000,000</li>
</ul>
<p>일단 주어진 order 배열에 대해서 이중 for문을 돌리는 건 무리가 있겠다고 생각했습니다.</p>
<p>보조 컨테이너에 잠시 물건을 두고 조건이 만족하면 꺼낸다는 점에서 스택을 사용해야겠다는 생각이 들었습니다.
스택을 사용하는 점에서 Lv.2 뒤에있는큰수찾기 문제와 비슷함을 느꼈습니다.
핵심은 1부터 5까지 for문을 돌려 해당하는 변수를 stack 배열에 넣는 것입니다.
1부터 5까지 반복문을 돌리는 이유는 박스들이 컨테이너 위에 1부터 5까지 순서대로 놓여있는 상태로 시작하기  때문입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 롤케이크자르기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%A1%A4%EC%BC%80%EC%9D%B4%ED%81%AC%EC%9E%90%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%A1%A4%EC%BC%80%EC%9D%B4%ED%81%AC%EC%9E%90%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Tue, 04 Apr 2023 09:26:04 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(topping) {
    const bro = new Map();
    const tpInfo = new Map();
    topping.forEach(tp =&gt; tpInfo.set(tp, (tpInfo.get(tp) || 0) + 1));

    let answer = 0;
    for (let tp of topping) {
        // ✅ 토핑을 하나씩 확인하면서 해당 토핑 개수를 하나씩 빼준다 (=케익 자르기)
        tpInfo.set(tp, tpInfo.get(tp) - 1);
        // ✅ 롤 케이크에 해당 토핑의 개수가 0인 경우
        if (!tpInfo.get(tp)) tpInfo.delete(tp);
        // ✅ 토핑 종류의 개수는 중요하지 않다 (존재 여부가 중요하기 때문)
        bro.set(tp, true);
        // ✅ 형과 동생 케이크 각각의 토핑 종류 개수를 비교한다
        if (tpInfo.size === bro.size) answer++;
        // ✅ 형의 케이크 토핑 종류가 더 많아지는 경우
        else if (tpInfo.size &lt; bro.size) break;
    }
    return answer;
}</code></pre><p>입력값의 제한은 다음과 같습니다.</p>
<ul>
<li>1 &lt;= 토핑들의 번호가 담긴 배열 topping &lt;= 1,000,000</li>
</ul>
<p>topping 배열에 대해서 이중 for문을 돌리는 건 무리가 있다고 생각했습니다.
그래서 시간복잡도가 O(n)인 완전탐색으로 접근을 시도했습니다.
로직은 애초에 모든 토핑이 나의 것으로 생각하고 전개했습니다.
topping 배열을 앞에서부터 하나씩 살펴보면서 뒤로 이동해 동생의 토핑 종류를 늘리는 식입니다. 
Map 자료형을 사용한 이유는 { 토핑 번호 : 개수 } 형태로 데이터를 관리하기 위해서입니다.
핵심은 동생이 가지게 되는 토핑 종류 개수가 더 많아지는 경우 반복문을 종료하는 것입니다.
동생이 가지게 되는 토핑 종류가 많아지는 순간부터는 내가 더이상 동생보다 많은 종류의 토핑을 가질 수 없기 때문입니다.
참고로 동생이 가지게 될 토핑의 종류는 { 토핑 번호: 개수 }의 형태가 아닌 { 토핑 번호: true } 형태입니다.
동생이 어떤 종류의 토핑을 몇 개 갖는 건 중요하지 않고 그냥 가지고 있다는 사실 자체가 중요하기 때문입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 귤고르기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EA%B7%A4%EA%B3%A0%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EA%B7%A4%EA%B3%A0%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Tue, 04 Apr 2023 08:32:41 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(k, tangerine) {
    // ✅ { 귤의 크기: 해당 크기 귤의 개수 }
    let info = {};
    tangerine.forEach(el =&gt; info[el] = (info[el] || 0) + 1);
    const arr = Object.entries(info).sort((a, b) =&gt; b[1] - a[1]);
    // ✅ std(귤 개수를 저장하는 임시 변수), answer(최소 귤 종류 개수)
    let [std, answer] = [0, 0];
    for (let i = 0; i &lt; arr.length; i++) {
        std += arr[i][1];
        if (std &lt; k) {
            // ✅ 포장한 귤의 개수 &lt; k 인 경우) 귤 종류를 하나 늘린다
            answer += 1;
        } else {
            // ✅ 포장한 귤의 개수 &gt;= k 인 경우) 귤 종류를 하나 늘리고 리턴한다
            return answer + 1;
        }
    }
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>1 &lt;= 귤의 크기를 담은 배열 tangerine &lt;= 100,000</li>
</ul>
<p>배열 tangerine에 대한 이중 for문의 사용은 피해야 겠다고 생각했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 시소짝꿍]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%8B%9C%EC%86%8C%EC%A7%9D%EA%BF%8D</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%8B%9C%EC%86%8C%EC%A7%9D%EA%BF%8D</guid>
            <pubDate>Tue, 04 Apr 2023 08:03:57 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(weights) {
    let answer = 0;
    // ✅ { 몸무게(weight): 사람 수(num) }
    let info = {};
    const RATIOS = [1.5, 4 / 3, 2];

    // ✅ weights 배열의 정보를 info 객체로 정리
    weights.forEach(weight =&gt; info[weight] = (info[weight] || 0) + 1);

    // ✅ info 객체를 배열로 변환 후 순회한다
    for (const [weight, num] of Object.entries(info)) {
        // ✅ 몸무게가 같은 사람들 간 시소 짝꿍 수를 구한다 (조합 공식 활용)
        // ✅ RATIOS 배열에 1을 담지 않은 이유
        answer += num * (num - 1) / 2;
        for (const ratio of RATIOS) {
            if (info[ratio * Number(weight)]) {
                answer += num * info[ratio * Number(weight)];
            }
        }
    }
    return answer;
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>2 &lt;= 사람들의 몸무게를 담은 배열 weights의 길이 &lt;= 100,000</li>
</ul>
<p>배열 weights에 대한 이중 for문 풀이는 힘들다고 생각했다.
약간의 수학적 지식(조합 공식)을 사용했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 뒤에있는큰수찾기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%92%A4%EC%97%90%EC%9E%88%EB%8A%94%ED%81%B0%EC%88%98%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%92%A4%EC%97%90%EC%9E%88%EB%8A%94%ED%81%B0%EC%88%98%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Tue, 04 Apr 2023 07:20:36 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(numbers) {
  let stack = [];
  let answer = new Array(numbers.length).fill(0);

  for (let i = 0; i &lt; numbers.length; i++) {
    // ✅ 해당 인덱스 i에 대해 이전 인덱스에 해당하는 값들과 비교한다
    while (stack.length &gt; 0 &amp;&amp; numbers[stack.at(-1)] &lt; numbers[i]) {
      answer[stack.pop()] = numbers[i];
    }
    // ✅ 스택에는 인덱스 정보가 담긴다
    stack.push(i);
  }

  // ✅ 스택에 인덱스가 남아있다면 해당 인덱스에 대한 값들을 모두 -1로 바꾼다
  while (stack.length &gt; 0) {
    answer[stack.pop()] = -1;
  }

  return answer;
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>4 &lt;= numbers 배열의 길이 &lt;= 1,000,000</li>
</ul>
<p>이중 for문의 사용은 불가하다.
시간복잡도가 O(n)인 알고리즘을 사용해야 된다고 생각했다.
자료구조 <strong>스택</strong>이 떠올랐다.
for문을 돌리면서 배열 numbers의 각 인덱스를 의미하는 i를 스택에 넣는다.
이전 인덱스까지도 따져주기 위해 while문을 돌렸다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Intersection Observer API를 사용한 무한스크롤구현]]></title>
            <link>https://velog.io/@jason_kim/Intersection-Observer-API%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%AC%B4%ED%95%9C%EC%8A%A4%ED%81%AC%EB%A1%A4%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@jason_kim/Intersection-Observer-API%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%AC%B4%ED%95%9C%EC%8A%A4%ED%81%AC%EB%A1%A4%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 04 Apr 2023 02:07:04 GMT</pubDate>
            <description><![CDATA[<p>useRef와 useCallback의 도움을 받아 Intersection Observer API로 무한 스크롤 기능을 구현해봤다.</p>
<h2 id="🔖-정리">🔖 정리</h2>
<p>우선 Intersection Observer API 관련 MDN 문서를 읽고 나서 나름의 정리를 해봤다.
핵심은 다음과 같다.</p>
<pre><code>let observer = new IntersectionObserver(callback, options);
observer.observe(target);</code></pre><ol>
<li>몇가지 옵션을 갖는 <strong>Observer 객체</strong>를 만든다.</li>
<li>생성한 Observer 객체에게 target 요소를 <strong>observe 하도록</strong> 한다.</li>
<li>관찰 중인 요소에 대한 intersection이 발생한 경우 Observer 객체를 생성할 때 옵션과 함께 인자로 넣었던 <strong>콜백함수가 실행</strong>된다.</li>
</ol>
<p>intersection이 발생한 경우, 콜백함수 안에 원하는 일련의 workflow를 작성하면 된다.
options 관련해서는 MDN 문서에 잘 정리되어 있다.</p>
<h2 id="💻-코드">💻 코드</h2>
<p>책 제목을 불러오는 커스텀 훅(useBookSearch)을 사용한 예제 코드다.
한가지 주목할 점은 ref에 callback ref를 전달할 수 있다.
<strong>callback ref</strong> 함수를 통해 ref가 설정되고 해제되는 로직을 세세하게 처리할 수 있다.
추가적인 작업을 할 수 있다! 정도로 알고 있으면 충분할 것 같다(?)
참고로 아래 코드에서 console.log(node); 를 실행하면 해당하는 html 요소를 출력한다.</p>
<p>🎯 아래 코드의 순서는 다음과 같다.</p>
<ol>
<li>(App.js) input 태그에 값을 입력하면 상태(query)가 업데이트 된다.</li>
<li>(useBookSearch.js) 커스텀 훅의 useEffect가 실행된다. &gt; axios 요청 발생</li>
<li>(useBookSearch.js) axios 요청에 대한 응답을 처리한다.</li>
<li>(App.js) isLoading이 false 값을 갖게 되면서 observer(관찰자)를 등록한다. 이때 책 제목 데이터도 동시에 갱신된다.</li>
<li>(App.js) 뷰포트 내에 관찰하고 있는 요소(node)가 들어오게 되면 pageNum 상태를 업데이트 한다. &gt; (useBookSearch.js) useEffect 실행</li>
<li>위와 동일한 로직이 반복된다.</li>
</ol>
<p>아래 App.js 코드 중 disconnect 하는 부분(🔵)을 주의해야 한다.
이 부분을 작성하지 않고 위쪽으로 스크롤을 하면 추가적인 데이터를 불러오게 된다.
(아래쪽이 아니라 위쪽으로 스크롤을 했는데 데이터가 갱신되는 건 확실히 이상하다 😅)
관찰자에 등록된 요소들에 대해 관찰을 중지하지 않았기 때문이다.
따라서 기존에 등록된 요소들에 대한 관찰을 모두 멈춰야 한다.</p>
<pre><code>// 📍 App.js

import { useState, useRef, useCallback } from &quot;react&quot;;
import useBookSearch from &quot;./useBookSearch&quot;;
import { ClipLoader } from &quot;react-spinners&quot;;

function App() {
  const [query, setQuery] = useState(&quot;&quot;);
  const [pageNum, setPageNum] = useState(1);
  const { books, isLoading, isError, hasMore } = useBookSearch(query, pageNum);
  const observer = useRef();

  const lastBookElementRef = useCallback(
    node =&gt; {
      // 🟠 로딩 중에는 아래의 로직 실행을 방지
      if (isLoading) return;

      // ✅ 4. 관찰자 등록
      if (observer.current) observer.current.disconnect(); // 🔵
      observer.current = new IntersectionObserver(entries =&gt; {
        if (entries[0].isIntersecting &amp;&amp; hasMore) {
          // ✅ 5. 상태(pageNum) 업데이트 -&gt; 커스텀 훅(useBookSearch) useEffect 실행
          setPageNum(prev =&gt; prev + 1);
        }
      });
      if (node) observer.current.observe(node);
    },
    [isLoading, hasMore]
  );

  function handleSearch(e) {
    setQuery(e.target.value);
  }

  return (
    &lt;&gt;
      {/* ✅ 1. 상태(query) 업데이트 */}
      &lt;input type=&quot;text&quot; value={query} onChange={handleSearch} /&gt;
      {books.map((book, idx) =&gt; {
        if (books.length === idx + 1) {
          return (
            // 🟠 마지막 데이터인 경우에 예외 처리
            &lt;div key={idx} ref={lastBookElementRef}&gt;
              {book}
            &lt;/div&gt;
          );
        }
        return &lt;div key={idx}&gt;{book}&lt;/div&gt;;
      })}
      &lt;div&gt;{isLoading &amp;&amp; &lt;ClipLoader /&gt;}&lt;/div&gt;
      &lt;div&gt;{isError &amp;&amp; &quot;Error&quot;}&lt;/div&gt;
    &lt;/&gt;
  );
}

export default App;
</code></pre><pre><code>// 📍 useBookSearch.js

import { useState, useEffect } from &quot;react&quot;;
import axios from &quot;axios&quot;;

export default function useBookSearch(query, pageNum) {
  const [isLoading, setIsLoading] = useState(true);
  const [isError, setIsError] = useState(false);
  const [books, setBooks] = useState([]);
  const [hasMore, setHasMore] = useState(false);

  useEffect(() =&gt; {
    setBooks([]);
  }, [query]);

  useEffect(() =&gt; {
    setIsLoading(true);
    setIsError(false);
    let cancel;
    axios({
      method: &quot;GET&quot;,
      url: &quot;http://openlibrary.org/search.json&quot;,
      params: { q: query, page: pageNum },
      // 🟠 axios 요청을 취소 (다른 요청을 보내는 중이라면)
      cancelToken: new axios.CancelToken(c =&gt; (cancel = c)),
    })
      .then(res =&gt; {
        // ✅ 3. 새로운 axios 요청에 대한 응답 처리
        setBooks(prevBooks =&gt; [
          ...new Set([...prevBooks, ...res.data.docs.map(book =&gt; book.title)]),
        ]);
        setHasMore(res.data.docs.length &gt; 0);
        setIsLoading(false);
      })
      .catch(err =&gt; {
        // 🟠 axios 요청 취소 시 발생하는 에러 처리
        if (axios.isCancel(err)) return;
        setIsError(true);
      });
    return () =&gt; cancel();
    // ✅ 2. 상태(query) 업데이트로 인해 useEffect 실행
  }, [query, pageNum]);

  return { isLoading, isError, books, hasMore };
}</code></pre><h2 id="💁🏻-결과">💁🏻 결과</h2>
<p><img src="https://velog.velcdn.com/images/jason_kim/post/41a2bbed-b7a7-47a9-bb40-4907a32e7461/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 숫자변환하기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%88%AB%EC%9E%90%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EC%88%AB%EC%9E%90%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 03 Apr 2023 11:52:21 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(x, y, n) {
    if (x === y) return 0;
    const dp = {};
    dp[x] = 0;
    let check = [x];

    while (check.length &gt; 0) {
        const newCheck = [];
        for (const std of check) {
            for (const tmp of [std + n, std * 2, std * 3]) {
                if (dp[tmp] || tmp &gt; y) continue;
                if (tmp === y) return dp[std] + 1;
                dp[tmp] = dp[std] + 1;
                newCheck.push(tmp);
            }
        }
        check = newCheck;
    }
    return -1;
}</code></pre><p>완전탐색 방법도 생각해봤지만 동일한 값에 대한 계산 과정이 중복될 것 같아서 DP 알고리즘을 사용하기로 했다.
따져줘야 하는 수는 check 배열에 넣어주고 계속 따져주는 식으로 로직이 진행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 마법의엘리베이터]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%A7%88%EB%B2%95%EC%9D%98%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.2-%EB%A7%88%EB%B2%95%EC%9D%98%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Mon, 03 Apr 2023 11:19:35 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(storey) {
    let answer = 0;
    let arr = [...String(storey)].map(el =&gt; +el);
    for (let i = arr.length - 1; i &gt;= 0; i--) {
        if (arr[i] &gt; 5) {
            if (i === 0) answer += 1;
            answer += (10 - arr[i]);
            arr[i - 1] += 1;
        } else if (arr[i] === 5 &amp;&amp; arr[i - 1] &gt;= 5) {
            answer += 5;
            arr[i - 1] += 1;
        } else {
            answer += arr[i];
        }
    }
    return answer;
}</code></pre><p>쉬운 듯 어려운 문제다.
경계를 잘 따져야봐야 겠다는 생각을 한 문제다.
일단 입력값의 제한을 보면 다음과 같다.</p>
<ul>
<li>층수가 총 1억(=10의 8승) 층까지 있다. 반복문을 돌려도 최대 8이다.</li>
</ul>
<p>완전탐색으로 접근해도 무방한 문제라고 생각했다.
문제는 탐색을 진행하면서 경우의 수를 따져주는 것이다.</p>
<ul>
<li>1) 해당 자리의 수가 5보다 큰 경우</li>
<li>2) 해당 자리의 수가 5 이거나 이전 자리의 수가 5보다 크거나 같은 경우</li>
<li>3) 그 외의 경우</li>
</ul>
<p>위 3가지 경우의 수를 떠올릴 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.2 무인도 여행]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%AC%B4%EC%9D%B8%EB%8F%84-%EC%97%AC%ED%96%89</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%AC%B4%EC%9D%B8%EB%8F%84-%EC%97%AC%ED%96%89</guid>
            <pubDate>Mon, 03 Apr 2023 10:47:59 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(maps) {
    let answer = [];
    // ✅ 문자열을 모두 숫자로 바꿔준다
    maps = maps
        .map(x =&gt; x.split(&#39;&#39;).map(y =&gt; {
        if (y === &#39;X&#39;) return 0;
        else return +y;
    }));
    // ✅ 행과 열의 개수
    const [r, c] = [maps.length, maps[0].length];
    // ✅ 상, 하, 좌, 우
    const dx = [0, 1, 0, -1];
    const dy = [-1, 0, 1, 0];

    // ✅ 깊이우선탐색(DFS) 함수
    function dfs(x, y) {
        maps[x][y] = 0;
        for (let i = 0; i &lt; 4; i++) {
            let nx = x + dx[i];
            let ny = y + dy[i];
            if (nx &gt;= 0 &amp;&amp; ny &gt;= 0 &amp;&amp; nx &lt; r &amp;&amp; ny &lt; c &amp;&amp; maps[nx][ny] !== 0) {
                tmp += maps[nx][ny];
                dfs(nx, ny);
            }
        }
    }

    // ✅ 문제풀이 시작
    let tmp;
    for (let i = 0; i &lt; r; i++) {
        for (let j = 0; j &lt; c; j++) {
            // ✅ 하나의 섬에 대해 연산이 끝난 경우에는 식량의 총합을 초기화 한다
            tmp = 0;
            if (maps[i][j] !== 0) {
                tmp += maps[i][j];
                dfs(i, j);
                if (tmp !== 0) answer.push(tmp);    
            }
        }
    }
    return answer.length &gt; 0 ? answer.sort((a, b) =&gt; a - b) : [-1];
}</code></pre><p>2 by 2 행렬을 보고 나서 바로 DFS나 BFS 탐색 알고리즘이 떠올랐다.
풀이는 DFS 알고리즘을 사용했다.
조건을 만족하는 지점들을 끝까지 파고 들면서 방문한 지점은 방문 처리를 해줬다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 크레인인형뽑기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%ED%81%AC%EB%A0%88%EC%9D%B8%EC%9D%B8%ED%98%95%EB%BD%91%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%ED%81%AC%EB%A0%88%EC%9D%B8%EC%9D%B8%ED%98%95%EB%BD%91%EA%B8%B0</guid>
            <pubDate>Mon, 03 Apr 2023 10:33:49 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function pickDoll(board, order) {
    for (let i = 0; i &lt; board.length; i++) {
        // ✅ 뽑힌 인형이 있다면
        if (!!board[i][order]) {
            let target = board[i][order];
            board[i][order] = 0;
            return target;
        }
    }
}

function solution(board, moves) {
    const stack = [];
    let answer = 0;

    moves.forEach(el =&gt; {
        const pickedDoll = pickDoll(board, el - 1);
        // ✅ 뽑힌 인형이 있다면
        if (pickedDoll) {
            // ✅ 이전 인형과 뽑힌 인형이 같은 경우
            if (stack[stack.length - 1] === pickedDoll) {
                stack.pop();
                answer += 2;
            } else {
                // ✅ 이전 인형과 뽑힌 인형이 같지 않은 경우
                stack.push(pickedDoll);
            }
        }
    });

    return answer;
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>5 &lt;= board 한 변의 길이 &lt;= 30</li>
<li>1 &lt;= moves 배열 &lt;= 1,000</li>
</ul>
<p>어떤 알고리즘을 사용해도 무방하다고 생각했다.
배열 board은 2 by 2 행렬의 형태다.
그렇기 때문에 pickDoll 함수를 새로 생성해서 중첩된 배열을 하나씩 살펴보면서 특정 행의 특정 열(order)이 0이 아닌 경우를 찾는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 다트게임]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%8B%A4%ED%8A%B8%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%8B%A4%ED%8A%B8%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Mon, 03 Apr 2023 09:18:12 GMT</pubDate>
            <description><![CDATA[<p><strong>2018 카카오 블라인드 채용</strong></p>
<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(dartResult) {
    let arr = [];
    let score;
    for (let i = 0; i &lt; dartResult.length; i++) {
        if (!isNaN(dartResult[i])) {
            // ✅ 숫자인 경우
            score = Number(dartResult[i - 1]) === 1 ? 10 : Number(dartResult[i]);
            continue;
        }

        switch(dartResult[i]) {
            case &#39;S&#39;:
                arr.push(score);
                break;
            case &#39;D&#39;:
                arr.push(score ** 2);
                break;
            case &#39;T&#39;:
                arr.push(score ** 3);
                break;
            case &#39;*&#39;:
                arr[arr.length - 2] *= 2;
                arr[arr.length - 1] *= 2;
                break;
            case &#39;#&#39;:
                arr[arr.length - 1] *= -1;
                break;
        }
    }
    return arr.reduce((prev, curr) =&gt; prev + curr, 0);
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 이상한문자만들기]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%9D%B4%EC%83%81%ED%95%9C%EB%AC%B8%EC%9E%90%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%9D%B4%EC%83%81%ED%95%9C%EB%AC%B8%EC%9E%90%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 03 Apr 2023 09:05:29 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(s) {
    return s
        .split(&#39; &#39;)
        .map(word =&gt; word
             .split(&#39;&#39;)
             .map((el, idx) =&gt; idx % 2 === 0
                  ? el.toUpperCase()
                  : el.toLowerCase())
             .join(&#39;&#39;))
        .join(&#39; &#39;);
}</code></pre><p>이중 map을 사용한 로직의 흐름이다.</p>
<ul>
<li>문장을 단어로 나눈다 &gt; 단어를 알파벳 별로 나눈다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 실패율]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%8B%A4%ED%8C%A8%EC%9C%A8</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%8B%A4%ED%8C%A8%EC%9C%A8</guid>
            <pubDate>Mon, 03 Apr 2023 09:02:33 GMT</pubDate>
            <description><![CDATA[<p><strong>2019 카카오 블라인드 채용</strong></p>
<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(N, stages) {
    const failRate = {};
    let [std, players] = [1, stages.length];

    while (std &lt;= N) {
        let len = stages.filter(stage =&gt; stage === std).length;
        failRate[std] = (len / players);
        players -= len;
        std++;    
    }
    let arr = Object.entries(failRate);

    return arr
        .sort((a, b) =&gt; b[1] - a[1])
        .map(el =&gt; +el[0]);
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>1 &lt;= 스테이지 개수 N &lt;= 500</li>
<li>1 &lt;= 배열 stages(유저 목록) &lt;= 200,000</li>
</ul>
<p>이중 for문 사용까지는 무난하다고 생각했다.
처음에는 실패율의 의미를 이해하기 어려웠는데 파악하고 나니까 문제 풀이는 어렵지 않았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 비밀지도]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%B9%84%EB%B0%80%EC%A7%80%EB%8F%84</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%B9%84%EB%B0%80%EC%A7%80%EB%8F%84</guid>
            <pubDate>Mon, 03 Apr 2023 08:43:16 GMT</pubDate>
            <description><![CDATA[<p><strong>2018 카카오 블라인드 채용</strong></p>
<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(n, arr1, arr2) {
    const map1 = arr1.map(el =&gt; el.toString(2).padStart(n, &#39;0&#39;).split(&#39;&#39;).map(el =&gt; {
        if (!+el) return &#39; &#39;;
        return &#39;#&#39;;
    }));
    const map2 = arr2.map(el =&gt; el.toString(2).padStart(n, &#39;0&#39;).split(&#39;&#39;).map(el =&gt; {
        if (!+el) return &#39; &#39;;
        return &#39;#&#39;;
    }))

    return map1
        .map((_, i) =&gt; map2[i].map((_, j) =&gt; {
            return map1[i][j] === &#39;#&#39; || map2[i][j] === &#39;#&#39; ? &#39;#&#39; : &#39; &#39;;
        }))
        .map(el =&gt; el.join(&#39;&#39;));
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>1 &lt;= 지도 한 변의 길이 &lt;= 16</li>
</ul>
<p>딱히 특정 알고리즘을 지양해야겠다고 생각하지는 않았다.
<strong>이중 배열</strong>을 처리할 때는 <strong>이중으로 map</strong>을 사용하자.
✨ 참고로 문자열 메서드인 <strong>padStart()</strong>와 <strong>padEnd()</strong>를 알아두자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 완주하지못한선수]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80%EB%AA%BB%ED%95%9C%EC%84%A0%EC%88%98</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80%EB%AA%BB%ED%95%9C%EC%84%A0%EC%88%98</guid>
            <pubDate>Mon, 03 Apr 2023 08:28:24 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(participant, completion) {
    const runners = {};

    participant.forEach(runner =&gt; {
        runners[runner] = (runners[runner] || 0) + 1;
    })

    completion.forEach(runner =&gt; {
        runners[runner] -= 1;
    })

    for (const key in runners) {
        if (runners[key] === 1) return key;
    }
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>1 &lt;= 마라톤에 참가한 선수 명단 배열 participant &lt;= 100,000</li>
<li>완주한 선수 명단 배열 completion의 길이 = participant의 길이 - 1</li>
</ul>
<p>이중 for문의 사용은 불가능하다고 생각했다.
처음에는 중복된 선수의 이름이 존재한다는 사실을 망각하고 배열을 사용한 답안코드를 작성했다.
</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 모의고사]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC</guid>
            <pubDate>Mon, 03 Apr 2023 08:10:50 GMT</pubDate>
            <description><![CDATA[<pre><code>function solution(answers) {
    const one = [1, 2, 3, 4, 5];
    const two = [2, 1, 2, 3, 2, 4, 2, 5];
    const three = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5];
    let cnt = [0, 0, 0];
    let answer = [];
    for (let i = 0; i &lt; answers.length; i++) {
        if (answers[i] === one[i % one.length]) cnt[0]++;
        if (answers[i] === two[i % two.length]) cnt[1]++;
        if (answers[i] === three[i % three.length]) cnt[2]++;
    }
    const maxCnt = Math.max(cnt[0], cnt[1], cnt[2]);
    for (let i = 0; i &lt; 3; i++) {
        if (cnt[i] === maxCnt) answer.push(i + 1);
    }
    return answer;
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>배열 answer &lt;= 10,000</li>
</ul>
<p>이중 배열을 사용한 문제풀이는 불가능하다고 생각했다.
문제풀이의 핵심은 각 수포자가 답을 찍는 패턴을 배열로 선언해두는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 Lv.1 체육복]]></title>
            <link>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%B2%B4%EC%9C%A1%EB%B3%B5</link>
            <guid>https://velog.io/@jason_kim/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-Lv.1-%EC%B2%B4%EC%9C%A1%EB%B3%B5</guid>
            <pubDate>Mon, 03 Apr 2023 08:03:28 GMT</pubDate>
            <description><![CDATA[<h2 id="💁🏻-코드">💁🏻 코드</h2>
<pre><code>function solution(n, lost, reserve) {
    const students = new Set(Array(n).fill(0).map((_, idx) =&gt; idx + 1));
    // ✅ 정렬해야 최대한의 학생이 체육 수업을 들을 수 있게 된다
    // ✅ 반례) reserveSet = [4, 2] &amp; losetSet = [3, 5]
    const lostSet = new Set(lost.sort((a, b) =&gt; a - b));
    const reserveSet = new Set(reserve.sort((a, b) =&gt; a - b));

    // ✅ 체육복을 잃어버렸지만 여분을 갖고 있는 경우에는 빌려주기 전에 자급자족해야 한다
    lost.forEach(el =&gt; {
        if (reserveSet.has(el)) {
            reserveSet.delete(el);
            lostSet.delete(el);
        }
    })

    lost.forEach(el =&gt; {
        if (reserveSet.has(el - 1)) {
            reserveSet.delete(el - 1);
            lostSet.delete(el);
        } else if (reserveSet.has(el + 1)) {
            reserveSet.delete(el + 1);
            lostSet.delete(el);
        }
    })

    return n - [...lostSet].length;
}</code></pre><p>입력값의 제한은 다음과 같다.</p>
<ul>
<li>2 &lt;= 전체 학생의 수 n &lt;= 30</li>
</ul>
<p>어떤 알고리즘을 써도 무방하다고 생각했다.
이 문제에서 핵심은 체육복을 잃어버린 학생들의 번호가 담긴 배열과 여분의 체육복을 갖고 있는 학생들의 번호가 담긴 배열을 <strong>정렬해야 한다</strong>는 사실이다.</p>
]]></description>
        </item>
    </channel>
</rss>