본문 바로가기
회고/코딩테스트 회고

소프트웨어 마에스트로 15기 - 2차 코딩테스트 회고

by NewCodes 2024. 3. 3.

 

소프트웨어 마에스트로 15기 - 2차 코딩테스트 회고

 

 

안녕하세요. NewCodes입니다!

 

1차 합격 문자!

 

 

1차 코딩테스트가 끝난 후, 1주일 뒤 바로 2차 코딩테스트를 봤습니다. 

 

 

1차 시험에 임박했을 때는 별생각 없이 열심히 했었는데, 2차는 체력적, 정신적으로 지쳤었습니다. 코딩 테스트를 2번이나 보는 건 생각보다 힘드네요...

 

 

솔직히 이번에 준비하면서 스트레스를 많이 받았습니다. '1차 코테 ~ 2차 코테' 이 사이의 기간은 참.. 힘든 시간이었습니다. 지쳐 있는 상황에서도 끝까지 준비해야 하니.. 알게 모르게 제 자신이 스트레스를 많이 받은 것 같았습니다. 평소에 안 나던 피부 트러블도 좀 나고요 ㅠㅠ 

 

 

그래도 1주일을 허투루 보내지는 않았습니다. 1주일 동안 새로운 문제들도 풀고, 복습도 하면서 그물을 더 촘촘히 짜고자 했습니다. 

 

 

이번에도 본격적으로 2차 코테를 회고해보겠습니다.

 

<참고 사항>

  • 선택 언어: 자바
  • ✅: 푼 문제
  • 🔺: 풀었지만, 틀릴 가능성이 높은 문제
  • ❌: 못 푼 문제

 


✅ SQL

제목 계좌 거래 내역 집계하기
유형 테이블을 능동적으로 만들기
난이도 LV5
풀이 시간 25분

 

유형은 딱 하나로 분류하긴 힘들 것 같습니다. 그나마 분류하자면 개인적으로 JOIN인 것 같습니다. 

 

 

시험을 보고 생각이 든 건, '와 리트코드 SQL 풀길 정말 잘했다..!!' 이전 기수 후기들을 보면 SQL은 '프로그래머스 고득점 kit'만 봐도 충분하다고 했는데요. 이번 2차 코테에 한해서는 아닌 것 같습니다.

 

 

저는 SQL 고득점 kit를 2회독을 해두니 sql 할 게 없더라고요. 프로그래머스에서 정답률이 가장 낮은 문제를 풀어도 나름 잘 풀렸습니다. 그래서 남은 기간 동안 리트코드에서 새로운 문제를 풀어보기로 했습니다. 

 

 

한 후기에서는 HackerRank를 푸신 분도 있던데, 저는 둘 다 해보니 리트코드 문제가 훨씬 괜찮았습니다. 퀄리티, 사용자 수, 기능적인 측면 모두 리트코드가 더 낫다는 체감이 들었습니다. 

 

 

그리고, 이번에 제가 SQL 문제를 풀게 해 준 3가지 문제들을 가져왔습니다. 이 문제들 덕분에 해당 SQL을 풀 수 있었다고 해도 과언이 아닙니다. 

 

 

1. 변수를 생성하여 열을 추가할 수 있구나!

https://school.programmers.co.kr/learn/courses/30/lessons/59413

 

SET @HOUR = -1;

SELECT (@HOUR := @HOUR+1) AS HOUR, (SELECT COUNT(*)
                                   FROM ANIMAL_OUTS
                                   WHERE HOUR(DATETIME) = @HOUR) AS COUNT
FROM ANIMAL_OUTS
WHERE @HOUR < 23

 

: SET 사용자 변수의 의미와 사용법을 알게 해준 문제입니다. 사용자 변수를 통해 기존 테이블에 없던 데이터를 추가할 수 있다는 걸 알게 되었습니다. 

 

물론 각 행에 새로운 데이터를 추가하는 건 SET 말고도 몇 가지 방법이 더 있는 걸로 압니다.

 

mysql에는 결과에 순번, 순위를 매기는 함수인 비집계함수라는 것이 있습니다. 여기에는 RANK, NTILE, DENSE_RANK, ROW_NUMBER 등이 포함됩니다. 여기서 ROW_NUMBER를 쓰게 되면 각 행에 순차적으로 번호를 매길 수 있습니다. 

 

