더해서 특정 수가 나오는 배열 값의 index 구하기

DongHee Kim·2021년 8월 18일
1

Algorithm

목록 보기
8/12
post-thumbnail

문제

twoSum함수에 숫자배열과 '특정 수'를 인자로 넘기면, 더해서 '특정 수'가 나오는 index를 배열에 담아 return해 주세요.

nums: 숫자 배열
target: 두 수를 더해서 나올 수 있는 합계
return: 두 수의 index를 가진 숫자 배열

예를 들어,
nums은 [4, 9, 11, 14] target은 13
nums[0] + nums[1] = 4 + 9 = 13 이죠?
그러면 [0, 1]이 return 되어야 합니다.

가정
target으로 보내는 합계의 조합은 배열 전체 중에 2개 밖에 없다고 가정하겠습니다.

첫번째 풀이 - 중첩 for문

사고의 흐름

Step.1 : 중첩 for문으로 정답 완성

두 개의 수를 가져와야 하므로 중첩 for문을 이용해 i, j를 가져오고, 이 둘을 더해서 target값이 나오는 경우 [i, j]값을 리턴했다.

중첩 for문 원리가 살짝 헷갈렸는데, 아래 그림을 참고하자.
상위 for문의 i=0일 때 하위 for문의 j가 한 바퀴 다 돌고, 그 다음 i=1.. 이런식으로 나아가는 거라 생각하면 된다. 큰 사이클 안에 작은 사이클이 돌아가는 셈

아래는 상위 for문의 i가 0부터 3까지, 하위 for문의 j가 1부터 3까지 반복될 때의 경우이다.

문제에선 상위 for문의 i=0일 때 하위 for문의 j가 i+1부터 nums.length전까지 휘리릭 돌고,
i=1일 때 j가 2부터 nums.length전까지 다시 한 바퀴 돌고..
이런식으로 풀이하면 정답을 찾을 수 있었다. 그림으로 다시 한 번 보자면

위 그림을 코드로 옮기면 아래와 같다.

const twoSum = (nums, target) => {
  for (let i=0; i<nums.length; i++) {
    for (let j= i+1; j<nums.length; j++){
      if(nums[i] + nums[j] === target) {
        return [i,j]
      }
    }
  }
}

Step.2 : 해결되지 않은 궁금증

사실 위 코드만으로 정답이 나왔는데, 해결되지 않은 궁금증이 있었다.
if문에 else {return('오답')} 를 기입할 경우 index의 배열이 [0,2], [0,3]와 같이 각각의 값이 서로 간격이 있을 경우,정답이 나와야 할 때에도 오답이 나왔다.

뭘까..무슨 일일까.. 혹시 문제에 오류가 있나? (헛된 생각..)
페어 분이랑 별별 생각을 다하며 고민에 빠졌다.

//오답이 나오던 코드
const twoSum = (nums, target) => {
  for (let i=0; i<nums.length; i++) {
    for (let j= i+1; j<nums.length; j++){
      if(nums[i] + nums[j] === target) {
        return [i,j]
      } else {
        return ('오답')
    }
  }
}

Step.3 : 궁금증 해결 완료

그러다 동기 분의 힌트로 해결 완료!
앞서 작성했던 중첩 for문의 원리를 제대로 이해하지 못해 일어난 문제였다.

상위 for문의 i=0일 때 하위 for문의 j가 1부터 nums.length전까지 휘리릭 한 바퀴 다 돌고
i=1일 때 j가 2부터 nums.length전까지 다시 한 바퀴 돈다고 적어두었다.
이걸 명심하고 아래의 흐름을 보자.

//오답이 나오던 코드
const twoSum = (nums, target) => {
  for (let i=0; i<nums.length; i++) {
    for (let j= i+1; j<nums.length; j++){
      if(nums[i] + nums[j] === target) {
        return [i,j]
      } else {
        return ('오답') //여기를 주목! 
    }
  }
}

i=0일 때 j의 정답값이 3이라면, j가 1, 2, 3...반복문을 돌다가 3을 만나 정답 처리가 되어야 한다.
하지만 위 함수에서 for문의 if문에 else {return('오답')} 을 걸어버리면
함수는 return을 만나면 끝나버리므로 오답을 만나는 즉시 오답을 리턴하고 끝나버린다. 오답을 만나도 다음 정답을 향해 영차영차 돌아가야할 for문이 뚝 끊겨버리는 것. 따라서 if문에 else값을 설정하지 않는다!


두 번째 풀이 - forEach문

사고의 흐름

Step.1 : 중첩 for문을 forEach문으로

정답은 나왔지만.. for문을 forEach문으로 돌리면 더 간단해진다는 동기분의 말에 자극받아 기존 중첩 for문을 forEach문으로 바꿔보기로 했다.

forEach문 안에 arrow function을 넣고, 그 인자를 num(반복문으로 돌아가는 nums 배열 안의 값), i(num의 index)로 설정했다.

Step.2 : 논리연산자 &&로 조건문 설정

동기 분께서 주신 힌트를 기반으로 num의 i값을 구하는 함수를 구현해냈다.

자 생각해보자.. a+b=target이라 한다면, a는 어떻게 구할 수 있을까?
어릴 때 배운 일차 방정식을 활용하면 결국 a=target-b이다.

여기서 착안한 방식으로, nums.indexOf(target-num)을 통해
target-num 값이 nums배열 안에 존재한다면(!= -1), 이 때 num의 index를 빈 배열 result에 push했다.

Javascript에선 && 논리연산자를 활용해 조건문의 형태를 만들 수 있으므로,이를 활용해 조건문은 간소하게 마무리했다.
(조건) %% (true일 경우 실행 로직)

  const twoSum = (nums, target) => {
  const result = [];
  nums.forEach((num,i)=>{ 
    (nums.indexOf(target-num) != -1) &&
      result.push(i);
    }
  )
return result;
}

느낀점

  • 간단한 for문 돌릴줄만 알지 중첩문의 원리를 자세히 알고있지 못했다. 이번 기회에 제대로 배웠으니 (그림까지 그려가며 고민했더니 잘 이해되었다!) 앞으로 잘 활용해보자

  • 잊지마! 함수에서 return을 만나면 그 함수는 종료된다. 함수와 반복문이 겹칠 경우엔 이번처럼 반복문이 망가질 수 있으니 유의하자.

  • forEach문 풀이에서 방정식을 활용한 부분.. 사실 혼자선 먼저 떠올리지 못했던 아이디어다. 동기 분의 수학적사고가 너무 대단하고 멋있게 느껴졌다. 1차원적으로 특정 값을 찍을 생각만 하지 말고, 수학적으로 고민하며 찾아보자.

  • 조건을 설정할 때만 쓰는 줄 알았던 논리연산자 &&로 조건문을 만들 수 있다는 걸 배웠다. false일 때의 값도 표기해야하는 삼항연산자보다 훨씬 간단해서 앞으로 유용하게 쓸 수 있을 것 같다.

profile
일상의 성실이 자존감을 만드는 성취주의자

1개의 댓글

comment-user-thumbnail
2021년 8월 21일

사고의 흐름을 정리하고 그림을 통해서 정확하게 작동 방식을 이해하는 모습이 정말 인상깊습니다!
항상 성실하게 알고리즘이든, 미션이든 뿌셔나가는 동희님 정말 멋있습니다 👍
남은 기간도 킵고잉~~!! ✨ 🏃🏻

답글 달기