재귀 문제 풀이

semin·2023년 6월 9일
0

section 3

목록 보기
1/5
post-thumbnail

비슷한 문제끼리 묶어서 풀이합니다.
개별 풀이는 주석 참고 부탁드립니다.

sumTo, factorial, fibonacci

function sumTo(num) {
  /*
  1. num => num
  2. base: num === 0
  3. recursive: num + sumTo(num-1)?
  */
  if (num === 0) return 0;
  return num + sumTo(num - 1);
}

/////

function factorial(num) {
  /*
  1. num => num
  2. base: num === 0
  3. recursive: num * factorial(num - 1)
  */
  if (num === 0) return 1;
  return num * factorial(num - 1);
}

/////

function fibonacci(num) {
  /*
  1. num => num
  2. base: num <= 1
  3. recursive: fibonacci(num - 2) + fibonacci(num - 1)
  */
  if (num <= 1) return num;
  return fibonacci(num - 2) + fibonacci(num - 1);
}
memoization: 계산된 값을 메모리에 저장해 다시 쓰는 기법

세 문제 모두 memoization 이 금지됩니다.
입출력값은 num => num 으로 정의되고,
각 함수는 기본 사례(base case)와 재귀 사례(recursive case)로 구성됩니다.

base
입력이 조건을 만족할 때 함수가 바로 결과값을 반환하도록 합니다.

recursive
입력을 더 작은 부분 문제로 나누고,
함수를 자기 자신에게서 호출하여 더 작은 부분 문제를 해결합니다.

조건문과 재귀로만 해결할 수 있는 문제입니다.

drop & take

function drop(num, arr) {
  /*
  1. num, [arr] => [arr.length - 3]
  2. base: arr.length <= num or num === 0
  3. recursive: drop(num-1, arr)
  */
  if (arr.length <= 0) return [];
  else if (num === 0) return arr;
  arr.shift(); // 미리 빼 줘야 함
  return drop(num-1, arr);
}

/////

function take(num, arr) {
  /*
  1. num, [arr] => sliced arr
  2. base: arr.length <= num
  3. recursive: result.pop 후 take(num, result);
  */
  if (arr.length <= num) return arr;
  let result = arr.slice();
  result.pop();
  return take(num, result);
}

입출력 값: num, [arr] => [arr]
base
조건에 따라 배열의 일부 또는 전체, 또는 []을 반환

recursive
새 배열을 생성, 기존 배열을 수정 후 재귀
재귀하며 배열이 변형되고, 기본 사례에 도달할 때까지 반복 실행됩니다.
재귀와 배열 요소 제거 메서드를 사용해 해결하는 문제입니다.

reverseArr

function reverseArr(arr) {
  /*
  1. [some] => [emos]
  2. base: arr.length < 1
  3. recursive: reverseArr(arr.slice(1번째))로 재귀하면서
     .concat(arr.shift())
  */
  if (arr.length === 0) return [];
  return reverseArr(arr.slice(1)).concat(arr.shift());
}

배열을 뒤집는 문제입니다.

base
주어진 배열의 길이가 0일 때 빈 배열을 반환
재귀를 멈추고 배열을 뒤집어 재구성

recursive
0번째 요소를 제거한 arr.slice(1)을 재귀 함수로 전달
이후 제거된 요소는 arr.shift()로 리턴
해당 요소를 concat -> 재귀 결과에 결합
다시 돌아가며 배열 재구성

findMatryoshka (fmk)

function findMatryoshka(matryoshka, size) {
  /*
  1. {{{...}}} => boolean
  2. base: Object.keys(mat).length === 0
  3. recursive: mat.size, 재귀: fmk(mat.mat, size);
  */
  
// 탈출 조건
  if (Object.keys(matryoshka).length === 0) return false;
  
// 첫 객체에서 같으면 나오기
  else if (matryoshka.size === size) return true;
  
// 받은 객체에 없으면 내부 객체 있는지 확인, 내부 객체로 재귀
  else if (matryoshka.matryoshka && matryoshka.size) 
  return findMatryoshka(matryoshka.matryoshka,size);
  
// 위에서 다 없으면 false
  else return false; 
}

base
마트료시카의 속성 개수가 0일 때 return false
= 재귀 종료, 같은 size 없음

recursive

  1. 현재 마트료시카의 크기와 입력받은 크기가 일치하는지 확인
    1-1. 일치하면 true, 재귀 종료
    1-2. 일치하지 않는 경우 -> 2

  2. 현재 마트료시카 객체에 내부 마트료시카 객체(mat.mat) 유무와
    크기(mat.size)를 확인
    2-1. 내부 마트료시카 객체로 재귀

  3. 내부 마트료시카를 검사하고 크기가 일치하는 객체를 찾을 때까지 탐색

  4. 내부 객체가 없거나 크기가 일치하는 객체를 찾지 못하면 false

unpackGiftbox,

function unpackGiftbox(giftBox, wish) {
  /*
  1. [..[..]] => boolean
  2. base: let el of giftBox에 대한 반복문 종료 시
  3. recursive: unpackGiftbox(el, wish)
  */
  let count = 0; // 있으면 카운트해서 대소비교로 boolean 리턴
  for (let el of giftBox) {
    if (Array.isArray(el)) { // 2차원 배열 있을 때
      count += unpackGiftbox(el, wish); // 배열 없을 때까지 재귀
    } else if (el === wish) count++;
    // 배열 없으면 wish 찾으면서 count하며 밖으로 나가기
  }
  return count > 0;
}


/////


function flattenArr(arr) {
  // TODO: 여기에 코드를 작성합니다.
  /*
  1. [[[...]]] => []
  2. base: let element of arr에 대한 반복문 종료 시
  3. recursive: flattenArr(element)로 재귀하면서
     새 변수에 el을 추가해야 함 (배열: concat, else: push)
  */
  let flattened = [];
  for (let element of arr) {
    if (Array.isArray(element)) {
      flattened = flattened.concat(flattenArr(element));
    } else flattened.push(element);
  }
  return flattened;
}

base
배열 안에 더 이상 다른 배열이 없을 때
-> 작업 수행, 결과 반환

recursive

  1. 배열 탐색 반복문
    1-1. 배열 안에 다른 배열이 있는 경우
    해당 배열에 대해 재귀 함수 호출
    1-2. 배열 내부 요소 변경
  2. 결과를 새로운 배열에 저장

0개의 댓글