Javascript - Algorithm

Javascript - Algorithm

자바스크립트를 사용하여 알고리즘 문제를 풀고 기록합니다.

📈 프로그래머스 코딩테스트 고득점 Kit

코딩테스트에는 어떤 알고리즘/자료구조가 출제될까요? 사람들은 어떤 문제를 어려워할까요? 국내에서 코딩테스트를 가장 많이 운영해온 프로그래머스 팀이 코딩테스트 결과를 분석해서 만든 고득점 Kit. 코딩테스트에 자주 나오는 유형, 사람들이 많이 틀리는 유형을 간추렸습니다.

🙂 해시

폰켓몬

/* redefine(재정의) */

// input : nums(폰켓몬의 종류 번호가 담긴 1차원 Array)
// return : N/2마리의 폰켓몬을 선택하는 방법 중, 최대로 가질 수 있는 포켓몬 종류 수

// condition : N은 짝수 -> N/2는 항상 정수(parseInt 생략 가능)

// algorithm : 해시 - set




/* solution(구현) */

function solution(nums) {
    
    const N = nums.length;           // nums의 길이
    const nums_set = new Set(nums);  // 폰켓몬의 종류 set

    //  nums_set의 size는 폰켓몬 종류 수
    //  (폰켓몬 종류 수 >= 폰켓몬 개수) ? 폰켓몬 개수 : 폰켓몬 종류 수
    answer = Math.min(nums_set.size, N/2);
    
    return answer;
}




/* check(검증) - Big O Notation */

// time complexity  : O(1)
// space complexity : O(n)

위장

/* redefine(재정의) */

// input : clothes(스파이 의상 2차원 Array)
// return : 서로 다른 옷의 조합 수

// condition1 : 의상의 수는 [1,30]
// condition2 : 의상의 이름은 유일
// condition3 : 문자열 길이 [1,20]
// condition4 : 최소 한 개의 의상을 입어야 함

// algorithm : 해시 - object

// logic(psuedo) :
// 1. clothType을 키로 하는 해시 테이블을 구현한다. {A:a, B:b, C:c, D:d}
// 2. 해시 테이블 생성과 동시에 키 리스트(clothesType)도 생성한다. {A, B, C, D}
// 3. clothesType의 요소를 순회하면서 경우의 수를 구한다(키가 존재하지 않는다면 값에 0 대입). ->  (a+1)*(b+1)*(c+1)*(d+1) - 1




/* solution(구현) */

function solution(clothes) {
    let clothesDict = {};
    let clothesType = [];
    
    let answer = 1;
    
    for (let [clothName, clothType] of clothes) {
        if (!clothesDict[clothType]) {
            clothesDict[clothType] = 0
            clothesType.push(clothType);
        }
        clothesDict[clothType] += 1
    }

    for (let k of clothesType) {
        answer *= clothesDict[k] + 1
    }
    
    
    return answer - 1;
}




/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n)

완주하지 못한 선수

/* redefine(재정의) */

// input1 : participant(마라톤 참여한 선수들 이름이 담긴 Array)
// input2 : completion(마라톤 완주한 선수들 이름이 담긴 Array)
// return : 마라톤을 완주하지 못한 선수들의 이름을 문자열로 return

// condition1 : 참가자 이름은 알파벳 소문자(20자내)
// condition2 : 참가자 동명이인 가능
// condition3 : 완주 못 한 참가자는 1명
// condition4 : participant.length <= 100,000

// algorithm : 해시 - object

// logic(psuedo) : 
// 1. 참가자 명을 키로 하는 해시 테이블을 구현한다.
// 2. 참가 +1, 완주 -1 하며 값을 계속 갱신한다
// 3. 순회하면서 값이 0을 초과하는 키를 반환한다.




/* solution(구현) */

function solution(participant, completion) {
    
    const participantDict = {};

    for (let i in participant) {
        if (!participantDict[participant[i]]) {
            participantDict[participant[i]] = 0;
        }
        participantDict[participant[i]] += 1
        if (!participantDict[completion[i]]) {
            participantDict[completion[i]] = 0;
        }
        participantDict[completion[i]] -= 1
    }

    for (let k of Object.keys(participantDict)) {    // Object.keys : O(n)
        if (participantDict[k] > 0) {
            return k;
        }
    }
}




/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(n)




/* improvements(개선점) */
// completion 배열의 길이가 participant 배열의 길이보다 1 작기 때문에,
// 마지막 n번째 순회 시 의도하지 않은 키-값 쌍(undefined: -1)이 participantDict에 추가된다.
// participantDict[k] > 0을 만족하지 않아 문제 풀이에 큰 영향은 없지만 useless code이므로 예외처리(82)

function solution(participant, completion) {
    
    const participantDict = {};

    for (let i in participant) {
        if (!participantDict[participant[i]]) {
            participantDict[participant[i]] = 0;
        }
        participantDict[participant[i]] += 1
        if (!participantDict[completion[i]]) {
            participantDict[completion[i]] = 0;
        }
        participantDict[completion[i]] -= 1
    }

    delete participantDict.undefined;    // 예외 처리 : participantDict의 프로퍼티 키가 Undefined인 속성 delete(O(1))

    for (let k of Object.keys(participantDict)) {
        if (participantDict[k] > 0) {
            return k;
        }
    }
}




/* recheck(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(n)

🙂 탐욕법

구명보트

/* redefine(재정의) */

// input1 : people(사람 몸무게를 담은 1차원 Array)
// input2 : limit(최대 하중 Number)
// return : 필요한 구명보트 개수의 최솟값 Number

// condition1 : people 길이 : [1, 50000]
// condition2 : people 요소 크기 : [40, 240]
// condition3 : limit 크기 : [40,240]
// condition4 : people 요소 크기 최댓값 < limit 
// condition5 : 구명보트 최대 탑승인원 2인

// <condition5> : people은 비정렬 상태

// algorithm : 그리디, 큐?

// logic(psuedo) : 
// 1. 오름차순 정렬
// 2. 만약, 가장 가벼운 사람 + 기장 무거운 사람 <= left++, right-- (2인 탑승)
// 2. 만약, 가장 가벼운 사람 + 가장 무거운 사람 > right-- (1인: 가장 무거운 사람 탑승)
// 3. 예외 처리
//    마지막에 남은 인원이 모두 탑승한 경우(2인 탑승) 구명보트 추가 X

/* solution(구현) */

function solution(people, limit) {
    
    let cnt = 1;                   // 구명보트 개수 1로 초기화

    let left = 0;
    let right = people.length-1;

    people.sort((a, b) => a - b);  // people 내림차순 정렬

    while (left < right) {   
        if (people[left] + people[right] <= limit) { 
            left++;
            if (left === right) cnt--;
        }
        right--;
        cnt++;  // 다음 구명보트 준비
    }
    
    return cnt;
}

/* 결과 */
// 정확성  테스트
// 테스트 1 〉    통과 (1.94ms, 35.5MB)
// 테스트 2 〉    통과 (1.07ms, 33.6MB)
// 테스트 3 〉    통과 (1.16ms, 33.7MB)
// 테스트 4 〉    통과 (1.11ms, 33.6MB)
// 테스트 5 〉    통과 (0.76ms, 33.6MB)
// 테스트 6 〉    통과 (0.42ms, 33.7MB)
// 테스트 7 〉    통과 (0.60ms, 33.6MB)
// 테스트 8 〉    통과 (0.16ms, 33.6MB)
// 테스트 9 〉    통과 (0.20ms, 33.7MB)
// 테스트 10 〉 통과 (1.86ms, 33.6MB)
// 테스트 11 〉 통과 (1.79ms, 33.6MB)
// 테스트 12 〉 통과 (0.91ms, 33.7MB)
// 테스트 13 〉 통과 (1.08ms, 33.6MB)
// 테스트 14 〉 통과 (1.25ms, 33.8MB)
// 테스트 15 〉 통과 (0.21ms, 33.7MB)
// 효율성  테스트
// 테스트 1 〉    통과 (14.94ms, 38.3MB)
// 테스트 2 〉    통과 (12.88ms, 38.2MB)
// 테스트 3 〉    통과 (62.95ms, 37.8MB)
// 테스트 4 〉    통과 (11.53ms, 38.5MB)
// 테스트 5 〉    통과 (10.42ms, 38.1MB)




/* check(검증) - Big O Notation */

// time complexity  : O(nlogn)
// space complexity : O(1)

// 위 코드의 시간 복잡도는 O(nlogn) 이다.
// 이 코드는 먼저 people 배열을 내림차순으로 정렬하기 위해 sort() 함수를 사용하는데 이는 시간 복잡도가 O(nlogn) 이다. 
// 그리고 while문에서는 left와 right 포인터를 이용하여 인덱스를 이동하며 구명보트를 결정하는데 이는 O(n) 이다.
// 그리고 공간 복잡도는 O(1) 이다.
// 코드는 people 배열만을 사용하고 정렬된 people 배열을 저장하는 공간을 추가로 사용하지 않기 때문에, 추가적인 메모리 공간을 사용하지 않는다.

조이스틱

/* redefine(재정의) */

// input : name(만들고자 하는 이름 String)
// return : 조이스틱 조작 횟수의 최솟값(Number)

// condition1 : name은 모두 알파벳 대문자(65~)
// condition2 : name 길이 : [1,20]

// algorithm : 그리디


/* solution(구현) */

function solution(name) {
    let cnt = 0;
    
    let start = "A".charCodeAt();    // A의 아스키코드 
    let end = "Z".charCodeAt() + 1;  // B의 아스키코드 + 1
    let center = (start + end) / 2;  // 중간
    
    let move = name.length - 1;      // 좌우 초기값 설정
    
    for (let i = 0; i < name.length; i++) {
        
        let charCode = name.charCodeAt(i);    // 위/아래 조작 방향 선택하기
        (charCode < center) ? cnt += charCode - start : cnt += end - charCode
        
        let indexNotA = i + 1;    // 다음 커서부터 조작해야하는 문자 찾기
        while (indexNotA < name.length && name[indexNotA] == 'A') { indexNotA++; }
        
        move = Math.min(
            move,                               // 이전 값
            (i * 2) + name.length - indexNotA,  // 정방향 -> 역방향 
            (name.length - indexNotA) * 2 + i   // 역방향 -> 정방향
        )
    }

    return cnt + move;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.06ms, 33.4MB)
