Unit1 - [자료구조/알고리즘] 재귀

예진·2022년 10월 22일
0
post-thumbnail

🔥 재귀(recursion)

재귀(再歸)의 개념

: 원래의 자리로 되돌아가거나 되돌아옴

function recursion () {
  console.log("This is")
  console.log("recursion!")
  recursion()
}
// `recursion` 함수를 호출하면 무한 루프 발생

=> 재귀 함수recursion 함수 처럼 자기 자신을 호출하는 함수를 의미한다.

재귀로 문제 해결하기

  1. 문제를 작게 쪼개기 (문제를 동일한 방식으로 계속 쪼개기)
    => 쪼개는 방식이 recursive case

  2. 문제를 가장 작은 단위로 쪼개기 (더 이상 안 쪼개지면 그것이 base case)
    => 재귀 함수의 탈출 조건

  3. 문제 해결하기 (base case 해결하기)
    => 쪼개졌던 문제가 한 번에 해결된다.

위 3단계를 적용해서 [1, 2, 3] 의 합을 구하는 arrSum 함수 작성

1) 문제를 작게 쪼개기 적용

/* 배열의 합을 구할 때 [1, 2, 3]의 합을 구하는 것보다
[2, 3]의 합을 구하는 것이 더 작은 문제 ... */

arrSum([1,2,3]) === 1 + arrSum([2,3])
arrSum([2,3]) === 2 + arrSum([3])
...

2) 문제를 가장 작은 단위로 쪼개기

// 위에서 문제를 쪼갠 방식을 반복해서 문제를 쪼개면 더이상 쪼갤 수 없는 상태 도달

arrSum([1,2,3]) === 1 + arrSum([2,3])
arrSum([2,3]) === 2 + arrSum([3])
arrSum([3]) === 3 + arrSum([])

3) 문제 해결하기

/* 문제를 쪼갤 때 같은 방식으로 쪼갰기 때문에
가장 작은 단위의 문제를 해결한 방식으로 문제 전체를 해결 가능 */

arrSum([]) === 0  // 문제가 더는 작아지지 않는 순간
arrSum([3]) === 3 + arrSum([]) === 3 + 0 === 3
arrSum([2,3]) ===  2 + arrSum([3]) === 2 + 3 === 5
arrSum([1,2,3]) === 1 + arrSum([2,3]) === 1 + 5 === 6

위 단계를 반영해 자연수로 이루어진 배열을 입력받고, 배열의 합을 리턴하는 함수 arrSum 작성