이번 소마 2차 SQL 문제에서는 직접적으로 새로운 데이터를 추가해서 리턴하라는 식의 문제는 아니었습니다. 하지만, 각 행의 index 즉 고유한 순번을 붙여둬야 원활하게 풀 수 있는 문제였습니다. 

 

 

 

 

2. 서브쿼리가 이렇게 유용한 거였어?

https://leetcode.com/problems/department-top-three-salaries/description/

 

SELECT D.NAME AS Department, E.NAME AS Employee, E.SALARY as Salary
FROM EMPLOYEE E
JOIN DEPARTMENT D ON E.DEPARTMENTID = D.ID
WHERE 3 > (SELECT COUNT(DISTINCT SALARY) 
	FROM EMPLOYEE E2
	WHERE E2.SALARY > E.SALARY and e2.departmentid = e.departmentid)

 

: '서브쿼리를 이렇게 쓸 수도 있구나...' 하면서 감탄했던 문제입니다. 동시에 SQL의 동작 원리를 알게 해준 고마운 문제입니다. 

 

저는 SQL 공부를 할 때 개념부터 차근차근 살펴본 게 아닙니다. 다짜고짜 문제 풀면서 모르는 문법 나타나면 바로 검색해 보는 식으로 공부했습니다. 그렇다 보니 유틸성의 사용 방법에 대한 지식만 늘어났고, '원리'에 대한 이해는 거의 없다시피 했습니다.

 

그런데 이 문제를 통해 뒤늦게나마 SQL의 동작 원리에 대해 공부해 보게 되었습니다. 우선 SQL의 동작 순서는 'FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY'라는 걸 알게 되었습니다. 직감적으로 알곤 있었지만, 이번 기회로 더욱 명시화할 수 있었습니다. 

 

또한, SQL에서 쿼리를 실행시킬 때는 각 행의 데이터가 WHERE 조건에 부합하는지 각각 확인합니다. 간단히 말하자면, 각 행 단위로 쿼리가 실행됩니다. 그렇기에 위 코드 블록과 같이 쿼리를 작성할 수 있습니다. 마치 이중 반복문처럼 작동되는 걸 확인해 볼 수 있습니다. 

 

위 코드를 더욱 자세히 살펴봅시다.

 

외부 쿼리에서 하나의 행(E.SALARY)이 WHERE 조건에 맞는지 각 행마다 순차적으로 검사합니다. 이때 서브 쿼리 안에서는 E2 테이블이 가진 E2.SALARY 행이 모두 나오면서 E.SALARY와 비교를 하게 되죠. 그러고 나서 해당 조건과 만족하는 E2.SALARY의 개수가 3개 미만일 때만 외부쿼리에서 E.SALARY를 가져오게 됩니다. 

 

이번 소마 코테 문제에서는 이러한 개념을 저는 어떻게 적용시켰냐면요. 앞서 SET 사용자 변수로 설정해 둔 index를 기반으로 현재 행의 index보다 1만큼 더 큰 index+1의 인덱스를 가진 행을 가져오게 했습니다. 그래서 index 행의 데이터와 index+1 행의 데이터를 하나의 행에 보여줄 수 있도록 쿼리 했습니다. 

 

 

 

 

3. SUM을 이렇게도 할 수 있구나!

https://leetcode.com/problems/trips-and-users/

 

 

WITH NOT_BANNED AS (SELECT *
FROM TRIPS 
WHERE CLIENT_ID NOT IN (SELECT USERS_ID
                        FROM USERS
                        WHERE BANNED = 'Yes')
AND DRIVER_ID NOT IN (SELECT USERS_ID
                        FROM USERS
                        WHERE BANNED = 'Yes')
AND REQUEST_AT BETWEEN '2013-10-01' AND '2013-10-03')

SELECT REQUEST_AT AS Day
    round(sum(if(status like 'cancelled%', 1, 0)) 
    / COUNT(REQUEST_AT), 2) AS 'Cancellation Rate'
FROM NOT_BANNED
GROUP BY REQUEST_AT

 