// 테스트 2 〉    통과 (0.06ms, 33MB)
// 테스트 3 〉    통과 (0.06ms, 33.1MB)
// 테스트 4 〉    통과 (0.07ms, 33.4MB)
// 테스트 5 〉    통과 (0.07ms, 33.4MB)
// 테스트 6 〉    통과 (0.06ms, 33.5MB)
// 테스트 7 〉    통과 (0.07ms, 33.4MB)
// 테스트 8 〉    통과 (0.06ms, 33.4MB)
// 테스트 9 〉    통과 (0.08ms, 33.4MB)
// 테스트 10 〉 통과 (0.15ms, 33.4MB)
// 테스트 11 〉 통과 (0.15ms, 33.5MB)
// 테스트 12 〉 통과 (0.15ms, 33.4MB)
// 테스트 13 〉 통과 (0.15ms, 33.4MB)
// 테스트 14 〉 통과 (0.15ms, 33.4MB)
// 테스트 15 〉 통과 (0.16ms, 33.5MB)
// 테스트 16 〉 통과 (0.06ms, 33.4MB)
// 테스트 17 〉 통과 (0.06ms, 32.8MB)
// 테스트 18 〉 통과 (0.14ms, 32.8MB)
// 테스트 19 〉 통과 (0.07ms, 33.4MB)
// 테스트 20 〉 통과 (0.15ms, 33.4MB)
// 테스트 21 〉 통과 (0.06ms, 33.4MB)
// 테스트 22 〉 통과 (0.15ms, 33.4MB)
// 테스트 23 〉 통과 (0.21ms, 33.4MB)
// 테스트 24 〉 통과 (0.07ms, 33.4MB)
// 테스트 25 〉 통과 (0.16ms, 33.5MB)
// 테스트 26 〉 통과 (0.14ms, 33.5MB)
// 테스트 27 〉 통과 (0.14ms, 33.4MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(1)

체육복

/* redefine(재정의) */

// input1 : n(전체 학생들의 수)
// input2 : lost(체육복을 도난당한 학생들의 번호(Number)가 담긴 1차원 Array)
// input3 : reserve(여벌옷을 가지고 있는 학생들의 번호(Number)가 담긴 1차원 Array)
// return : 체육 수업을 들을 수 있는 학생들의 수(Number)

// condition1 : 전체 학생 수 : [2,30]
// condition2 : lost, reserve 모두 length : [1,n], 중복X
// condition3 : 여벌 옷을 가지고 온 학생들도 도난(두 개 중 하나만)당할 수 있음

// algorithm : 그리디

// logic(psuedo) : 잎 반호의 학생을 우선적으로 탐색




/* solution(구현) */

function solution(n, lost, reserve) {
    
    const studentClothes = new Array(n).fill(1);             // 기본 값 설정(1) 
     
    lost.map(num => {studentClothes[num - 1] = 0});      // 도난 옷 갱신(-1)
    reserve.map(num => {studentClothes[num - 1] += 1});  // 여벌 옷 갱신(+1)

    let cnt = n;
    for(let num=0; num < n; num++) {
        
        if (studentClothes[num] === 0) {               // 해당 번호의 학생이 체육복이 없다면,             
            studentClothes[num] = 1;                   // 여벌의 옷을 받는다고 가정(+1)
            
            if (studentClothes[num - 1] === 2) {       // 앞 번호의 학생에게 여벌 옷이 있다면,
                studentClothes[num - 1] = 1;           // 앞 번호의 학생 1로 초기화(-1)
            }
            else if (studentClothes[num + 1] === 2) {  // 뒤 번호의 학생에게 여벌 옷이 있다면,
                studentClothes[num + 1] = 1;           // 뒤 번호의 학생 1로 초기화(-1)
            }
            else {                                     // 앞, 뒤 번호의 학생 모두 여벌 옷이 없다면,
                studentClothes[num] = 0;               // 현재 번호의 학생 0으로 초기화
                cnt--;                                 // 체육 수업을 듣는 학생 수 감소(1)
            }
        }
    }
    
    return cnt;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.08ms, 33.4MB)
// 테스트 2 〉    통과 (0.17ms, 33.6MB)
// 테스트 3 〉    통과 (0.16ms, 33.4MB)
// 테스트 4 〉    통과 (0.08ms, 33.4MB)
// 테스트 5 〉    통과 (0.15ms, 33.5MB)
// 테스트 6 〉    통과 (0.07ms, 33.4MB)
// 테스트 7 〉    통과 (0.16ms, 33.4MB)
// 테스트 8 〉    통과 (0.07ms, 33.4MB)
// 테스트 9 〉    통과 (0.08ms, 33.4MB)
// 테스트 10 〉 통과 (0.17ms, 33.4MB)
// 테스트 11 〉 통과 (0.08ms, 33.4MB)
// 테스트 12 〉 통과 (0.07ms, 33.5MB)
// 테스트 13 〉 통과 (0.08ms, 33.4MB)
// 테스트 14 〉 통과 (0.08ms, 33.4MB)
// 테스트 15 〉 통과 (0.07ms, 33.4MB)
// 테스트 16 〉 통과 (0.07ms, 33.5MB)
// 테스트 17 〉 통과 (0.07ms, 33.5MB)
// 테스트 18 〉 통과 (0.07ms, 33.4MB)
// 테스트 19 〉 통과 (0.07ms, 33.5MB)
// 테스트 20 〉 통과 (0.07ms, 33.6MB)
// 테스트 21 〉 통과 (0.07ms, 33.1MB)
// 테스트 22 〉 통과 (0.07ms, 33.5MB)
// 테스트 23 〉 통과 (0.07ms, 33.4MB)
// 테스트 24 〉 통과 (0.07ms, 33.4MB)
// 테스트 25 〉 통과 (0.07ms, 33.4MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n)

큰 수 만들기

/* redefine(재정의) */

// input : number(문자열 형식의 숫자 String)
// return : k(제거할 개수)

// condition1 : number 길이 : [2,1000000]
// condition2 : k 크기 : [1, number 길이)

// algorithm : 그리디

// logic(psuedo) : 부분배열에서 최댓값을 구하고 필요없는 값들은 제거하는 방식을 반복




/* solution(구현) - 시간초과(10, 12) */

function solution(number, k) {
    let answer = '';
    let numList = [...number];
    let startIdx = 0;
    let delCnt = 0;
    
    let ord = 0;
    while (k - delCnt > 0) {
        ord++;
        let maxIdx = 0;
        let maxNum = -1;
        
        for (let i = startIdx; i < startIdx + k - delCnt + 1; i++) {
            if (numList[i] > maxNum) {
                maxNum = numList[i];
                maxIdx = i;
                if (maxNum === 9) break;  // 예외 처리
            }
        }

        answer += maxNum;
        startIdx = maxIdx + 1;
        delCnt = startIdx - ord;

    }
    
    if (startIdx - 1 < number.length) { answer += number.substr(startIdx) }
    
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.06ms, 33.4MB)
// 테스트 2 〉    통과 (0.15ms, 33.3MB)
// 테스트 3 〉    통과 (0.16ms, 33.4MB)
// 테스트 4 〉    통과 (0.18ms, 33.4MB)
// 테스트 5 〉    통과 (0.51ms, 33.4MB)
// 테스트 6 〉    통과 (82.85ms, 36.8MB)
// 테스트 7 〉    통과 (168.04ms, 37.3MB)
// 테스트 8 〉    통과 (1290.72ms, 38.1MB)
// 테스트 9 〉    통과 (5.32ms, 38.5MB)
// 테스트 10 〉 실패 (시간 초과)
// 테스트 11 〉 통과 (0.08ms, 33.4MB)
// 테스트 12 〉 실패 (시간 초과)



/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(n)




/* improvements(개선점) */

// 1. numList = [...number]을 생성하지 말고 charAt()을 이용하여 문자열에서 문자 추출
//   -> 시간복잡도 개선 X(유의미한 차이가 없음)
// 2. 스택 자료구조 이용

function solution(number, k) {
    const arr = [];
    // 반복문을 통해 number의 길이만큼 반복
    for (let i = 0; i < number.length; i++) {
        // arr의 길이가 0보다 크고, 
        // arr의 마지막 요소가 number[i]보다 작고, 
        // k가 0보다 클 때
        while (
            arr.length > 0 
            && arr[arr.length - 1] < number[i] 
            && k > 0
        ) {
            // k를 1 감소
            k--;
            // arr의 마지막 요소를 제거
            arr.pop();
        }
        // number[i]를 arr에 추가
        arr.push(number[i]);
    }
    // arr의 길이에서 k를 뺀 값만큼 제거
    arr.splice(number.length - k);
    // arr를 join 해서 문자열로 반환
    return arr.join("");
}



/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.05ms, 33.4MB)
// 테스트 2 〉    통과 (0.14ms, 33.5MB)
// 테스트 3 〉    통과 (0.14ms, 33.5MB)
// 테스트 4 〉    통과 (0.23ms, 33.6MB)
// 테스트 5 〉    통과 (0.28ms, 33.6MB)
// 테스트 6 〉    통과 (7.23ms, 37.2MB)
// 테스트 7 〉    통과 (7.40ms, 38.6MB)
// 테스트 8 〉    통과 (10.04ms, 39.9MB)
// 테스트 9 〉    통과 (29.42ms, 54.6MB)
// 테스트 10 〉 통과 (32.01ms, 49.6MB)
// 테스트 11 〉 통과 (0.05ms, 33.6MB)
// 테스트 12 〉 통과 (0.05ms, 33.5MB)   


/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(n)

🙂 정렬

H-Index

/* redefine(재정의) */

// input : citations(발표한 논문 n편의 인용 횟수를 담은 1차원 Array)
// return : H-Index

// condition1 : 논문의 수 N : [1, 1000]
// condition1 : 논문별 인용 횟수(citations[i]) : [1, 1000]

