게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌23 - Algorithm - 6년 전 등록 / 4년 전 수정

프로그래머스 문제 풀이 Level 1

1번~10번

안녕하세요. 이번 시간에는 프로그래머스의 알고리즘 문제 풀이를 하겠습니다. 사용 언어는 자바스크립트입니다. 혹시나 다른 알고리즘 문제 사이트 중 자바스크립트를 지원하고, 문제 내용이 알찬(백준처럼 너무 많지는 않은) 사이트가 있다면 알려주세요~ 시간될 때마다 풀어보겠습니다.

Level 3까지는 제가 쉽게 푸는데 4랑 5는 좀 고민을 해야겠더군요 ㅠㅠ. 알고리즘 수업을 들은 지 오래 돼서요. 못 푸는 문제가 있다면 일단 그건 뒤로 보류하고 푼 문제부터 알려드리겠습니다. 여기 풀이는 모두 제가 직접 푼 풀이입니다. 혹시나 더 나은 풀이가 있다면 댓글로 알려주세요.

프로그래머스 알고리즘을 풀 생각이 있으신 분들은 아래 풀이를 보지 마세요. 고민하지 않고 답 먼저 본다면 전혀 실력이 늘지 않습니다. 고민을 하고 다양한 시도를 해야 실력이 느는 것 같습니다. 저도 뭐 알고리즘 대회 입상한 적도 없고, 참가할 생각도 없는 그냥 평범한 사람입니다. 유일한 업적은 대학교 수업 알고리즘 A+받은 것뿐이랄까요 ㅋㅋ. 그냥 고민하면서 한 문제씩 풀어나가고 있습니다.

문제들을 보니까 if문과 for문만 있으면 간단하게 풀 수 있어 보입니다. 하지만 저는 for문을 쓰지 않는 코딩을 하기(map, forEach 등의 함수형 메서드를 더 선호합니다) 때문에 여러분이 푼 답과 조금 다를 수도 있습니다. 특히 한 줄로 문제를 풀어버리는 것을 좋아해서 짧고 간결하게 풀어보겠습니다. 그리고 몇몇 문제는 정규표현식을 쓰면 엄청 간단하게 풀 수 있습니다. 그래서 어떠한 문제는 정규표현식과 일반 풀이 두 개를 모두 제공해드리겠습니다. 단순한 알고리즘 풀이 외에도 자바스크립트의 메서드나 여러 줄임 표현식같은 것을 알려드리고 있기에 한 번 씩 봐보셔도 좋습니다.

Level 1 1번~10번

2016

2016년 a월 b일은 무슨 요일일까요?라는 문제입니다.

new Date에서 month가 0부터 시작(1월이 0이고, 12월이 11)한다는 것을 알고, getDay가 요일을 가져오는 메서드라는 것을 이용하면 쉽게 풀 수 있습니다.

function solution(a, b) {
  return ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'][new Date(2016, a - 1, b).getDay()];
}

짧은 리스트는 위와 같이 배열로 그냥 하드코딩하는 게 편하기도 합니다.

가운데 글자 가져오기

abcde에서는 c를 가져오고 qwer에서는 we 두 글자를 가져오는 문제입니다.

보통 이런 문제는 글자를 자르면 됩니다. 인덱스를 구하면 되죠.

function solution(s) {
  return s.substr(Math.ceil(s.length / 2) - 1, s.length % 2 === 0 ? 2 : 1);
}

substr로 문자열을 자를 수 있고, 시작 인덱스를 적절히 찾으면 됩니다. 중간점을 찾으려면 보통 s.length / 2를 내림하거나 올림하면 됩니다.

같은 숫자는 싫어

[1,1,3,3,0,1,1]이면 [1,3,0,1]로 연속되는 중복을 제거하는 문제죠.

이런 문제는 필연적으로 반복문이 최소 한 번은 들어가게 됩니다. 사람이라면 어떻게 중복을 제거할 지를 생각해봐도 좋을 것 같습니다. 저라면 지금 숫자랑 다음 숫자랑 같으면 그 숫자는 없애는 방식을 택하겠습니다. 1, 1, 3, 3, 0, 1, 1

