티스토리 뷰
💡 문제
🔍 분석 및 계획
✅ 문제 이해
핸드폰 키패드 숫자가 주어질때 거리를 비교해 양손 중 어느쪽 손가락으로 눌렀는지 각 주어진 번호를 누른 입력 손가락을 문자열에 순서대로 담아 반환하는 문제
- 입력 값 : 숫자 배열, 오른손잡이인지 왼손잡이인지 나타내는 문자열
- 1 <= 숫자 배열 길이 <= 1,000 / 빈배열 없음
- 숫자 배열 원소의 값 : 0~9
- 문자열은 "left" or "right" - 출력 값 : 사용한 손가락을 담은 문자열
- 왼손인 경우 "L", 오른손인경우 "R"
이 문제 풀이 순서는
- 각 손가락의 위치를 저장할 변수 L / R, 순서를 담은 배열 order
- 숫자 배열에 따른 반복문 처리
- 1, 4, 7 인 경우 => 왼손
- 3, 6, 9 인 경우 => 오른손
- 2, 5, 8, 0 인 경우
- L과 R로 부터 주어진 번호까지의 거리 계산
- 두 거리 비교하여 => 왼손, 오른쪽 결정
- order를 문자열로 변환하여 반환
(*어떤 손가락으로 눌렀는지 결정되면, order에 추가하고 손가락 위치를 업데이트 해준다)
이 과정의 핵심은 2번 가운데 열의 숫자 차례때의 거리계산이다
각 번호는 한줄에 3개의 숫자씩 나열되어 있으므로 가로로는 1씩, 세로로는 3씩 차이가 난다는 것을 이용하면된다
3으로 나눴을경우 같은 몫의 올림 값 차이만큼 이동하는 것이 세로 이동거리, 나머지의 차이만큼 이동하는 것이 가로거리이다
나머지 차이로 계산하면 예외상황이 생겨버린다. 예를 들어 3, 4를 나머지 차이로 거리계산을 한다면 실제 거리는 2칸인데 1칸으로 계산된다. 왜냐하면 3에서 4로 넘어가면 내려쓰기로 줄이 변경되기 때문이다.
이 부분때문에 고민을 하였으나 딱히 좋은 방법을 찾지 못해 반복문으로 3이하의 숫자가 될때까지 3씩 빼준후의 차이의 절대값으로 계산했다.. (BAD)
예를 들어, 3과 7의 거리를 구한다고 가정하자
3과 7을 각각 3으로 나눴을 경우 몫인 1, 2.333...을 올림하게 되면 1, 3이 나온다. 따라서 세로이동은 2칸이다.
그리고 가로칸은 두 수가 1~3사이의 수가 되도록 3씩 빼준다
그러면 3 은 그대로 3이고, 7은 3을 2번빼게되면 1이된다. 이 두 수의 차이는 2이므로 가로이동은 2칸이다 (BAD)
여기서 예외사항은 0이다. 0은 실제로 숫자가 9이상으로 채워진다고 가정한다면 * = 10, 0 = 11, # = 12의 차리이므로 계산할때 일시적으로 11로 치환해서 계산해주면 된다.
위 계산순서에 따라서 의사코드를 작성해보자
✅ 의사코드 작성
// 각 엄지손가락 위치를 저장할 변수 lPos, Rpos;
// numbers 원소를 순회할 reduce함수(order, curPos)
// 원소가 1, 4, 7일 경우
// lPos = curPos, order.push("L")
// 원소가 3, 6, 9일 경우
// rPos = curPos, order.push("R")
// 원소가 2, 5, 8, 0 일경우
// 세로 거리 계산
// 가로 거리 계산
// 거리 비교하여 짧은거리 손가락을 위와 같이 설정
// 같을 경우 손잡이손가락을 위와 같이 설정
// 문자열로 반환하여 반환
📝 코드 구현
의사코드 + 코드
function solution(numbers, hand) {
// 각 엄지손가락 위치를 저장할 변수 lPos, Rpos;
let lPos = 10, rPos = 12; // * = 10, # = 12
// numbers 원소를 순회할 reduce함수(order, curPos)
return numbers.reduce((order, curPos) => {
// 원소가 1, 4, 7일 경우
if ([1, 4, 7].includes(curPos)) {
// lPos = curPos, order.push("L")
lPos = curPos;
order.push("L");
}
// 원소가 3, 6, 9일 경우
else if ([3, 6, 9].includes(curPos)) {
// rPos = curPos, order.push("R")
rPos = curPos;
order.push("R");
}
// 원소가 2, 5, 8, 0 일경우
else {
let lMove = 0, rMove = 0;
if (curPos === 0) curPos = 11; // 0일 경우 거리 재정의
// 세로 거리 계산
lMove += Math.abs(Math.ceil(curPos / 3) - Math.ceil(lPos / 3));
rMove += Math.abs(Math.ceil(curPos / 3) - Math.ceil(rPos / 3));
// 가로 거리 계산
let tmpCurPos = curPos;
let tmpLPos = lPos;
let tmpRPos = rPos;
while (tmpCurPos > 3) tmpCurPos -= 3;
while (tmpLPos > 3) tmpLPos -= 3;
while (tmpRPos > 3) tmpRPos -= 3;
lMove += Math.abs(tmpCurPos - tmpLPos);
rMove += Math.abs(tmpCurPos - tmpRPos);
// 거리 비교하여 짧은거리 손가락을 위와 같이 설정
if (lMove < rMove) {
lPos = curPos;
order.push("L");
} else if (lMove > rMove) {
rPos = curPos;
order.push("R");
}
// 같을 경우 손잡이손가락을 위와 같이 설정
else {
if (hand === "left") {
lPos = curPos;
order.push("L");
} else {
rPos = curPos;
order.push("R");
}
}
}
return order;
// 문자열로 변환하여 반환
}, []).join("");
}
최종 코드
function solution(numbers, hand) {
let lPos = 10, rPos = 12; // * = 10, # = 12
return numbers.reduce((order, curPos) => {
if ([1, 4, 7].includes(curPos)) {
lPos = curPos;
order.push("L");
}
else if ([3, 6, 9].includes(curPos)) {
rPos = curPos;
order.push("R");
}
else { // 2, 5, 8, 0
let lMove = 0, rMove = 0;
if (curPos === 0) curPos = 11; // 0
lMove += Math.abs(Math.ceil(curPos / 3) - Math.ceil(lPos / 3));
rMove += Math.abs(Math.ceil(curPos / 3) - Math.ceil(rPos / 3));
let tmpCurPos = curPos;
let tmpLPos = lPos;
let tmpRPos = rPos;
while (tmpCurPos > 3) tmpCurPos -= 3;
while (tmpLPos > 3) tmpLPos -= 3;
while (tmpRPos > 3) tmpRPos -= 3;
lMove += Math.abs(tmpCurPos - tmpLPos);
rMove += Math.abs(tmpCurPos - tmpRPos);
if (lMove < rMove) {
lPos = curPos;
order.push("L");
} else if (lMove > rMove) {
rPos = curPos;
order.push("R");
}
else {
if (hand === "left") {
lPos = curPos;
order.push("L");
} else {
rPos = curPos;
order.push("R");
}
}
}
return order;
}, []).join("");
}
시간 복잡도 : O(n²) => n에 제한이 없을 경우
💭 되돌아 보기
✅ 다른 사람 풀이
1.
코딩을 그만두고 싶게 만드는 코드다
우선, 내가 실수한 점은 가운데 줄일때만 거리 계산을 한다는 점이다. 처음에 계획을 잘못세운다음 다시 일부 수정하면서 이전 풀이순서를 그대로 가져오면서, 1열과 3열을 비교해야하는 상황까지 고려하는 참사가 발생했다
이 풀이는 변수를 객체로 생성함으로써 엄청난 이점을 챙겼다
키값을 활용해 거리가 같을경우의 손잡이 처리를 손쉽게 처리했을 뿐아니라 변수 개수가 줄여 가독성이 훨씬 좋아진다
function solution(numbers, hand) {
hand = hand[0] === "r" ? "R" : "L"
let position = [1, 4, 4, 4, 3, 3, 3, 2, 2, 2]
let h = { L: [1, 1], R: [1, 1] }
return numbers.map(x => {
if (/[147]/.test(x)) {
h.L = [position[x], 1]
return "L"
}
if (/[369]/.test(x)) {
h.R = [position[x], 1]
return "R"
}
let distL = Math.abs(position[x] - h.L[0]) + h.L[1]
let distR = Math.abs(position[x] - h.R[0]) + h.R[1]
if (distL === distR) {
h[hand] = [position[x], 0]
return hand
}
if (distL < distR) {
h.L = [position[x], 0]
return "L"
}
h.R = [position[x], 0]
return "R"
}).join("")
}
'IT기초 > 프로그래머스 문제풀이' 카테고리의 다른 글
| [프로그래머스 LV.1] #32 체육복 (JavaScript) (0) | 2023.05.31 |
|---|---|
| [프로그래머스 LV.1] #31 크레인 인형뽑기 게임 (JavaScript) (0) | 2023.05.30 |
| [프로그래머스 LV.1] #29 두 개 뽑아서 더하기 (JavaScript) (0) | 2023.05.28 |
| [프로그래머스 LV.1] #28 3진법 뒤집기 (JavaScript) (0) | 2023.05.27 |
| [프로그래머스 LV.1] #27 내적 (JavaScript) (0) | 2023.05.26 |