// algorithm : 정렬(내림차순) : sort((a,b) => b - a)




/* solution(구현) */

function solution(citations) {
    let answer = 0;
    citations.sort((a, b) => b - a);    // 공간복잡도 O(n/2)    
    
    while (true) {
        if (citations[answer] >= Math.max(answer + 1, citations.length - answer)) {
            answer++;
        } else {
           return answer;
        }
    }
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.27ms, 33.4MB)
// 테스트 2 〉    통과 (0.35ms, 33.5MB)
// 테스트 3 〉    통과 (0.33ms, 33.6MB)
// 테스트 4 〉    통과 (0.32ms, 33.5MB)
// 테스트 5 〉    통과 (0.36ms, 33.3MB)
// 테스트 6 〉    통과 (0.39ms, 33.5MB)
// 테스트 7 〉    통과 (0.22ms, 33.5MB)
// 테스트 8 〉    통과 (0.14ms, 33.2MB)
// 테스트 9 〉    통과 (0.15ms, 33.5MB)
// 테스트 10 〉 통과 (0.24ms, 33.5MB)
// 테스트 11 〉 통과 (0.41ms, 33.5MB)
// 테스트 12 〉 통과 (0.16ms, 33.5MB)
// 테스트 13 〉 통과 (0.38ms, 33.4MB)
// 테스트 14 〉 통과 (0.36ms, 33.4MB)
// 테스트 15 〉 통과 (0.39ms, 33.5MB)
// 테스트 16 〉 통과 (0.05ms, 33.5MB)




/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n/2)




/* improvements(개선점) - 가독성 개선 */

function solution(citations) {
    let answer = 0;
    let flag = true;
    
    citations.sort((a, b) => b - a);      
    
    while (flag) {   
        citations[answer] >= Math.max(answer + 1, citations.length - answer)
        ? answer++
        : flag = false
    }
    
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.27ms, 33.6MB)
// 테스트 2 〉    통과 (0.36ms, 33.5MB)
// 테스트 3 〉    통과 (0.39ms, 33.7MB)
// 테스트 4 〉    통과 (0.32ms, 33.5MB)
// 테스트 5 〉    통과 (0.36ms, 33.7MB)
// 테스트 6 〉    통과 (0.42ms, 33.5MB)
// 테스트 7 〉    통과 (0.22ms, 33.6MB)
// 테스트 8 〉    통과 (0.14ms, 33.6MB)
// 테스트 9 〉    통과 (0.16ms, 33.5MB)
// 테스트 10 〉 통과 (0.24ms, 33.6MB)
// 테스트 11 〉 통과 (0.42ms, 33.6MB)
// 테스트 12 〉 통과 (0.17ms, 33.6MB)
// 테스트 13 〉 통과 (0.41ms, 33.6MB)
// 테스트 14 〉 통과 (0.37ms, 33.6MB)
// 테스트 15 〉 통과 (0.39ms, 33.6MB)
// 테스트 16 〉 통과 (0.05ms, 33.5MB)




/* check(검증) - Big O Notation */

// time complexity  : O(nlogn)
// space complexity : O(1)

K번째수

/* redefine(재정의) */

// input1 : array(1차원 Array)
// input2 : commands([i,j,k]를 요소로 가지는 2차원 Array)
// return : command별 연산 결과가 담긴 1차원 Array

// condition1 : array 길이 : [1,100]
// condition2 : array 원소 값 : [1,100]
// condition3 : commands 길이 : [1,50]

// algorithm : 정렬(오름차순) : sort((a, b) => a - b)

// logic(psuedo) : 




/* solution(구현) */

function solution(array, commands) {
    let answer = [];
    for ([i, j, k] of commands) {
        segArray = array.slice(i - 1, j).sort((a, b) => a - b);    // 공간복잡도 O(n/2)
        answer.push(segArray[k - 1]);
    }
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.08ms, 33.4MB)
// 테스트 2 〉    통과 (0.11ms, 33.4MB)
// 테스트 3 〉    통과 (0.07ms, 33.4MB)
// 테스트 4 〉    통과 (0.08ms, 33.5MB)
// 테스트 5 〉    통과 (0.09ms, 33.4MB)
// 테스트 6 〉    통과 (0.07ms, 33.4MB)
// 테스트 7 〉    통과 (0.08ms, 33.4MB)




/* check(검증) - Big O Notation */

// time complexity  : O(n^3*logn)
// space complexity : O(n)

가장 큰 수

/* redefine(재정의) */

// input : numbers(0 또는 양의 정수가 담긴 1차원 Array)
// return : 순서 재배치 결과, 가장 큰 수(String)

// condition1 : numbers 길이 : [1,100000]
// condition2 : numbers 원소 값 : [1,1000]

// algorithm : 정렬(비교연산 - 내림차순) : sort((n1, n2) => (n2 + n1) - (n1 + n2))

// logic(psuedo) : 
// 1. numbers 원소 타입을 문자열로 형변환
// 2. (n2+n1) (n1+n2) 비교 연산을 적용한 내림차순 정렬
// 3. 배열을 문자열로 변환(join)
// 4. 예외 처리("00...0" -> "0")




/* solution(구현) */

function solution(numbers) {
    let answer = numbers
        .map((num) => String(num))
        .sort((n1, n2) => (n2 + n1) - (n1 + n2))    // 공간복잡도 O(n/2)
        .join('');

    return Number(answer) === 0 ? '0' : answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (134.70ms, 42.9MB)
// 테스트 2 〉    통과 (63.72ms, 42.1MB)
// 테스트 3 〉    통과 (169.54ms, 44.9MB)
// 테스트 4 〉    통과 (24.57ms, 37.1MB)
// 테스트 5 〉    통과 (104.43ms, 44.7MB)
// 테스트 6 〉    통과 (91.92ms, 44MB)
// 테스트 7 〉    통과 (0.14ms, 33.4MB)
// 테스트 8 〉    통과 (0.13ms, 33.6MB)
// 테스트 9 〉    통과 (0.13ms, 33.4MB)
// 테스트 10 〉 통과 (0.13ms, 33.4MB)
// 테스트 11 〉 통과 (0.15ms, 33.4MB)
// 테스트 12 〉 통과 (0.06ms, 33.4MB)
// 테스트 13 〉 통과 (0.06ms, 33.5MB)
// 테스트 14 〉 통과 (0.06ms, 33.4MB)
// 테스트 15 〉 통과 (0.06ms, 33.4MB)




/* check(검증) - Big O Notation */

// time complexity  : O(n^3*logn)
// space complexity : O(n)

🙂 스택 & 큐

같은 숫자는 싫어

/* redefine(재정의) */

// input : arr(0부터 9까지의 숫자로 이루어진 1차원 Array)
// return  연속된 숫자를 제거한 1차원 배열

// condition1 : arr의 길이 : [1,1000000]
// condition2 : arr 원소 값 : [0, 9]

// algorithm : 스택

// logic(psuedo) : 




/* solution(구현) */

function solution(arr)
{
    let answer = [];
    let lastNum = -1;    // 0 ~ 9에 포함되지 않도록 초기값(-1) 설정

    for (let num of arr) {
        if (num !== lastNum) {    // 연속된 숫자가 아니라면,
            lastNum = num;        // lastNum 갱신
            answer.push(num);     // 스택에 push
        }
    }
    
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.06ms, 33.6MB)
// 테스트 2 〉    통과 (0.18ms, 33.5MB)
// 테스트 3 〉    통과 (0.19ms, 33.4MB)
// 테스트 4 〉    통과 (0.17ms, 33.4MB)
// 테스트 5 〉    통과 (0.22ms, 33.6MB)
// 테스트 6 〉    통과 (0.16ms, 33.4MB)
// 테스트 7 〉    통과 (0.17ms, 33.5MB)
// 테스트 8 〉    통과 (0.20ms, 33.4MB)
// 테스트 9 〉    통과 (0.13ms, 33.6MB)
// 테스트 10 〉 통과 (0.18ms, 33.5MB)
// 테스트 11 〉 통과 (0.15ms, 33.4MB)
// 테스트 12 〉 통과 (0.16ms, 33.6MB)
// 테스트 13 〉 통과 (0.13ms, 33.5MB)
// 테스트 14 〉 통과 (0.14ms, 33.4MB)
// 테스트 15 〉 통과 (0.13ms, 33.5MB)
// 테스트 16 〉 통과 (0.14ms, 33.5MB)
// 테스트 17 〉 통과 (0.04ms, 33.5MB)
// 효율성
// 테스트 1 〉    통과 (27.29ms, 91.2MB)
// 테스트 2 〉    통과 (30.33ms, 90.8MB)
// 테스트 3 〉    통과 (29.95ms, 91.4MB)
// 테스트 4 〉    통과 (56.42ms, 90.9MB)




/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n)

기능개발

/* redefine(재정의) */

// input : progresses(작업의 진도가 담긴 1차원 Array), speeds(작업의 개발 속도가 담긴1차원 Array)
// return : 배포별 배포되는 기능 수가 담긴 1차원 Array

// condition1 : input 배열들의 길이 : [1,100]
// condition2 : 작업 진도 및 속도 : [1,100]

// algorithm : 스택

// logic(psuedo) : 




/* solution(구현) */