function solution(arr) {
  return arr.filter((v, i) => v !== arr[i + 1]);
}

반복문을 돌면서 숫자를 없앨 수 있는 메서드가 있습니다. 바로 filter죠. 그것을 사용하면 쉽게 다음 숫자랑 같은 숫자를 제거할 수 있습니다.

나누어 떨어지는 숫자 배열 

[5,9,7,10]과 5가 주어지면 [5, 10]을 리턴해야 합니다. 아무것도 없으면 [-1]을 리턴합니다.

역시나 filter로 쉽게 필터링할 수 있습니다. 근데 [-1]을 리턴해야하는 조항 때문에 한 줄로 못 끝내서 짜증이 나네요(한 줄로 하면 코드가 너무 지저분해집니다)

function solution(arr, divisor) {
  const answer = arr.filter(el => el % divisor === 0);
  return answer.length ? answer.sort((p, c) => p - c) : [-1];
}

마지막에 정답 배열의 개수로 오름차순 정렬을 할지, [-1]을 리턴할지 결정하고 있습니다.

두 정수 사이의 합 

3과 5가 주어지면 3+4+5를 해서 리턴하면 됩니다.

function solution(a, b) {
  a > b && ([a, b] = [b, a]);
  return Array(b - a + 1).fill().map((v, i) => v + i).reduce((a, c) => a + c);
}

5, 3과 같이 역순으로 주어지는 경우만 추가적으로 고려하면 됩니다. [a, b] = [b, a]는 두 숫자를 바꾸는 편리한 문법이므로 알아두시면 좋습니다. Array부터 시작해서 fill, map까지 이어지는 과정도 [1,2,3,4,5]이런 순차적인 숫자 배열을 만드는 방식이므로 알아두시면 좋습니다.

이렇게 풀고 짧게 끝냈다며 좋아하고 있었는데, 다른 사람의 풀이를 보는 순간 벙 쪘습니다. 가우스 방식(가우스가 1부터 100까지를 순식간에 더했던 일화)을 사용하신 분들이 있었던 거죠. ㅋㅋ 저는 너무 창의력이 떨어지는 것 같습니다.

function solution(a, b) {
  return (a + b) * ((a > b ? a - b : b - a) + 1) / 2;
}

양쪽 두 수를 더한 것에, 두 수 사이의 숫자 수 + 1을 곱하고 나누기 2를 하는 게 가우스 방식입니다. a와 b 대소관계 때문에 좀 지저분하네요.

문자열 내 마음대로 정렬하기 

['abce', 'abcd', 'cdx']와 2가 주어지면 2번째 인덱스 글자(c, c, x)를 기준으로 정렬합니다. 만약 2번째 인덱스 글자가 서로 같다면(c, c처럼) 사전순으로 정렬합니다. abce와 abcd를 사전순으로 정렬하는 것이죠.

따라서 같을 때와 다를 때 정렬 방법이 달라집니다. 정렬이기 때문에 당연히 sort 메서드가 들어가고요. sort 시 -1이면 순서가 유지되고, 1이면 순서가 서로 바뀐다는 것 이용하면 됩니다.

function solution(strings, n) {
  return strings.sort((p, c) => p[n] === c[n] ? p.localeCompare(c) : p[n].localeCompare(c[n]))
}

여기서 꿀팁! localeCompare로 사전순으로 정렬할 수 있습니다! sort 메서드 안에서 return a.localeCompare(b)하면 됩니다. localeCompare 없이도 사전순으로 정렬하는 것을 직접 구현하셔도 되겠죠. 키워드도 하나 알려드립니다. lexicographic order가 사전순 정렬입니다.

문자열 내 p와 y의 개수 

문자열 내의 p와 y의 개수가 같으면(대소문자 구분 없음) true 아니면 false를 리턴하면 됩니다. Ppayay는 true입니다.