: SUM 집계함수 내부if, case 등의 구문을 포함시켜 SUM을 조금 더 역동적으로 할 수 있다는 걸 깨달았습니다. 주어진 테이블에 있는 데이터만을 딱딱하게 집계할 수 있는 게 아니라 때로는 제가 원하는 만큼만, 혹은 테이블에 주어지지 않은 데이터로 더할 수 있다는 게 매력적이었습니다. 

 

이번 소마 SQL 문제에서는 이렇게 자신이 원하는 행만을 선택해서 SUM할 줄 아는 게 중요했습니다. 

 

 

SQL 최종 피드백

위 세 가지 개념을 이용하여 푼 풀이는 지극히 제 개인적인 풀이일 뿐입니다. 이외에도 row_num, sum over, LAG, LEAD 등의 함수를 이용하여 문제를 해결할 수 있습니다. 다만 제 풀이는 이러한 윈도우 함수를 몰라도 사용자 변수와 기본 구문으로도 뚫을 수 있는 풀이입니다. 

 

무엇이든 원리를 이해하는 건 정말 중요합니다. 소마 코테를 준비하면서 약 1주일 전에 SQL 개념과 원리에 대한 이해가 부족하다는 걸 스스로 느꼈고, 이를 나름 가성비 있게 잘 보완한 것 같았습니다. 

 

이전 기수의 여러 후기에서 SQL은 고득점 kit만 풀어도 된다고 했습니다. 하지만, 여기에 안주하지 않고 새로운 문제를 찾아 풀며 대비한 건 정말 잘한 선택인 것 같습니다. 물론 처음부터 리트코드를 풀려고 하진 않았습니다만, SQL은 공부할 양이 적어 복습도 다 하고 나니 할 게 없어서 자연스레 푼 거이긴 합니다. 

 

이번 SQL 문제로 보면, 다음 기수부터는 고득점 Kit뿐만 아니라 SQL의 기본 개념과 원리, 각종 윈도우 함수를 이해하고 리트코드도 풀어보며 대비하셔야 할 것 같습니다. 

 

저는 해당 문제를 실전에서 한 번에 풀지 못했었습니다. 알고리즘을 풀다가 다시 돌아와서 풀어냈습니다. SQL이 어렵게 나오면 못 풀 수도 있다는 걸 감안해 두고 대비해서 그런지, 실전에서 어려운 난이도에 대해 그렇게 당황하지는 않았던 것 같습니다. 

 

 

 


 1번

제목 워드 프로세서
유형 그리디(구현), 문자열
난이도 실버
풀이 시간 12분

 

1번은 구현 성격이 강한 그리디였습니다. 저는 한 클래스를 이용하여 해당 문제를 쉽게 풀 수 있었습니다. 바로 StringToknizer라는 클래스입니다. 백준을 자바로 푸는 분들은 아마 익히 알고 계시는 클래스일 거라고 생각합니다.

 

이는 주로 공백을 기준으로 주어지는 입력값을 받을 때 자주 활용합니다. 그러나 StringToknizer는 입력을 받아올 때만 유용한 게 아닙니다. 

 

 

StringToknizer는 하나의 문자열을 여러 개의 토큰으로 분리하는 클래스입니다.

 

 

StringTokenizer(Strign str, String delim, boolean retrunDElims)

 

 

위와 같이 파라미터는 세 개입니다.

 

str만 입력하면 스페이스( ), 탭(\t), 줄바꿈(\n), 캐리지 리턴(\r) 등의 기본 구분자를 기준으로 토큰화하고요.

 

delim에 따로 구분자를 전달해 줄 수 있습니다.

 

그리고 세 번째 불리언 파라미터에 true를 전달하면 분리할 때 사용된 구분자를 토큰에 포함시킬 수 있습니다. 이 파라미터를 이용하는 게 해당 문제에서 중요한 핵심이었습니다. 

 

사용 예시를 살펴보시죠.

 

String str = "100-200*300-500+20";

StringTokenizer st = new StringTokenizer(str, "+-*", true); 

while (st.hasMoreTokens()) {
    System.out.print(st.nextToken());        
}