function solution(progresses, speeds) {
    
    let answer = [];
    let endDay = [];

    for (let i=0; i<progresses.length; i++) {
        end = Math.ceil((100 - progresses[i]) / speeds[i]);  // 작업 잔여일 구하기
        endDay.push(end);                                    // 작업 잔여일 리스트에 push
    }

    let cnt = 0;             // 배포되는 기능 수
    let maxDay = endDay[0];  // 배포별 기준일 (첫 작업 잔여일로 초기화)
    
    for (let day of endDay) {
        if (day > maxDay) {    // 다음 배포일에 속한다면,  
            maxDay = day;      // 배포별 기준일 갱신 
            answer.push(cnt)   // 배포되는 기능 수 anwer 리스트에 push
            cnt = 1;           // 배포되는 기능 수 초기화(1)
        }
        else {
            cnt++;             // 같은 배포일에 속한다면 cnt++
        }
    }
    if (cnt > 0) answer.push(cnt);  // 마지막 배포되는 기능 수가 있는지 확인하고, 있으면 push
    
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.07ms, 33.4MB)
// 테스트 2 〉    통과 (0.18ms, 33.5MB)
// 테스트 3 〉    통과 (0.18ms, 33.4MB)
// 테스트 4 〉    통과 (0.17ms, 33.5MB)
// 테스트 5 〉    통과 (0.07ms, 33.1MB)
// 테스트 6 〉    통과 (0.07ms, 33.1MB)
// 테스트 7 〉    통과 (0.18ms, 33.5MB)
// 테스트 8 〉    통과 (0.07ms, 33.4MB)
// 테스트 9 〉    통과 (0.18ms, 33.3MB)
// 테스트 10 〉 통과 (0.18ms, 33.4MB)
// 테스트 11 〉 통과 (0.07ms, 33.5MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n)

다리를 지나는 트럭

/* redefine(재정의) */

// input1 : bridge_length(최대 올라갈 수 있는 트럭 수 Number)
// input2 : weight(견딜 수 있는 최대 하중 Number)
// input3 : truck_weights(대기 중인 트럭별 하중이 담긴 1차원 Array)
// return : 모든 트럭이 다리를 건너는데 걸리는 최소시간(초) Number

// condition1 : bridge_length : [1,10000]
// condition2 : weight : [1,10000]
// condition3 : truck_weights : [1,10000]
// condition4 : truck 속도 : 1

// algorithm : 큐

// logic(psuedo) : 




/* solution(구현) */

function solution(bridge_length, weight, truck_weights) {
    let answer = 0;
    let weightSum = 0;
    let bridge = new Array(bridge_length).fill(0);
  
    do {
        answer++;
        weightSum -= bridge.shift();
      
        (truck_weights.length > 0 && weightSum + truck_weights[0] <= weight)
        ? nextWeight = truck_weights.shift()
        : nextWeight = 0
      
        weightSum += nextWeight;
        bridge.push(nextWeight);
    }
    while (weightSum > 0);
  
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.61ms, 33.6MB)
// 테스트 2 〉    통과 (7.73ms, 37.4MB)
// 테스트 3 〉    통과 (0.14ms, 33.5MB)
// 테스트 4 〉    통과 (5.15ms, 37.4MB)
// 테스트 5 〉    통과 (29.79ms, 37.9MB)
// 테스트 6 〉    통과 (9.77ms, 38MB)
// 테스트 7 〉    통과 (0.51ms, 33.5MB)
// 테스트 8 〉    통과 (0.27ms, 33.5MB)
// 테스트 9 〉    통과 (4.46ms, 36.6MB)
// 테스트 10 〉 통과 (0.19ms, 33.5MB)
// 테스트 11 〉 통과 (0.14ms, 33.5MB)
// 테스트 12 〉 통과 (0.22ms, 33.4MB)
// 테스트 13 〉 통과 (0.46ms, 33.5MB)
// 테스트 14 〉 통과 (0.22ms, 33.4MB)


/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(n)

올바른 괄호

/* redefine(재정의) */

// input : s( '(' , ')' 로만 이루어진 문자열 s)
// return : 괄호가 바르게 짝지었는지에 따른 boolean 값

// condition : s의 길이 : [1,100000]

// algorithm : 

// logic(psuedo) :
// 1. left < right가 된다면, return false
// 2. left/right 갱신이 끝난 후, left === right 조건을 만족하지 않으면, return false




/* solution(구현) */

function solution(s){
    let left = 0;   // 왼쪽 괄호('(')의 개수
    let right = 0;  // 오른쪽 괄호(')')의 개수

    for (let c of s) { 
        
        (c === '(') ? left++ : right++    // 괄호 모양에 따라 left, right 갱신

        if (left < right) return false;   // logic1 : left < right가 된다면, return false
    }    
    
    if (left === right) { return true } 
    else { return false }                 // logic2 : left === right 조건을 만족하지 않으면, return false
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.04ms, 33.5MB)
// 테스트 2 〉    통과 (0.04ms, 33.4MB)
// 테스트 3 〉    통과 (0.04ms, 33.4MB)
// 테스트 4 〉    통과 (0.05ms, 33.4MB)
// 테스트 5 〉    통과 (0.05ms, 33.5MB)
// 테스트 6 〉    통과 (0.04ms, 33.4MB)
// 테스트 7 〉    통과 (0.04ms, 33.6MB)
// 테스트 8 〉    통과 (0.04ms, 33.5MB)
// 테스트 9 〉    통과 (0.05ms, 33.4MB)
// 테스트 10 〉 통과 (0.07ms, 33.5MB)
// 테스트 11 〉 통과 (0.07ms, 33.4MB)
// 테스트 12 〉 통과 (0.13ms, 33.4MB)
// 테스트 13 〉 통과 (0.13ms, 33.4MB)
// 테스트 14 〉 통과 (0.13ms, 33.4MB)
// 테스트 15 〉 통과 (0.13ms, 33.4MB)
// 테스트 16 〉 통과 (0.13ms, 33.4MB)
// 테스트 17 〉 통과 (0.13ms, 33.4MB)
// 테스트 18 〉 통과 (0.14ms, 33.4MB)
// 효율성
// 테스트 1 〉    통과 (9.30ms, 38MB)
// 테스트 2 〉    통과 (5.40ms, 38.2MB)




/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(1)




/* improvements(개선점 - 가독성 향상) */

function solution(s){
    let left = 0
    let right = 0;

    for (let c of s) { 
        
        (c === '(') ? left++ : right++

        if (left < right) return false;
    }    
    
    return (left === right) ? true : false;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.04ms, 33.5MB)
// 테스트 2 〉    통과 (0.06ms, 33.5MB)
// 테스트 3 〉    통과 (0.04ms, 33.5MB)
// 테스트 4 〉    통과 (0.04ms, 33.4MB)
// 테스트 5 〉    통과 (0.04ms, 33.4MB)
// 테스트 6 〉    통과 (0.05ms, 33.4MB)
// 테스트 7 〉    통과 (0.05ms, 33.5MB)
// 테스트 8 〉    통과 (0.04ms, 33.4MB)
// 테스트 9 〉    통과 (0.05ms, 33.5MB)
// 테스트 10 〉 통과 (0.07ms, 33.4MB)
// 테스트 11 〉 통과 (0.04ms, 33.5MB)
// 테스트 12 〉 통과 (0.16ms, 33.4MB)
// 테스트 13 〉 통과 (0.13ms, 33.4MB)
// 테스트 14 〉 통과 (0.14ms, 33.4MB)
// 테스트 15 〉 통과 (0.18ms, 33.6MB)
// 테스트 16 〉 통과 (0.14ms, 33.4MB)
// 테스트 17 〉 통과 (0.13ms, 33.4MB)
// 테스트 18 〉 통과 (0.19ms, 33.4MB)
// 효율성
// 테스트 1 〉    통과 (5.41ms, 38.4MB)
// 테스트 2 〉    통과 (6.65ms, 38.3MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(1)

프린터

/* redefine(재정의) */

// input : priorities(문서의 중요도가 담긴 1차원 Array), location(인쇄를 요청한 문서의 위치 Number)
// return : 인쇄를 요청한 문서의 인쇄 순서(Number)

// condition1 : 중요도 : [1,9]
// condition2 : priorities 길이 : [1,100]

// algorithm : 큐

// logic(psuedo) : 




/* solution(구현) */

function solution(priorities, location) {
    let answer = 0;
    let flag = false;
    
    while(true) {
          maxPriority = Math.max(...priorities);
        
        let priority = priorities.shift();
        
        (priority === maxPriority) 
        ? ((location === 0) ? flag = true : answer++)
        : priorities.push(priority)
        
        if (flag) return (answer + 1);
        
        (location > 0) ? location-- : location = priorities.length - 1
    }
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.15ms, 33.4MB)
// 테스트 2 〉    통과 (0.35ms, 33.5MB)
// 테스트 3 〉    통과 (0.18ms, 33.5MB)
// 테스트 4 〉    통과 (0.15ms, 33.4MB)
// 테스트 5 〉    통과 (0.05ms, 33.4MB)
// 테스트 6 〉    통과 (0.16ms, 33.5MB)
// 테스트 7 〉    통과 (0.16ms, 33.6MB)
// 테스트 8 〉    통과 (0.26ms, 33.5MB)
// 테스트 9 〉    통과 (0.14ms, 33.5MB)
// 테스트 10 〉 통과 (0.18ms, 33.4MB)
// 테스트 11 〉 통과 (0.24ms, 33.5MB)
// 테스트 12 〉 통과 (0.14ms, 33.5MB)
// 테스트 13 〉 통과 (0.23ms, 33.6MB)
// 테스트 14 〉 통과 (0.05ms, 33.4MB)
// 테스트 15 〉 통과 (0.06ms, 33.5MB)
// 테스트 16 〉 통과 (0.15ms, 33.4MB)
// 테스트 17 〉 통과 (0.30ms, 33.5MB)
// 테스트 18 〉 통과 (0.17ms, 33.5MB)
// 테스트 19 〉 통과 (0.37ms, 33.5MB)
// 테스트 20 〉 통과 (0.15ms, 33.4MB)


/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// space complexity : O(1)

🙂 BFS & DFS

게임 맵 최단거리

/* redefine(재정의) */

// input1 : maps(n * m 크기의 2차원 Array(Matrix)) 
// return : 목표지점에 도착하기 위해서 지나야 하는 칸의 개수(불가능하다면 -1) Number

// condition1 : map의 요소 : 0-벽, 1-칸
// condition2 : n과 m은 서로 다를 수 있으며, [1,100] (단 1X1은 제외)
// condition3 : 출발지점 (1,1) -> 목표지점(n,m)

// algorithm : BFS, Queue

// logic(psuedo) : 
// 1. 큐 자료구조 이용한 BFS 탐색
// 2. 인덱스 범위 유효/방문 여부 검사
// 3. 목표지점 도달 여부 검사
// 4. 다음 레벨 노드를 큐에 삽입
// 5. 목표지점에 도달할 수 없다면, -1 반환