해답은 간단하죠. 개수를 세서 하면 됩니다.

function solution(s) {
  const p = s.split('').filter(v => ['p', 'P'].includes(v));
  const y = s.split('').filter(v => ['y', 'Y'].includes(v));
  return p.length === y.length;
}

문자열을 배열로 만들어서 각 단어가 p나 P인지, 또는 y나 Y인지를 찾아서 필터링 후, 개수를 비교합니다.

이 문제도 제 함수형에 대한 집착(고정관념)을 반성하게 해준 문제입니다. 정규표현식보다 함수형 메서드를 우선 시하여 생각하는 문제가 있는 거죠. 정규표현식으로 하면 엄청 간단한 문제였습니다.

function solution(s) {
  return s.match(/p/gi).length === s.match(/y/gi).length;
}

정규표현식에 걸리는 것들의 개수를 세면 됐습니다. g는 모두 찾는 거고, i는 대소문자 구분 안 한다는 뜻입니다. 그런데 이게 에러가 납니다. (p와 y가 없는 경우 문제가 됩니다)

function solution(s){
  return s.replace(/p/gi, '').length == s.replace(/y/gi, '').length;
}

이렇게 replace에도 정규식을 써서 풀 수 있습니다. 위에 match는 왜 안 되는지 잘 모르겠네요.

문자열 내림차순으로 배치하기 

문자열을 역순으로 정렬합니다. 대문자는 소문자보다 뒤에 위치해야 합니다. 예를 들어 Zbcdefg는 gfedcbZ가 됩니다.

당연히 정렬(sort)을 하게 되고요. localeCompare는 여기서 못 씁니다. 대문자가 소문자보다 뒤에 위치해야 해서요.

function solution(s) {
  return s.split('').sort((prev, cur) => cur.charCodeAt() - prev.charCodeAt()).join('');
}

제가 좋아하는 split, sort(또는 map) join(또는 reduce)으로 이어지는 메서드입니다. split-apply-combine 기법이라고도 불리는 3단 콤보입니다. 문자열을 split으로 배열로 바꿔서 원하는 처리를 하고 다시 join으로 문자열로 되돌립니다. charCodeAt은 문자의 숫자코드를 알려주는 메서드입니다. 이걸 사용해서 정렬하면 됩니다. 대소문자 정렬에서 localeCompare과 다른 결과를 보여줍니다.

문자열 다루기 기본

문자열의 길이가 4 또는 6이고 숫자로만 구성되어 있는지 확인합니다.

숫자로만 되어있는지와(AND) 길이가 4 또는(OR) 6인지를 확인하면 되겠네요.

function solution(s) {
  return /^[0-9]+$/.test(s) && [4,6].includes(s.length);
}

프로그래머스에서 \d 정규표현식을 지원하지 않아 저렇게 숫자로 했습니다. OR 조건에서 (s.length === 4 || s.length === 6) 대신 includes로 짧게 줄일 수 있다는 것 알아두세요.

서울에서 김서방 찾기 

속담으로 이름을 지은 문제인 것 같네요. ['Jane', 'Kim']이란 배열이 있으면 Kim의 위치를 찾으면 됩니다.

간단하게 indexOf로 끝내버립시다.

function solution(seoul) {
  return '김서방은 ' + seoul.indexOf('Kim') + '에 있다';
}

Level 1 문제는 30개가 있더군요. 앞으로 두 편 더 Level 1 문제 풀이를 보여드리겠습니다. 여러분의 답과 비교해보시고 얼마나 더 짧게 줄일 수 있는지 테스트해보세요 ㅎㅎ. 사실 너무 쉬워서 블로그 포스팅하는 시간이 몇 배는 더 걸렸네요 ㅠㅠ. Level 2부터 좀 재밌어집니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