// "100", "-", "200", "300", "-", "500", "+", "20"

 

 

위와 같이 "+-*"을 인자로 전달하면 "+", "-", "*" 각각을 구분자로 취급하여 토큰화합니다. 사실 위 예시는 아래 문제에서 가져온 겁니다. 그리고 저는 이번 코테 1번 문제에서 유용하게 활용했던 StringTokenizer를 프로그래머스에서의 한 문제를 통해서 배울 수 있었습니다. 

 

 

StringTokenizer와 유사한 메서드인 split과의 차이점을 간단히 정리해 보자면 아래와 같습니다. 

  StringTokenizer split
속한 클래스 java.util String
구분자 타입 String 정규표현식
리턴 타입 String String[]
개인적인 경험 (구분자) 분할 방식이 직관적이어서 구분자 넣기 편함. 정규표현식 알아야 해서 좀 무거움.
구분자 전달 길이가 1인 문자열만 구분자로 인식함. 예를 들어, "end" 자체 통째로 구분하고 싶다면 split을 사용해야 함.  길이가 긴 문자열을 구분자로 지정할 수 있음.

 

 

더욱 자세한 건 해당 블로그를 참고하시는 걸 추천합니다. 되게 잘 정리해 주셨더라고요!

 

 

물론 해당 문제를 split으로도 풀 수 있습니다. 그리고 대부분 split을 사용하신 것 같습니다. 그러나 split은 리턴값에 구분자를 포함시킬 수 있는 기능이 없습니다. 그에 반해 구분자를 리턴값에 포함시키는 기능은 StringTokenizer가 가진 강력한 장점입니다. 

 

 

해당 문제를 split으로 풀게 되면 없어진 구분자를 따로 추가해줘야 하는 메서드를 작성해야 합니다. 이와 더불어 약간의 예외 처리도 해줘야 합니다. 예외 처리를 하는 게 번거롭다면, 문자열을 순회하면서 하나하나 파싱하는 방법도 있습니다. 

 

 

그래서 요약하자면, 해당 문제는 자바의 StringTokenizer를 활용하시는 게 편안한 해법이 아닐까 생각합니다. 파싱만 잘해두면 나머지 구현은 어렵지 않은 문제였습니다. 테케도 충분히 주어진 편이어서, 구현 중에서 덕분에 잘못된 부분을 디버깅할 수도 있었습니다.

 

 

1번 문제에 관해서는 딱히 피드백할 게 없을 것 같습니다. 저는 평소에 문제를 풀 때마다 해당 문제에 관해 피드백을 한 뒤, 깃허브에 올려두고 주기적으로 복습합니다. 해당 습관이 1번 문제를 쉽게 풀 수 해줬던 가장 큰 요인이지 않을까 싶습니다. 

 

피드백 레포!

 

 

제 피드백한 과정이 궁금하시다면 아래 레포를 참고해주세요!

 

https://github.com/NewCodes7/algorithm-study

 

GitHub - NewCodes7/algorithm-study: 코딩 테스트 연습

코딩 테스트 연습. Contribute to NewCodes7/algorithm-study development by creating an account on GitHub.

github.com

 

 


🔺2번

제목 떨어지는 과일 담기
유형 DP
난이도 골드
풀이 시간 약 1시간

 

2번은 아마 0.5솔인 것 같습니다. (0.1솔일지도..?) 저는 'BFS + 약간의 성능 개선'으로 주어진 테케를 통과했습니다. 시간제한이 10초였는데, 최대 실행 시간이 9의 300 제곱인가 나오더라고요. 뭐.. 굳이 직접 계산을 하지 않아도 어마어마한 숫자라는 걸 알 수 있습니다. 

 

 

끝나고 보니 다들 DP 유형이라고들 하셨습니다. DP 연습을 많이 안 했어서 실전에서는 DP인지 눈치를 못 챘습니다 ㅠㅠ 도저히 BFS 말고는 다른 풀이로 풀만한 방법이 떠오르지 않았습니다. 어쩔 수 없이 시간 초과 걸릴 걸 알면서도 BFS로 꾸역꾸역 구현했습니다. 

 

 