/* solution(구현) */

function solution(maps) {

    const n = maps.length - 1;     // n(행)
    const m = maps[0].length - 1;  // m(열)

    const dx = [0, 0, -1, 1];      // 방향 설정
    const dy = [1, -1, 0, 0];      // 상/하/좌/우

    let route = [[0, 0]];           // route 세팅(Queue)

    while(route.length > 0) {                                     // BFS 시작                  
        
        for (let i = 0; i<route.length; i++) {                    // 동일 레벨 내 노드 탐색
            const [x, y] = route.pop();                           // 노드 설정
            
            for (let j=0; j<4; j++) {                             // 상/하/좌/우 탐색
                const nx = x + dx[j];                             // 다음 노드 x좌표 설정
                const ny = y + dy[j];                             // 다음 노드 y좌표 설정
                
                if (nx < 0 | nx > n | ny < 0 | ny > m) continue;  // 인덱스 범위 유효 검사(index range)
                if (maps[nx][ny] !== 1) continue;                 // 방문 여부 검사(visited)
                
                maps[nx][ny] = maps[x][y] + 1;                    // 방문하지 않았다면 +1
                
                if (nx === n && ny === m) return maps[nx][ny];    // 목표지점에 도달 여부 검사
                
                route.push([nx, ny]);                             // 다음 레벨 노드를 큐에 삽입
            }
        }
    }   
    return -1;  // 목표지점에 도달하지 못했다면, -1 반환
}


/* 결과 */
// 정확성  테스트
// 테스트 1 〉    통과 (0.21ms, 33.3MB)
// 테스트 2 〉    통과 (0.22ms, 33.3MB)
// 테스트 3 〉    통과 (0.21ms, 33.4MB)
// 테스트 4 〉    통과 (0.21ms, 33.4MB)
// 테스트 5 〉    통과 (0.21ms, 33.4MB)
// 테스트 6 〉    통과 (0.21ms, 33.4MB)
// 테스트 7 〉    통과 (0.36ms, 33.4MB)
// 테스트 8 〉    통과 (0.22ms, 33.5MB)
// 테스트 9 〉    통과 (0.22ms, 33.4MB)
// 테스트 10 〉 통과 (0.22ms, 33.5MB)
// 테스트 11 〉 통과 (0.21ms, 33.4MB)
// 테스트 12 〉 통과 (0.20ms, 33.4MB)
// 테스트 13 〉 통과 (0.21ms, 33.4MB)
// 테스트 14 〉 통과 (0.21ms, 33.4MB)
// 테스트 15 〉 통과 (0.22ms, 33.4MB)
// 테스트 16 〉 통과 (0.19ms, 33.4MB)
// 테스트 17 〉 통과 (0.22ms, 33.4MB)
// 테스트 18 〉 통과 (0.09ms, 33.4MB)
// 테스트 19 〉 통과 (0.08ms, 33.3MB)
// 테스트 20 〉 통과 (0.08ms, 33.3MB)
// 테스트 21 〉통과 (0.08ms, 33.4MB)
// 효율성  테스트
// 테스트 1 〉    통과 (37.53ms, 37.4MB)
// 테스트 2 〉    통과 (4.94ms, 37.3MB)
// 테스트 3 〉    통과 (41.64ms, 37.5MB)
// 테스트 4 〉    통과 (31.12ms, 37.1MB)




/* check(검증) - Big O Notation */

// time complexity  : O(nm)
// 인덱스 범위 유효/방문 여부 검사를 통해 각 노드를 최대 한 번씩만 탐색하기 때문

// space complexity : O(min(n, m))
// BFS에 사용되는 큐의 길이 <= min(n,m)

타겟 넘버

/* redefine(재정의) */

// input1 : numbers(사용할 수 있는 숫자가 담긴 1차원 Array)
// input2 : target(타겟 Number)
// return : 타겟 넘버를 만드는 방법의 수 Number

// condition1 : numbers 길이 : [2,20]
// condition2 : numbers 요소 크기 : [1,50]
// condition3 : target 요소 크기 : [1,1000]

// algorithm : DFS

// logic(psuedo) : 
// 1. 각 노드의 자식을 두 개(다음 인덱스의 +요소, -요소)로 설정
// 2. DFS, +요소부터 탐색
// 3. DFS 완료(cnt === 0), 타겟과 일치하는지 확인하고 반환 값 갱신




/* solution(구현) */

