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문을 이용해 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]
}
}
}
}
사실 위 코드만으로 정답이 나왔는데, 해결되지 않은 궁금증이 있었다.
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 ('오답')
}
}
}
그러다 동기 분의 힌트로 해결 완료!
앞서 작성했던 중첩 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값을 설정하지 않는다!
정답은 나왔지만.. for문을 forEach문으로 돌리면 더 간단해진다는 동기분의 말에 자극받아 기존 중첩 for문을 forEach문으로 바꿔보기로 했다.
forEach문 안에 arrow function을 넣고, 그 인자를 num(반복문으로 돌아가는 nums 배열 안의 값)
, i(num의 index)
로 설정했다.
동기 분께서 주신 힌트를 기반으로 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일 때의 값도 표기해야하는 삼항연산자보다 훨씬 간단해서 앞으로 유용하게 쓸 수 있을 것 같다.
사고의 흐름을 정리하고 그림을 통해서 정확하게 작동 방식을 이해하는 모습이 정말 인상깊습니다!
항상 성실하게 알고리즘이든, 미션이든 뿌셔나가는 동희님 정말 멋있습니다 👍
남은 기간도 킵고잉~~!! ✨ 🏃🏻