BFS로 본격적으로 구현하기기 이전에 3번 문제를 읽어봤습니다. 17분 동안 문제를 읽고 조건에 따라 설계도 조금 해봤지만, 쉽게 풀기 힘들 것 같았습니다. 그래서 2번 문제를 우선 풀기로 했고, BFS로 시원하게 말아줬습니다. 

 

 

BFS로 우선 구현한 뒤, 조금씩 성능 개선을 해보려는 계획이었습니다. 약 30~40분 정도 쓰니 BFS로 테케 통과를 할 수 있었습니다. 그리고 성능 개선을 할 수 있는 방법을 탐색했습니다. 

 

 

성능 개선의 과정

(조금 딥한 이야기를 하겠습니다. BFS로 해당 문제를 푸신 분이 아니라면 이해하기 어려우실 수 있습니다.)

 

그리고 성능 개선 방법을 하나 찾았는데요. 바로 유망하지 않은 바구니 큐를 삭제하는 것이었습니다. 유망한지 아닌지 판단하는 기준은 다음과 같습니다. 지금껏 큐에서 나온 최대 과일 개수와 현재 큐에서 나온 과일 개수가 n-3 초과해서 차이가 날 때, 해당 노드를 삭제했습니다. 

 

 

예를 들어, n = 5일 때 상황을 가정해 보겠습니다. 큐에서 진행하면서 지금껏 가장 많이 담은 과일 개수가 5개이고, 현재 큐에서 담은 과일 개수가 1개라면 해당 노드를 삭제합니다. 이만큼 차이가 나면 향후에 어떻게 바구니를 움직이든 최대 과일 개수를 담을 수가 없습니다. 이는 직접 시행해 보면서 귀납적으로 얻은 결론입니다. 

 

 

결론은 if문 2개로 약간의 성능 개선을 했습니다. 그런데 제 경험상 if문 좀 끄적인다고 해서 극적인 성능 향상을 거뒀던 경험이 없어서, 시간제한에 통과할지는 잘 모르겠습니다. 아마 통과하지 못할 확률이 더 높을 것 같긴 합니다. 

 

 

해당 문제는 채점 케이스에 따라 얻을 수 있는 점수가 많이 달라질 것 같습니다. 기도해야겠죠... 혹시 저와 비슷하게 푸신 분이 있거나 해당 방법에 의문이 있다면, 댓글 부탁드립니다!!

 

 

2번 문제 피드백

해당 문제에서 얻을 수 있는 피드백은 그렇게 날카롭진 않습니다. 그저 공부 방향과 준비 기간의 문제인 것 같아요. 저는 최근 2달 동안 빈출 유형인 DFS, BFS, 백트랙킹, 구현 위주로 준비했고, 아쉽게도 DP는 포함 대상이 아니었습니다. 이번 15기 코테에서 빈출 유형이 직접적으로 출제되지 않아 조금 아쉽네요 ㅠㅠ

 

 

그런데도 지금 준비 기간으로 다시 돌아간다고 해도 이와 비슷한 전략으로 공부할 것 같습니다. 저는 알고리즘과 자료구조에 대해 무지했었고, 빈출 유형을 공략하는 게 최선이라 생각했기 때문입니다. 

 

 

향후에는 DP, 그리디, 분할 정복, 수학 등 다양한 유형을 꾸준히 공부해야 할 것 같습니다. 그리고 여유가 있으면 해당 문제를 DP로도 한 번 풀어봐야겠습니다. 

 

 


❌ 3번

제목 포화이진트리에서 모빌 회전하기
유형 분할정복
난이도 골드
풀이 시간 17분(풀이를 안 함 ㅎㅎ)

 

17분 동안 문제를 읽고 설계를 해보려 했지만, 현재 제 실력에는 버거운 문제였습니다. 위에서부터 모빌을 회전해야 할지 아래서부터 모빌을 회전해야 할지도 헷갈렸습니다. 이런 분할 정복 문제를 많이 안 풀어봐서 어떻게 풀어야 할지 감이 잘 안 왔습니다. 

 

 

이제는 평소에 다양한 유형을 풀이해 보며, 알고리즘 실력을 좀 더 늘려보겠습니다!

 

 


4번