function solution(numbers, target) {
    
    let answer = 0;                                       // 반환 값 초기 세팅(0)
    

    function totalDFS(cnt, total) {                       // totalDFS(cnt: 남은 탐색 횟수, total: 총계)         
    
        if (cnt > 0) {                                    // cnt가 0보다 크다면,
            totalDFS(cnt - 1, total + numbers[cnt - 1]);  // DFS 실행(+ 연산 탐색)
            totalDFS(cnt - 1, total - numbers[cnt - 1]);  // DFS 실행(- 연산 탐색)
        }
    
        else {                                            // DFS가 끝나면,
            (total === target)? answer++ : answer;        // target과 일치하는지 확인(일치하면 ++)
            return;
        }
        
    }
    totalDFS(numbers.length, 0);
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (37.05ms, 36.4MB)
// 테스트 2 〉    통과 (41.16ms, 36.3MB)
// 테스트 3 〉    통과 (0.19ms, 33.4MB)
// 테스트 4 〉    통과 (0.39ms, 33.3MB)
// 테스트 5 〉    통과 (29.51ms, 36.2MB)
// 테스트 6 〉    통과 (0.26ms, 33.4MB)
// 테스트 7 〉    통과 (0.19ms, 33.4MB)
// 테스트 8 〉    통과 (20.62ms, 36.3MB)



/* check(검증) - Big O Notation */

// time complexity  : O(2^n)
// depth = n, e = 2이므로

// space complexity : O(n)
// 호출 스택의 크기가 n에 비례하며, 깊이가 n일 때의 호출 스택의 크기가 n에 해당하기 때문

단어 변환

/* redefine */

// input1: 시작 단어, begin
// input2: 타겟 단어, target
// input3: 단어의 집합, words
// output: 최소 몇 단계의 과정을 거쳐야하는지(변환할 수 없다면 0)

// cond1 : begin, target을 이루는 단어는 알파벳 소문자로 구성
// cond2 : 단어의 길이는 [3, 10]이며, 모든 단어의 길이는 동일
// cond3 : words의 길이는 [3, 50]
// cond4 : begin과 target은 상이

// logic : 한 번에 한 개의 알파벳만 바꿀 수 있으며, 변환된 단어는 words에 있어야 함


/* solution */
function solution(begin, target, words) {
    if (!words.includes(target)) return 0;
    
    let answers = [];
    let visited = {};
    
    let queue = [[begin, 0]];
    while (queue.length) {
        let [before, answer] = queue.shift();
        if (visited[before]) continue;
        
        let cnt = 0;
        
        visited[before] = true;
        
        if (before === target) {
            answers.push(answer);
            continue;
        }
        
        for (let after of words) {
            for (let i=0; i<after.length; i++) {
                if (before[i] !== after[i]) cnt++;
            }
            if (cnt === 1) queue.push([after, answer + 1]);
            cnt = 0;
        }
    }
    
    return Math.min(...answers);
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.10ms, 33.5MB)
// 테스트 2 〉    통과 (0.27ms, 33.5MB)
// 테스트 3 〉    통과 (0.48ms, 33.4MB)
// 테스트 4 〉    통과 (0.10ms, 33.4MB)
// 테스트 5 〉    통과 (0.07ms, 33.4MB)

📝 코딩테스트 연습

LV.2️⃣

무인도 여행

/* redefine(재정의) */

// input1 : maps(각 행의 식량정보(String)를 요소로 갖는 1차원 Array)
// return : 각 섬에서 머무를 수 있는 최대 일수(Number)를 담은 1차원 Array

// condition1 : maps의 행, 열 크기(서로 다를 수 있음) : [3,100]
// condition2 : maps[i]는 'X' 또는 [1, 9]
// condition3 : 섬이 없다면 -1 반환

// algorithm : BFS, Queue

// logic(psuedo) :
// 1. 섬 노드인 경우만 BFS 진행
// 2. 큐에 출발 노드 삽입
// 3. 방문/섬/인덱스 범위 유효 여부 검사하면 BFS 진행
// 4. BFS 완료 후, 식량 여부 검사(있다면 반환 값 갱신)
// 5. 예외 처리(섬이 하나도 없는 경우) 및 반환 값 조건 만족



/* solution(구현) */

function solution(maps) {
    
    let days = [];                                 // 반환 값 세팅

    const n = maps.length;                         // n(행)
    const m = maps[0].length;                      // m(열)

    const dx = [0, 0, -1, 1];                      // 방향 설정
    const dy = [1, -1, 0, 0];                      // 상/하/좌/우
    
    let visited = new Array(n*m).fill(false);      // 방문 배열(false)

    
    for (let i=0; i<n; i++) {                      // 모든 노드 탐색                  
        for (let j=0; j<m; j++) {
            if (maps[i][j] === "X") { continue; }  // 섬 여부 검사("X": 바다)
            
            let route = [[i, j]];                  // route 세팅(Queue)
            let cnt = 0;                           // 식량 수(총계) 초기화          

            
            while(route.length > 0) {                                   // BFS 시작   

                const [x, y] = route.shift();                           // 노드 설정
    
                if (visited[x * m + y] === true) continue;              // 방문 여부 검사
                visited[x * m + y] = true;                              // 방문 처리
                
                cnt = Number(cnt) + Number(maps[x][y]);                 // 식량 수(총계) 갱신
                
                
                for (let j=0; j<4; j++) {                               // 상/하/좌/우 탐색
                    const nx = x + dx[j];                               // 다음 노드 x좌표 설정
                    const ny = y + dy[j];                               // 다음 노드 y좌표 설정
                    
                    if (nx < 0 | nx >= n | ny < 0 | ny >= m) continue;  // 인덱스 범위 유효 검사(index range)
                    if (maps[nx][ny] === 'X') continue;                 // 섬 여부 검사("X": 바다)
                    
                    route.push([nx, ny]);                               // 다음 레벨 노드를 큐에 삽입
                }

            }

            if (cnt > 0) {days.push(cnt)}                               // 식량 여부 검사, 있다면 push
        }           
    }

    // (섬이 있다면) ? 오름차순 정렬된 days 반환 : [-1] 반환
    return (days.length > 0) ? days.sort((a,b) => a-b) : [-1];
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.14ms, 33.6MB)
// 테스트 2 〉    통과 (0.25ms, 33.6MB)
// 테스트 3 〉    통과 (0.30ms, 33.5MB)
// 테스트 4 〉    통과 (0.38ms, 33.5MB)
// 테스트 5 〉    통과 (1.33ms, 34MB)
// 테스트 6 〉    통과 (1.95ms, 34.3MB)
// 테스트 7 〉    통과 (1.18ms, 33.9MB)
// 테스트 8 〉    통과 (3.27ms, 34.6MB)
// 테스트 9 〉    통과 (9.70ms, 38.2MB)
// 테스트 10 〉 통과 (4.83ms, 37.4MB)
// 테스트 11 〉 통과 (4.71ms, 37.4MB)
// 테스트 12 〉 통과 (10.86ms, 38.4MB)
// 테스트 13 〉 통과 (10.69ms, 38.5MB)
// 테스트 14 〉 통과 (18.53ms, 38.7MB)
// 테스트 15 〉 통과 (18.41ms, 38.7MB)
// 테스트 16 〉 통과 (18.67ms, 38.8MB)
// 테스트 17 〉 통과 (0.59ms, 33.7MB)
// 테스트 18 〉 통과 (18.61ms, 38.8MB)
// 테스트 19 〉 통과 (18.75ms, 38.8MB)
// 테스트 20 〉 통과 (0.77ms, 33.6MB)
// 테스트 21 〉 통과 (0.71ms, 33.7MB)
// 테스트 22 〉 통과 (0.31ms, 33.5MB)
// 테스트 23 〉 통과 (9.82ms, 38.4MB)
// 테스트 24 〉 통과 (10.82ms, 38.6MB)
// 테스트 25 〉 통과 (0.42ms, 33.6MB)


/* check(검증) - Big O Notation */

// time complexity  : O(NM)
// 모든 노드(NxM 크기)를 탐색하므로

// space complexity : O(NM)
// visited 배열 크기(NxM)

호텔 대실

/* redefine(재정의) */

// input : book_time(["HH:MM", "HH:MM"]) 형태로 입실 시간과 퇴실 시간이 담긴 2차원 Array)
// return : 예약 손님이 모두 입실하기 위해 필요한 최소 객실의 수 Number

// condition1 : book_time 길이 : [1,1000]
// condition2 : 입실 및 퇴실 시간(24시간 표기법) : ["00:00","23:59"]
// condition3 : 입실 시간 < 퇴실 시간

// algorithm : 정렬, 스택, 그리디?

// logic(psuedo) :
// 1. input 데이터 가공(분으로 환산하여 Number 형으로) : [입실 시간(분):Number, 퇴실 시간(분):Number]
// 2. 대실 시작 시간을 기준으로 오름차순 정렬
// 3. 객실마다 입실 여부를 검사
// 4. 입실이 가능하다면, 해당 객실(room[i]) 값 갱신
// 5. 입실이 불가능하다면, 새로운 객실(room[room.length + 1])에 push
// 6. 모든 예약 손님들의 입실이 끝나면, room.length 반환


/* solution(구현) */

function solution(book_time) {
    
    const bookTime = book_time.map(function([start, end]) {
        
        [sh, sm] = start.split(":").map(Number);  // "(sh):(sm)"
        [eh, em] = end.split(":").map(Number);    // "(eh):(em)"
        
        return [sh * 60 + sm, eh * 60 + em];      // minute(분)으로 환산
    });
    
    bookTime.sort((a, b) => a[0] - b[0]);  // 대실 시작 시간을 기준으로 오름차순 정렬
    
    let room = [0];
    for (let [start, end] of bookTime) {         // 현재 손님의 [입실시간, 퇴실 시간] 세팅
        
        let flag = false;                        // 입실 가능/불가능 표시
        
        for (let i=0; i<room.length; i++) {      // 객실(room[i])마다 탐색
            if (start >= room[i] + 10) {         // 현재 손님의 입실 시간 >= 이전 손님의 퇴실 시간+10 이면
                room[i] = end;                   // 해당 객실에 손님 입실
                flag = true;                     // 입실 가능 표시
                break;                           // break
            }
        }
        if (flag === false) { room.push(end); }  // 만약 입실하지 못했으면, 새로운 객실을 만들어 push
    }
    return room.length;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.55ms, 33.5MB)
// 테스트 2 〉    통과 (1.59ms, 33.6MB)
// 테스트 3 〉    통과 (8.66ms, 37.6MB)
// 테스트 4 〉    통과 (7.80ms, 37.1MB)
// 테스트 5 〉    통과 (0.12ms, 33.4MB)
// 테스트 6 〉    통과 (8.54ms, 37.4MB)
// 테스트 7 〉    통과 (7.06ms, 37.6MB)
// 테스트 8 〉    통과 (1.60ms, 33.8MB)
// 테스트 9 〉    통과 (1.93ms, 33.6MB)
// 테스트 10 〉 통과 (8.41ms, 37.4MB)
// 테스트 11 〉 통과 (9.80ms, 37.6MB)
// 테스트 12 〉 통과 (9.55ms, 37.5MB)
// 테스트 13 〉 통과 (1.69ms, 33.7MB)
// 테스트 14 〉 통과 (6.78ms, 37.5MB)
// 테스트 15 〉 통과 (9.67ms, 37.6MB)
// 테스트 16 〉 통과 (2.72ms, 33.7MB)
// 테스트 17 〉 통과 (6.16ms, 37.5MB)
// 테스트 18 〉 통과 (5.54ms, 37.4MB)
// 테스트 19 〉 통과 (6.85ms, 38.5MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// 이중 for 문으로 탐색

// space complexity : O(n)
// room의 최대 크기 <= n

뒤에 있는 큰 수 찾기

/* redefine(재정의) */

// input : numbers(정수로 이루어진 1차원 Array) 
// return : 모든 원소에 대한 뒷 큰 수들을 차례로 담은 1차원 Array

// condition1 : numbers 길이 : [4, 1000000]
// condition2 : numbers[i] : [1, 1000000]

// algorithm : 우선순위 큐

// logic(psuedo) : 




/* solution(구현) */

function solution(numbers) {
    
    var answer = [];
    
    for (let i = 0; i < numbers.length; i++) {          // index 0부터 모든 노드 탐색
        let bigNum = -1;                                // bigNum : 뒷 큰 수 세팅(-1)
        
        for (let j = i + 1; j < numbers.length; j++) {
            if (numbers[j] > numbers[i]) {              // 만약, 뒷 큰 수를 찾으면
                bigNum = numbers[j];                    // bigNum 갱신
                break;                                  // 탐색을 멈추고
            }
        }
        answer.push(bigNum);                            // answer에 push
    }
    
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.11ms, 33.4MB)
// 테스트 2 〉    통과 (0.06ms, 33.4MB)
// 테스트 3 〉    통과 (0.16ms, 33.3MB)
// 테스트 4 〉    통과 (0.13ms, 33.4MB)
// 테스트 5 〉    통과 (0.40ms, 33.6MB)
// 테스트 6 〉    통과 (3.04ms, 37.9MB)
// 테스트 7 〉    통과 (2.30ms, 38MB)
// 테스트 8 〉    통과 (4.23ms, 42.1MB)
// 테스트 9 〉    통과 (3.60ms, 42.1MB)
// 테스트 10 〉 통과 (5.66ms, 46.7MB)
// 테스트 11 〉 통과 (6.66ms, 46.8MB)
// 테스트 12 〉 통과 (10.10ms, 56.1MB)
// 테스트 13 〉 통과 (11.07ms, 56.2MB)
// 테스트 14 〉 통과 (29.47ms, 77.3MB)
// 테스트 15 〉 통과 (94.46ms, 142MB)
// 테스트 16 〉 통과 (88.20ms, 142MB)
// 테스트 17 〉 통과 (85.91ms, 142MB)
// 테스트 18 〉 통과 (81.09ms, 142MB)
// 테스트 19 〉 통과 (66.23ms, 142MB)
// 테스트 20 〉    실패 (시간 초과)
// 테스트 21 〉    실패 (시간 초과)
// 테스트 22 〉    실패 (시간 초과)
// 테스트 23 〉    실패 (시간 초과)


/* check(검증) - Big O Notation */

// time complexity  : O(n^2)
// for loop(n)^2

// space complexity : O(n)




/* improvements(개선점) - 우선순위 큐를 사용하여 시간 복잡도 개선(n^2 => nlogn) */

function solution(numbers) {
    const answer = [];
    
    const pq = []; // 우선순위 큐 세팅
    
    for (let i = numbers.length - 1; i >= 0; i--) {                             // 역순으로 순회
        
        while (pq.length > 0 && pq[pq.length - 1] <= numbers[i]) { pq.pop(); }  // 우선순위 큐 갱신
        
        answer[i] = (pq.length > 0) ? pq[pq.length - 1] : -1;                   // answer 갱신()
        
        pq.push(numbers[i]);                                                    // 우선순위 큐 삽입
    }
    
    return answer;
}


// 테스트 1 〉    통과 (0.05ms, 33.4MB)
// 테스트 2 〉    통과 (0.05ms, 33.5MB)
// 테스트 3 〉    통과 (0.13ms, 33.6MB)
// 테스트 4 〉    통과 (0.14ms, 33.5MB)
// 테스트 5 〉    통과 (0.30ms, 33.7MB)
// 테스트 6 〉    통과 (3.20ms, 38.2MB)
// 테스트 7 〉    통과 (3.30ms, 38.2MB)
// 테스트 8 〉    통과 (7.17ms, 42.3MB)
// 테스트 9 〉    통과 (6.92ms, 42.5MB)
// 테스트 10 〉 통과 (12.41ms, 47MB)
// 테스트 11 〉 통과 (12.11ms, 47MB)
// 테스트 12 〉 통과 (34.04ms, 55.9MB)
// 테스트 13 〉 통과 (25.24ms, 55.8MB)
// 테스트 14 〉 통과 (55.62ms, 77.3MB)
// 테스트 15 〉 통과 (110.82ms, 137MB)
// 테스트 16 〉 통과 (115.39ms, 137MB)
// 테스트 17 〉 통과 (106.52ms, 137MB)
// 테스트 18 〉 통과 (129.07ms, 137MB)
// 테스트 19 〉 통과 (122.90ms, 137MB)
// 테스트 20 〉 통과 (117.40ms, 134MB)
// 테스트 21 〉 통과 (100.29ms, 133MB)
// 테스트 22 〉 통과 (86.16ms, 104MB)
// 테스트 23 〉 통과 (113.67ms, 127MB)


/* check(검증) - Big O Notation */

// time complexity  : O(nlogn)
// for loop(n) * priorityQueue(logn)

// space complexity : O(n)

숫자 변환하기

/* redefine(재정의) */

// input : x, y, n (Number)
// return : 필요한 최소 연산 횟수(Number), 만들 수 없다면 -1

// condition1 : x, y : [1,1000000]
// condition2 : n : [1,y)

// algorithm : BFS & Set

// logic(psuedo) : 




/* solution(구현) */

function solution(x, y, n) {

    if (x === y) { return 0; }
    
    let cnt = 0;                               // 레벨 깊이
    let stack1 = [];                           // 레벨 별 노드를 담을 스택
    let visited = new Array(1000001).fill(0);  // 방문 배열 작성

    stack1.push(x);  // 시작 노드 세팅
    
    while (true) {
        cnt++;         
        let stack2 = [];
        
        while (stack1.length > 0) {    // 레벨 내 노드 유무 검사
            const num = stack1.pop();  // 부모 노드 세팅

            const n1 = num * 2;        // 자식 노드 세팅1
            const n2 = num * 3;        // 자식 노드 세팅2
            const n3 = num + n;        // 자식 노드 세팅3
            
            if (n1===y || n2===y || n3===y) { return cnt; }  // 
            
            if (n1 < y && visited[n1] === 0) {               // n1 < y && 방문X
                stack2.push(num * 2);                        // 임시 스택에 Push
                visited[n1] = 1;                             // 방문 표시
            }
            if (n2 < y && visited[n2] === 0) {               // n2 < y && 방문X
                stack2.push(num * 3);                        // 임시 스택에 Push
                visited[n2] = 1;                             // 방문 표시
            }
            if (n3 < y && visited[n3] === 0) {               // n3 < y && 방문X 
                stack2.push(num + n);                        // 임시 스택에 Push
                visited[n3] = 1;                             // 방문 표시
            }
        }
        if (stack2.length === 0) { return -1; }              // 불가능한 경우, -1 반환
        stack1.push(...stack2);                              // 다음 연산을 위해 push
    }
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (6.32ms, 41.2MB)
// 테스트 2 〉    통과 (6.73ms, 41.1MB)
// 테스트 3 〉    통과 (6.48ms, 41.1MB)
// 테스트 4 〉    통과 (6.65ms, 41.2MB)
// 테스트 5 〉    통과 (14.36ms, 46.3MB)
// 테스트 6 〉    통과 (0.07ms, 33.5MB)
// 테스트 7 〉    통과 (18.54ms, 47MB)
// 테스트 8 〉    통과 (6.18ms, 41.3MB)
// 테스트 9 〉        실패 (런타임 에러)
// 테스트 10 〉    실패 (런타임 에러)
// 테스트 11 〉 통과 (19.32ms, 48.3MB)
// 테스트 12 〉 통과 (6.08ms, 41.3MB)
// 테스트 13 〉 통과 (6.19ms, 41.1MB)
// 테스트 14 〉 통과 (7.17ms, 41.3MB)
// 테스트 15 〉 통과 (6.90ms, 41.3MB)
// 테스트 16 〉 통과 (6.42ms, 41.2MB)


/* check(검증) - Big O Notation */

// time complexity  : O()
// space complexity : O()




/* improvements(개선점 - Set 활용) */

function solution(x, y, n) {
    
    if (x === y) { return 0; }
    
    let cnt = 0;            
    let numSet1 = new Set();
    numSet1.add(x);
    
    while(numSet1.size > 0) {
        if(numSet1.has(y)) { return cnt; }
        const numSet2 = new Set();
        for (const num of numSet1.values()) {
            if(num * 2 <= y) { numSet2.add(num * 2); }
            if(num * 3 <= y) { numSet2.add(num * 3); }
            if(num + n <= y) { numSet2.add(num + n); }
        }
        numSet1 = numSet2;
        cnt++;
    }
    return -1;
}


// 정확성
// 테스트 1 〉    통과 (0.07ms, 33.4MB)
// 테스트 2 〉    통과 (0.09ms, 33.4MB)
// 테스트 3 〉    통과 (0.12ms, 33.5MB)
// 테스트 4 〉    통과 (0.09ms, 33.4MB)
// 테스트 5 〉    통과 (43.41ms, 52.2MB)
// 테스트 6 〉    통과 (0.06ms, 33.4MB)
// 테스트 7 〉    통과 (45.59ms, 52.7MB)
// 테스트 8 〉    통과 (0.10ms, 33.4MB)
// 테스트 9 〉    통과 (248.04ms, 88.3MB)
// 테스트 10 〉 통과 (201.50ms, 88.1MB)
// 테스트 11 〉 통과 (61.75ms, 56.7MB)
// 테스트 12 〉 통과 (0.07ms, 33.4MB)
// 테스트 13 〉 통과 (0.07ms, 33.4MB)
// 테스트 14 〉 통과 (1.32ms, 34.1MB)
// 테스트 15 〉 통과 (0.23ms, 33.5MB)
// 테스트 16 〉 통과 (0.50ms, 33.7MB)


/* check(검증) - Big O Notation */

// m은 cnt의 최댓값

// time complexity  : O(3^m * n)
// while 루프에서는 각 레벨에서 만들어지는 새로운 숫자의 수가 O(3) 이고, 각 레벨은 O(n) 의 시간이 걸린다.

// space complexity : O(m)
// 각 레벨에서 새로운 숫자를 저장하기 위해 필요한 Set 개수

시소 짝꿍

/* redefine(재정의) */

// input : weights(사람들의 몸무게가 담긴 1차원 Array)
// return : 시소 짝꿍이 몇 쌍 존재하는지 Number

// condition1 : weights 길이 : [1, 100000]
// condition2 : weights[i] : [100,1000]

// algorithm :

// logic(psuedo) : 




/* solution(구현) */

function solution(weights) {
    let answer = 0;
    
    const people = new Array(1001).fill(0);
    for (let w of weights) { people[w] += 1; }
    
    for (let w of weights) {
        const w1 = w / 2;
        const w2 = w * 2 / 3;
        const w3 = w * 3 / 4;
        const w4 = w;
        const w5 = w * 4 / 3;
        const w6 = w * 3 / 2;
        const w7 = w * 2;
        
        if (w1 % 1 === 0 && w1 < 1001) { answer += people[w1]; }
        if (w2 % 1 === 0 && w2 < 1001) { answer += people[w2]; }
        if (w3 % 1 === 0 && w3 < 1001) { answer += people[w3]; }
        if (w4 % 1 === 0 && w4 < 1001) { answer += (people[w4] - 1); }
        if (w5 % 1 === 0 && w5 < 1001) { answer += people[w5]; }
        if (w6 % 1 === 0 && w6 < 1001) { answer += people[w6]; }
        if (w7 % 1 === 0 && w7 < 1001) { answer += people[w7]; }
    }
    
    return answer/2;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.08ms, 33.5MB)
// 테스트 2 〉    통과 (0.09ms, 33.5MB)
// 테스트 3 〉    통과 (0.09ms, 33.6MB)
// 테스트 4 〉    통과 (7.50ms, 38.3MB)
// 테스트 5 〉    통과 (8.83ms, 38.8MB)
// 테스트 6 〉    통과 (12.04ms, 39.5MB)
// 테스트 7 〉    통과 (12.62ms, 39.8MB)
// 테스트 8 〉    통과 (14.19ms, 40.3MB)
// 테스트 9 〉    통과 (16.26ms, 39.3MB)
// 테스트 10 〉 통과 (18.69ms, 39.7MB)
// 테스트 11 〉 통과 (19.87ms, 39.7MB)
// 테스트 12 〉 통과 (15.92ms, 39.8MB)
// 테스트 13 〉 통과 (28.45ms, 39.9MB)
// 테스트 14 〉 통과 (21.51ms, 39.7MB)
// 테스트 15 〉 통과 (19.33ms, 39.8MB)
// 테스트 16 〉 통과 (0.09ms, 33.6MB)
// 테스트 17 〉 통과 (0.11ms, 33.5MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(1001)

마법의 엘리베이터

/* redefine(재정의) */

// input : storey(엘리베이터가 있는 층을 나타내는 정수)
// return : 0층으로 가기 위해 필요한 마법의 돌의 최소값(Number)

// condition :

// algorithm : dfs

// logic(psuedo) : 




/* solution(구현) */

function solution(storey) {

  let answer = Number.MAX_SAFE_INTEGER;

  const dfs = (num, cnt) => {
    if (cnt >= answer) return;

    if (num === 0) { answer = cnt; }
    else {
      dfs(Math.floor(num / 10), cnt + num % 10);
      dfs(Math.floor(num / 10) + 1, cnt + 10 - num % 10);
    }
  }
  
  dfs(storey, 0);
    
  return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.16ms, 33.5MB)
// 테스트 2 〉    통과 (0.06ms, 33.4MB)
// 테스트 3 〉    통과 (0.14ms, 33.4MB)
// 테스트 4 〉    통과 (0.15ms, 33.5MB)
// 테스트 5 〉    통과 (0.15ms, 33.5MB)
// 테스트 6 〉    통과 (0.14ms, 33.5MB)
// 테스트 7 〉    통과 (0.16ms, 33.4MB)
// 테스트 8 〉    통과 (0.16ms, 33.4MB)
// 테스트 9 〉    통과 (0.15ms, 33.4MB)
// 테스트 10 〉 통과 (0.26ms, 33.4MB)
// 테스트 11 〉 통과 (0.22ms, 33.4MB)
// 테스트 12 〉 통과 (0.15ms, 33.4MB)
// 테스트 13 〉 통과 (0.14ms, 33.4MB)



/* check(검증) - Big O Notation */

// time complexity  : O(n)
// space complexity : O(n)

유사 칸토어 비트열

/* redefine(재정의) */

// input1 : n (유사 칸토어 비트열 순번: n번째) - Number
// input2 : l (1-base index 시작점) - Number
// input3 : r (1-base index 마침점) - Number
// return : 1-base index 폐구간[l,r] 내의 1의 개수 - Number

// condition1 : n : [1,20]
// condition2 : l,r : [1,5**n]
// condition3 : r : [l,l+10000000)

// algorithm1 : 메모리제이션
// algorithm2 : 수학(5진법)

// logic(psuedo) : 


/* solution(구현) */

function solution(n, l, r) {

    const CantorLike = (preCount) => {
        
        // 0. currCount = preCount;
        const currCount = [...preCount];
        
        // 1. max값 : currCount[currCount.length - 1], arr : preCount 리스트
        let maxCnt = currCount[currCount.length - 1];
        
        // 2. arr을 순회하며 각 (요소 + max) 값을 currCount에 push, max 갱신 : currCount[currCount.length - 1]
        for (let i=0; i<preCount.length; i++) { currCount.push(preCount[i] + maxCnt); }
        maxCnt = currCount[currCount.length - 1];
        
        // 3. 5**(n-1)만큼 max 값을 currCount에 push
        for (let i=0; i<preCount.length; i++) { currCount.push(maxCnt); }
        
        // 4. 2번 단계를 반복
        for (let i=0; i<preCount.length; i++) { currCount.push(preCount[i] + maxCnt); }
        maxCnt = currCount[currCount.length - 1];
        
        // 5. 2번 단계를 반복
        for (let i=0; i<preCount.length; i++) { currCount.push(preCount[i] + maxCnt); }
        
        // 6. currCount 리스트를 return
        return currCount;
    }
    
    let countOne = [1];
    let m = 0;
    while (m < n) {
        m++;
        countOne = CantorLike(countOne);
    }
    
    return countOne[r-1] - countOne[l-1] + 1;
}


/* 결과 */
// 일부 런타임 에러 발생, 토의 필요


/* check(검증) - Big O Notation */

// time complexity  : O(n * 5^(n-1))
// space complexity : O(5^n)




/* improvements(개선점) */

// n번째 = n-1번째 + n-1번째 + '0'.repeat(n-1번째 길이만큼) + n-1번째 + n-1번째

// index -> 5진법 변환 (~0, ~1, ~2, ~3, ~4)
// <경우1> : ~에 2가 포함되어 있지 않은 경우 : 11011 패턴
// <경우2> : ~에 2가 포함되어 있는 경우     : 00000 패턴

// 1이 나오는 경우는 <경우1>에서 끝자리가 2가 아닌 경우, 즉 5진법으로 변환했을때 2가 하나도 없는 경우
// !i.toString(5).match('2')

function solution(n, l, r) {
    let answer = 0;
    for (let i = l - 1; i <=r - 1; i++) {
        if (!i.toString(5).match('2')) answer += 1;
    }
    return answer;
}


/* check(검증) - Big O Notation */

// time complexity  : O(r - l + 1)
// space complexity :  O(1)

혼자 놀기의 달인

/* redifine(재정의) */

// input : cards : number[]
// return : 게임 내 얻을 수 있는 최고 점수 : number

// condition1 : cards 길이 : [2, 100]
// condition2 : cards 요소 : [1, cards.length]

// algorithm : closed graph

// logic(psuedo) : 




/* solution(구현) */

function solution(cards) {
    let group = [];                                     // 각 그룹에 속하는 상자 수 저장                   
    let visited = new Array(cards.length).fill(false);  // 노드 방문 여부 저장
    
    for (let i=0; i<cards.length; i++) {
        if (visited[i]===true) continue;  // 방문한 노드라면 continue
        
        let cnt = 0;                      // 상자 수
        let idx = i;                      // 노드 번호
        
        while (visited[idx]===false) {    // 닫힌 그래프 내 방문 처리가 안된 노드들 순회
            visited[idx] = true;          // 방문 처리
            cnt++;                        // 상자 수++
            idx = cards[idx] - 1;         // 다음 노드 설정
        }
        group.push(cnt);                  // group별 cnt push
    }
    
    group.sort((a, b)=> a - b);           // group 오름차순 정렬
    
    return (group.length === 1) 
        ? 0                                              // 최종 그룹 개수가 1개인 경우
        : group[group.length-1] * group[group.length-2]  // 최대 점수
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.07ms, 33.5MB)
// 테스트 2 〉    통과 (0.15ms, 33.4MB)
// 테스트 3 〉    통과 (0.16ms, 33.4MB)
// 테스트 4 〉    통과 (0.16ms, 33.5MB)
// 테스트 5 〉    통과 (0.15ms, 33.5MB)
// 테스트 6 〉    통과 (0.16ms, 33.6MB)
// 테스트 7 〉    통과 (0.17ms, 33.4MB)
// 테스트 8 〉    통과 (0.18ms, 33.4MB)
// 테스트 9 〉    통과 (0.16ms, 33.5MB)
// 테스트 10 〉 통과 (0.17ms, 33.4MB)


/* check(검증) - Big O Notation */

// time complexity  : O(nlogn)
// space complexity : O(n)

테이블 해시 함수

/* redefine(재정의) */

// input1 : data : number 타입인 컬럼들(튜플)로 이루어진 테이블
// input2 : col(number)
// input3 : row_begin(number)
// input4 : row_end(number)
// return : 테이블의 해시 값(number)

// condition1 : 1-index 기준
// condition2 : data 길이 : [1, 2500]
// condition3 : data의 원소 길이 : [1, 500]
// condition4 : data[i][j] : [1, 1000000]
// condition5 : col : [1, data의 원소의 길이]
// condition6 : 1 <= row_begin <= row_end <= data의 길이
// condition7 : 튜플의 첫번째 값은 기본키이므로 중복 x

// algorithm : 다중 조건 정렬, bittwise XOR

// logic(psuedo) : 
// 1. 다중 조건 정렬(|| 연산자 사용)
// 2. S_i를 갱신하면서 answer 갱신(^)


/* solution(구현) */

function solution(data, col, row_begin, row_end) {
    let answer = 0;
    let sum = 0;
    
    data.sort((a, b) => a[col-1] - b[col-1] || b[0] - a[0])  // 다중 조건 정렬을 위해 || 연산자 이용
    
    for (let i=row_begin-1; i<row_end; i++) {
        sum = 0;
        data[i].forEach(el => sum += el%(i+1));              // S_i 갱신
        answer = answer ^ sum;                               // bitwise XOR
    }
    
    return answer;
}


/* 참고 링크 */
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.24ms, 33.4MB)
// 테스트 2 〉    통과 (0.21ms, 33.6MB)
// 테스트 3 〉    통과 (0.21ms, 33.6MB)
// 테스트 4 〉    통과 (0.23ms, 33.7MB)
// 테스트 5 〉    통과 (0.99ms, 37.2MB)
// 테스트 6 〉    통과 (11.42ms, 83.3MB)
// 테스트 7 〉    통과 (7.84ms, 89.1MB)
// 테스트 8 〉    통과 (9.93ms, 88.7MB)
// 테스트 9 〉    통과 (11.10ms, 89.1MB)
// 테스트 10 〉 통과 (11.81ms, 88.5MB)
// 테스트 11 〉 통과 (0.07ms, 33.5MB)


/* check(검증) - Big O Notation */

// time complexity  : O(n*m)
// O(nlogn + (row_end - row_begin + 1) * m)에서 row_end - row_begin + 1의 최대 크기가 n이므로.

점 찍기

/* redefine(재정의) */

// input1 : k(간격 - number)
// input2 : d(원점과의 거리 - number)
// return : 찍히는 점의 총 개수 - number

// condition1 : k : [1,1000000]
// condition2 : d : [1,1000000]

// algorithm : 수학

// logic(psuedo) : 




/* solution(구현) */

function solution(k, d) {
    let answer = 0;
    
    for (let i=0; i<=d; i+=k) {
        if (i > d) break;
        answer += parseInt(Math.sqrt(d**2 - i**2)/k) + 1;
    }
    return answer;
}


/* 결과 */
// 정확성
// 테스트 1 〉    통과 (0.04ms, 33.4MB)
// 테스트 2 〉    통과 (0.04ms, 33.4MB)
// 테스트 3 〉    통과 (0.48ms, 33.7MB)
// 테스트 4 〉    통과 (0.31ms, 33.6MB)
// 테스트 5 〉    통과 (0.63ms, 33.9MB)
// 테스트 6 〉    통과 (0.54ms, 33.9MB)
// 테스트 7 〉    통과 (0.40ms, 33.7MB)
// 테스트 8 〉    통과 (2.97ms, 37.6MB)
// 테스트 9 〉    통과 (0.63ms, 33.8MB)
// 테스트 10 〉 통과 (2.39ms, 37.1MB)
// 테스트 11 〉 통과 (17.80ms, 37.9MB)
// 테스트 12 〉 통과 (0.06ms, 33.4MB)
// 테스트 13 〉 통과 (12.57ms, 37.9MB)
// 테스트 14 〉 통과 (7.88ms, 37.8MB)
// 테스트 15 〉 통과 (0.06ms, 33.6MB)
// 테스트 16 〉 통과 (0.04ms, 33.4MB)



/* check(검증) - Big O Notation */

// time complexity  : O(d/k)
// space complexity : O(1)