7개의 댓글이 있습니다.
6달 전
이런 백준이나 프로그래머스 문제들 잘풀려면 자료구조 공부하고 해야 수월하게풀수있나요?
6달 전
레벨1은 자료구조도 필요 없습니다. 2부터는 공부가 필요합니다
3년 전
문자열 내 p와 y의 개수에서 match는 값을 찾지 못하면 null값이 반환되어 null.length로 되어 에러 생기는것 같습니당
3년 전
본문에 다 써놓은 내용입니다.
4년 전
두 정수 사이의 합
return Array(b - a + 1).fill().map((v, i) => a + i).reduce((a, c) => a + c);

이 부분에서 map((v,i) => a + i) 헷갈리는 부분입니다. 순서대로하면 v: 요소, i:인덱스인데
갑자기 왜 a+i가 되는지 모르겠어요. reduce도 그렇고 c와 v의 역할을 좀더 자세히 알려주실 수 있을까요?

a > b && ([a, b] = [b, a]);
논리연산자를 쓸 때 조건문 없어도 돌아가는 이유가 궁금합니다.
3년 전
a > b && ([a, b] = [b, a]);
return Array(b-a+1).fill(a).map((v, i) => v + i) 오타이신것 같아요
6년 전
'문자열 내 p와 y의 갯수' 문제에서 정규표현식 사용한 문법은 예외처리가 안되어 있어서 그런지 에러가 발생하는데요. 에러처리 좀 도와주실 수 있나요? p도 y도 없는 경우에 에러가 발생하는 것 같아서요.
6년 전
질문드리고 나서
function solution(s) {
let answer = true;
const reg = '/p|y/gi';
if(s.match(reg)!== null){
return answer = s.match(/p/gi).length === s.match(/y/gi).length;
} else {
answer = false;
};
console.log(answer);
return answer;
}
이렇게 해봤는데 혹시 try 구문을 사용해서 하는 방법도 있을까요?
6년 전
try는 쓰지마세요. try는 비동기 함수를 제외하면 쓰지 않는 게 좋습니다.
6년 전
고맙습니다.
6년 전
저 죄송한데 한개만 더 질문드릴게요. 괜찮으시면 답변 부탁드립니다 ㅠㅠ..

function solution(s) {
let answer = '';

if(s.match(/p|y/gi)!== null){
answer = (s.match(/p/gi)).length === (s.match(/y/gi)).length;
} else {
answer = false;
};

return(answer);
}

이렇게 작성해서 예외처리 했다고 생각했는데, 여전히 테스트2, 3에서 걸리네요.
추가적으로 예외상황이 안떠올라서요ㅠ_ㅠ.. 혹시 예상 가능한 예외상황 있으신가요..?
6년 전
테스트 2, 3번이 좀 이상하네요. 이건 뭔가 프로그래머스에 오류가 있는 것 같습니다. 참고로 else문에서는 answer = true를 해야합니다. 모두 하나도 없는 경우는 true를 리턴해야 하거든요.
6년 전
function solution(s){
return s.replace(/p/gi, '').length == s.replace(/y/gi, '').length;
}
이렇게 하니 되네요.
6년 전
감사합니다!!
6년 전
'나누어 떨어지는 숫자 배열' 문제에서 질문이 있는데요. filter 함수를 arr.filter(v => v % divisor === 0); 로 작성하는 것과 arr.filter((v) => {v % divisor === 0}); 로 작성하는 건 어떤 차이가 발생하는 건가요? 제가 혼자 문제 풀면서 후자로 작성했는데 테스트를 1개만 통과하더라구요.
6년 전
중괄호({ })로 감싸지 않으면 바로 return이 됩니다. 전자는 return v % divisor === 0;과 같습니다. 후자는 return undefined;라서 크나큰 차이가 발생합니다.
6년 전
아..!! 이해했습니다. 설명고맙습니다.
6년 전
자바스크립트 알고리즘 풀이가 필요했는데 고맙습니다!!ㅠㅠ LeetCode도 양질의 문제가 많아서 나중에 풀어주실 문제 고르실 때 참고해주세요ㅎ
6년 전
네 알겠습니다! 시간 날 때마다 틈틈이 풀어보겠습니다~
6년 전
코드파이트도 자바스크립트 지원해요~ codefights.com