function arrSum (arr) {
  // 빈 배열을 입력 받았을 때 0을 리턴하는 조건문
  // 가장 작은 문제를 해결하는 코드, 재귀를 멈추는 코드
  if(arr.length === 0) {
    return 0
  }
  
  // 배열의 첫 요소 + 나머지 요소가 담긴 배열을 받는 arrSum 함수
  // 재귀(자기 자신 호출)를 통해 문제를 작게 쪼개나가는 코드
  return arr.shift() + arrSum(arr)

재귀를 사용하기에 적합한 상황

  • 주어진 문제를 비슷한 구조의 더 작은 문제로 나눌 수 있는 경우
  • 중첩된 반복문이 많거나 반복문의 중첩 횟수(number of loops)를 예측하기 어려운 경우

=> 모든 재귀 함수는 반복문으로 표현 가능하지만 재귀를 적용한 코드가 더욱 간결하고 이해하기 쉽다.

재귀적으로 사고하기

1. 재귀 함수의 입력값과 출력값 정의하기

재귀적 사고를 위해 가장 먼저 해야 할 일 : 문제를 추상적으로, 단순하게 정의하는 것

  • 함수 arrSum의 경우 number 타입을 요소로 갖는 배열을 입력으로 받고, number 타입 리턴
    => arrSum: [number] => number 입출력값 정의

2. 문제를 쪼개고 경우의 수를 나누기

문제를 쪼갤 기준을 정하고, 주어진 입력값에 따라 경우의 수를 나눠서
문제를 더 이상 쪼갤 수 없는 경우와 그렇지 않은 경우로 나눈다.

  • 함수 arrSum은 입력값이 빈 배열인 경우와 그렇지 않은 경우로 나눌 수 있다.
    => arrSum: [number] => number
    => arrSum([ ]) 입력값이 빈 배열인 경우
    => arrSum([요소1, 요소2, ... , 요소n]) 그렇지 않은 경우

3. 단순한 문제 해결하기

문제를 여러 경우로 구분한 다음, 가장 해결하기 쉬운 문제부터 해결 => 재귀의 기초(base case)
재귀의 기초(base case) : 재귀 함수를 구현할 때, 재귀의 탈출 조건(재귀 호출이 멈추는 조건)을 구성

( 탈출 조건이 없는 경우, 재귀 함수는 끝없이 자기 자신을 호출한다. 문제를 최대한 작게 쪼갠 후 문제 해결하는 것이 중요하다.)

  • 함수 arrSum을 더 이상 쪼갤 수 없는 경우는 입력값이 빈 배열일 경우이고, 이때 arrSum([])의 리턴값은 0이다.
    => arrSum: [number] => number
    => arrSum([ ]) === 0 입력값이 빈 배열인 경우 : "해결"
    => arrSum([요소1, 요소2, ... , 요소n])

4. 복잡한 문제 해결하기

  • 길이가 1 이상인 배열이 함수 arrSum에 입력된 경우,
    입력된 배열을 배열의 첫 요소와 나머지 요소를 입력값으로 갖는 문제로 쪼개고, 둘을 더한다.
    => arrSum: [number] => number
    => arrSum([ ]) === 0
    => arrSum([요소1, 요소2, ... , 요소n]) 그렇지 않은 경우 : "해결"
    => 배열을 첫 요소와 더 작은 문제로 쪼개는 방법만 안다면, 함수 arrSum을 재귀적으로 구현 가능

5. 코드 구현하기

function arrSum(arr) {
  // base case : 문제를 더 이상 쪼갤 수 없는 경우 (재귀의 기초)
  if (arr의 길이가 0인 경우) {
    return 0;
  }
  
  // recursive case : 그렇지 않은 경우
  return 요소1 + arrSum([요소2, ... , 요소n]);
}
function recursive (input1, input2, ...) {
  // base case : 문제를 더 이상 쪼갤 수 없는 경우 
  if (문제를 더 이상 쪼갤 수 없는 경우) {
    return 단순한 문제의 해답;
  }
  
  //recursive case : 그렇지 않은 경우
  return 더 작은 문제로 새롭게 정의된 문제
}

🔥 JSON(JavaScript Object Notation)

: 서로 다른 프로그램 사이에서 데이터 교환을 위해 만들어진 객체 형태의 포맷

전송 가능한 조건 (transferable condition)

  • 수신자(reciever)와 발신자(sender)가 같은 프로그램을 사용한다.
  • 문자열처럼 범용적으로 읽을 수 있어야 한다.

객체는 타입 변환을 이용해 String으로 변환할 경우 객체 내용을 포함하지 않는다.
- JavaScript에서 message.toString() 또는 String(message)을 시도하면 [object Object] 라는 결과를 리턴한다.

위 문제를 해결하는 방법
=> 객체를 JSON의 형태로 변환하거나 JSON을 객체의 형태로 변환

  • JSON.stringify : 객체를 JSON으로 변환 => 직렬화(serialize)

  • JSON.parse : JSON을 객체로 변환 => 역직렬화(deserialize)

// 메시지를 담고 있는 객체
const message = {
  sender: "김코딩",
  receiver: "박해커",
  message: "안녕?",
  createdAt: "2022-10-22 10:10:10"
}

let transMessage = JSON.stringify(message)

console.log(transMessage)
// `{"sender":"김코딩","receiver":"박해커","message":"안녕?","createdAt":"2022-10-22 10:10:10"}`

console.log(typeof(transMessage))
// `string`

=> message 객체를 JSON으로 변환하는 메서드 JSON.stringify

let packet = `{"sender":"김코딩","receiver":"박해커","message":"안녕?","createdAt":"2022-10-22 10:10:10"}`
let obj = JSON.parse(packet)

console.log(obj)
/*
{
  sender: "김코딩",
  receiver: "박해커",
  message: "안녕?",
  createdAt: "2022-10-22 10:10:10"
}
*/

console.log(typeof(obj))
// `object`

직렬화된 JSON에 메서드 JSON.parse를 적용하면 다시 객체의 형태로 변환할 수 있다.

JSON의 기본 규칙

JavaScript의 객체와 같은 규칙을 가지지 않는다.

profile
😊

0개의 댓글