제목 +×+×+×+×
유형 비트연산, DP
난이도 골드~플래
풀이 시간 0분

 

읽어보지도 못했습니다. 톡방에서는 해당 문제의 유형이 비트연산, DP로 수렴되는 듯합니다. 

 

 

소마 톡방피셜 유사문제: https://school.programmers.co.kr/learn/courses/30/lessons/131702

 

 


코테 준비 기간을 회상하며

코테 준비 기간: 2024.01.02 ~ 2024.03.02

 

생애 첫 코테를 준비하며, 슬픔과 기쁨, 빡침과 희망이 공존했던 시간이었습니다.

 

개인적인 일정으로 알고리즘 공부에만 집중할 순 없었습니다. 그래도 하루에 3~4시간 정도는 꾸준하게 투자했습니다. 

 

짧은 기간인 만큼, 여러 전략을 세워 임했습니다. 그중 가장 핵심 전략인 '빈출 유형 공략'은 제대로 먹히지 않았습니다.

 

그럼에도 빈출 유형을 공부하며, 알고리즘 문제 풀이 전략과 여러 내공, 구현 실력을 쌓을 수 있었습니다. 

 

 

2개월 동안 빡센 일정과 스트레스로 인해 솔직히 쉽지 않았습니다. 멘탈이 터져 침대와 함께 나태하게 시간을 보낼 때도 있었죠. 

 

그래도 저는 나름의 확신을 가지고 공부를 할 수 있었습니다. 그 이유는, 실패해도 성공할 거라는 걸 잘 알고 있었기 때문입니다. 

 

불합격한다고 한들, 준비하면서 쌓인 알고리즘, 자료구조, SQL, 코테 경험은 '소마 코테'가 아니더라도 꼭 필요한 역량이라 생각했기 때문입니다. 실패한 들 저에게 생긴 경험 자체는 곧 성공이라 믿었습니다. 

 

 

결과는 어떻게 나올지는 모르겠습니다. 히든 테케에 갈려 불합 할 수도 있고, 운이 좋게 합격할 수도 있겠죠. 그래도 제 자신을 믿고 이후 일정을 잘 소화하고자 합니다. 합격, 불합격과 상관없이 마무리해야 하는 개인 프로젝트가 있어 이를 성실히 마무리해보려 합니다. 

 

 

피드백 요약

  1. 스트레스 관리도 계획에 포함시키자. 지금보다 운동도 더 해야 할 듯.
  2. 문풀을 하면서 얻은 피드백과 학습 내용을 잘 정리하여 복습한 점 Good. -> 덕분에 1번 쉽게 clear
  3. SQL 능동적으로 더 학습한 점 Good. -> 남들이 정해준 기준만 보지말고, 내 필요성에 따라 학습 계획을 수립하자.
  4. 2번 문제는 DP로 다시 풀어보자.
  5. 이제는 알고리즘 유형을 편식하지 않고, 꾸준히 다양한 유형을 학습하자!

 

 

이상 2차 코딩테스트 회고 마무리하겠습니다. 

 

궁금하신 사항, 지적하실 만한 부분 등이 있다면 편하게 댓글 남겨주세요!! 바로 보겠습니다 ㅎㅎ 

 

 

1차 코테 회고가 궁금하시다면, 아래 글을 읽어보세요! 1차 코테 회고를 많은 분들이 좋아해 주시고 읽어주셔서 정말 감사하게 생각합니다.

 

소프트웨어 마에스트로 15기 - 1차 코딩테스트 회고

 

소프트웨어 마에스트로 15기 - 1차 코딩테스트 회고

소프트웨어 마에스트로 15기 - 1차 코딩테스트 회고 안녕하세요. NewCodes입니다!! 어제인 2월 24일 토요일에 소프트웨어 마에스트로 15기 코딩테스트 1차를 응시했습니다. 저는 평소에 알고리즘, 자

newcodes.tistory.com

 

 

 


2024.03.06 소식 추가!!

 

 

다행히 코테 2차도 합격했네요!! 면접 보는 상상만 했었는데 현실이 되네요 ㅠㅠㅠ 감격스럽습니다! 면접도 최선을 다해서 준비해보겠습니다...!!!