개발 공부를 하면서 느낀 바로 자바 언어와 프레임워크를 주로 사용하는 제 자신을 보고 자바 프로그래밍
실력을 늘리고 싶은 마음에 이번 캐치카페 취업 스터디에서 주관하는
'자바 코딩테스트 스터디 27기'에 참여하게되었습니다.
2022년 8월 22일부터 시작되었고, 첫 주는 스터디에 대한 전반적인 정보공유와 방법을 알려주는 OT(오리엔테이션)이어서 그 다음주부터 스터디를 진행하는 것으로 하였습니다.
단톡방에 총 3명정도가 모여 자바 언어로 코딩테스트 문제를 풀어보는 스터디입니다.
먼저 금요일까지 각자 선정한 문제를 공유하고, 그 다음주 수요일까지 풀어본 후 깃허브에 올려 코드를 공유할 수 있도록 정했습니다.
수요일에는 구글 Meet 을 통해 정기적으로 모여 화상회의로 서로의 코드를 리뷰해보고 비교하며 피드백도 주고 받는 시간을 가지기로 하였습니다.
그렇게 진행된 "9월 1일 15:00분 부터 약 15:30분까지 진행된 자바 코테 스터디 1회차"를 잘 마무리할 수 있었습니다.
1회차에 저를 포함한 저희 팀원이 선정한 문제들은 이러합니다.
2. 백준 1092번 : 배
* 팀원들이 푼 전체코드는 포스트하지 않음을 알려드립니다.
1. 백준 5585번 : 거스름돈
<문제 제시>
<예시 입출력>
<문제 해결 과정>
이 문제는 1000엔이라는 고정 지불 금액이 정해져있고, '타로'라는 분이 지불할 금액을
데이터 입력값으로 입력해주어야합니다.
만약 380을 입력한다면, 1000엔을 낸 후 620엔을 받는 시스템인 것입니다.
데이터 출력값으로는 만약 380엔이면 이 620엔 잔돈의 최소갯수가 몇개인지 출력해주어야합니다.
데이터를 입력받고, 출력받기 위해
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader, BuffereWriter를 선언해주었습니다.
// 타로가 받을 잔돈
int taro = Integer.parseInt(br.readLine());
'타로'라는 분이 받을 잔돈을 readLine으로 입력받아 줍니다.
잔돈은 500엔, 100엔, 50엔, 10엔, 5엔, 1엔 처럼 정해져있으므로 전역변수로 정수형 배열을 선언해주었습니다.
전체지불금액에서 '타로'가 지불한 금액을 차감해주고, for-each문을 통해 잔돈 배열을 n_coin으로 넣었습니다.
그 n_coin을 인수로 num이라는 함수를 만들어 전달합니다.
num함수에서는 n_coin을 인수로, 연산을 하게됩니다.
// 동전 갯수 연산 메소드
public static void num(int n_coin) {
count += (price / n_coin);
price = price - (n_coin * (price / n_coin));
}
count라는 잔돈개수 변수에 지속해서 더하고, 받을 잔돈 금액(price)을 잔돈(n_coin)으로 나눕니다.
for-each문 로직을 다시 살펴보면,
coin 정수형 배열에 담긴 500, 100, 50, 10, 5, 1에서 하나씩 꺼내서 n_coin에 넣고 그 값을 인수로하여 함수를 연산하게됩니다.
그럼 만약 380엔이 입력되었다했을때의 연산 과정을 살펴보겠습니다.
price는 1000엔으로 고정되어있으므로 받을 잔돈은 620엔일 것입니다.
620엔을 coin으로 나누며 떨어지는 몫을 개수로 카운트해주면 될 것입니다.
1) 500엔
620엔을 500엔으로 나누어도 price와 n_coin모두 정수형으로 선언되어있기때문에, 소수점없이 ' 1 ' 이라는 값이 몫으로 나오게됩니다. 그것을 count로 누적시켜주고,
밑의 price연산에서는 500 * (620 / 500) 을 해주어 500엔이 사용된 개수로
잔돈 금액에서 차감 (price = price - (500 * (620 / 500)) 하게 됩니다.
그렇게 500엔이 1개 사용되었으며, 620엔에서 500x1를 차감한 120엔이 남게되며, 다음 잔돈연산으로 넘어갑니다.
2) 100엔
남은 120엔을 연산하면 count += (120 / 100); 이므로 1이 더 추가되어 count에는 총 2가 누적됩니다.
잔돈 금액에서 차감하게되면 20엔이 남게되며, 다음 잔돈연산으로 넘어갑니다.
3) 50엔
여기서는 50엔보다 적은 금액이 남아있기때문에 0이 count되어 누적값 2는 그대로 유지됩니다.
4) 10엔
20엔을 연산하여 2가 더 추가되므로 count는 총 4가 누적됩니다.
남은 돈이 없으므로 1엔은 연산하지 않고 count로 4가 결과값으로 출력됩니다.
<핵심코드>
price = 1000; // 고정된 지불금액
price = price - taro;
for(int n_coin:coin) {
num(n_coin);
}
// 동전 갯수 연산 메소드
public static void num(int n_coin) {
count += (price / n_coin); // 잔돈 동전 갯수 누적 카운트
price = price - (n_coin * (price / n_coin)); // 잔돈 업데이트
}
2. 백준 1092번 : 배
<문제 제시>
<예시 입출력>
<문제 해결 과정>
이 문제는 주어지는 것이
- 크레인의 대 수
- 크레인의 무게 제한
- 박스의 개수
- 박스의 무게
총 4개가 주어집니다.
최종적으로 값이 담길 공간은 어레이 리스트로 크레인, 박스를 선언해봤습니다.
여기서도 입력과 출력을 위해
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader, BuffereWriter를 선언해주었습니다.
그리고 데이터가 담겨 무게를 비교해가며 적재할 배열을 선언하였습니다.
// 크레인, 박스 배열 선언
ArrayList<Integer> crane = new ArrayList<>();
ArrayList<Integer> box = new ArrayList<>();
배열 리스트로 만들었는데, 이는 나중에 인덱스 값으로 비교해가며 .get() 메서드와 .remove()메서드를 활용하기 위해서
선언해주었습니다.
크레인의 수와 크레인의 각 무게는 readLine(), split(" ")이 두가지를 적절히 활용해서 입력받고,
크레인의 각 무게는 여러개가 지정되므로 String[] 배열로 만들어주었습니다.
for-each문을 통해 이 String[]배열에 담긴 데이터를 배열 리스트인 crane으로 정수형 변환 후 옮겨줍니다.
옮겨줄때는 .add() 와 Integer.parseInt()를 사용하였습니다.
박스의 수와 박스의 각 무게 또한 위와 같은 로직으로 처리해줍니다.
이제 핵심인 정렬을 해야하는데, 정렬을 해야하는 이유는 크레인의 순서가 정해져있지않은 상태에서 박스의 무게가 순서대로 실리다보면 여러 대의 크레인을 거쳐 담길 수 있으므로 이 문제의 목표인 '최소 시간'을 맞추기 위해
내림차순 정렬을 해줍니다. 내림차순 정렬을 해줄때는
// ^^^ 정렬 알고리즘 ^^^
Collections.sort(crane, Collections.reverseOrder());
Collections.sort(box, Collections.reverseOrder());
처럼 선언해주며 import java.util.Collections; 를 필요로합니다.
sort메소드는 첫번째 인자로 정렬할 대상을 선정하고, 두번째 인자로 정렬 방법을 선정합니다.
여기서 지정된 정렬 방법인 reverseOrder가 내림차순 정렬(역정렬)을 의미하는 것입니다.
이제 실질적인 연산을 하는 부분을 만들어야합니다.
조건문을 하나 만들고, 만약 크레인의 무게가 박스의 무게보다 부족하다면 -1을 출력하며 다시 되돌아가게합니다.
(각 크레인은 무게 제한이 있다. 이 무게 제한보다 무거운 박스는 크레인으로 움직일 수 없다.) 을 참고한 것입니다.
while문을 통해 box 리스트가 비어있지 않는한 계속 반복하는 연산을 만듭니다.for문을 통해 배열 리스트의 길이만큼 접근하도록 합니다. (crane.size();)
int num = 0; // 박스 순서에 접근하기위한 변수
for(int i = 0; i < crane.size();) { // 리스트의 길이만큼
if(num == box.size()) break;
if(crane.get(i) >= box.get(num)) { // i번째 크레인이 num 박스보다 크거나 같으면
box.remove(num); // 크레인에 담긴것으로 간주하고 제거
i++;
}
else num++;
}
만약 box 배열 리스트의 길이가 0이라면 즉시 빠져나갑니다.
만약 i번째 크레인이 0번째 박스보다 크다면 0번째 인덱스에 있는 box 배열리스트 데이터를 삭제하고, i를 증가시킵니다.
else문에서는 num을 증가시킵니다.이렇게 한번 로직을 도는 것이 1분 증가이므로 minute을 증가시킵니다.
이후
bw.write(String.valueOf(minute));
연산이 다 된 minute을 결과값으로 출력하면 됩니다.
<핵심코드>
// 크레인, 박스 배열 선언
ArrayList<Integer> crane = new ArrayList<>();
ArrayList<Integer> box = new ArrayList<>();
// 크레인의 수
int n = Integer.parseInt(br.readLine());
// 크레인의 각 무게
String[] cranes = br.readLine().split(" ");
for(String c : cranes) {
crane.add(Integer.parseInt(c)); // 배열에 정수 변환 후 추가
}
// ***박스 입력도 동일*** //
// ^^^ 정렬 알고리즘 ^^^
Collections.sort(crane, Collections.reverseOrder());
Collections.sort(box, Collections.reverseOrder());
if(crane.get(0) < box.get(0)) { // 크레인 무게가 박스 무게보다 부족하다면
bw.write(String.valueOf(-1));
bw.flush();
bw.close();
return ;
}
while(!box.isEmpty()) { // 박스 리스트에 데이터가 있는 동안 반복
int num = 0; // 박스 순서에 접근하기위한 변수
for(int i = 0; i < crane.size();) { // 리스트의 길이만큼
if(num == box.size()) break;
if(crane.get(i) >= box.get(num)) { // i번째 크레인이 num 박스보다 크거나 같으면
box.remove(num); // 크레인에 담긴것으로 간주하고 제거
i++;
}
else num++;
}
minute++;
}
<팀원의 다른 코드>
1.
저는 ArrayList를 선언할때
ArrayList<Integer> crane = new ArrayList<>();
로만 선언이 되는 줄 알았으나,
List<Integer> crane = new ArrayList<>();
간단히 이렇게도 선언이 가능한 것을 알게되었습니다.
2.
저는 입력받을 시 공백구분을 .split(" ")메소드를 활용한 반면
// 크레인의 수
int n = Integer.parseInt(br.readLine());
// 크레인의 각 무게
String[] cranes = br.readLine().split(" ");
for(String c : cranes) {
crane.add(Integer.parseInt(c)); // 배열에 정수 변환 후 추가
}
팀원분은 split 메소드 대신 StringTokenizer을 활용하였습니다.
for-each문대신 for문으로 처리하며 st.nextToken() 으로 받는 것을 확인할 수 있었습니다.
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i=0; i<N; i++) {
crane.add(Integer.valueOf(st.nextToken()));
}
StringTokenizer를 사용하는 방법을 좀 더 자세히 공부해봐야겠다고 생각했습니다.
3. 프로그래머스 2019 카카오 코딩테스트 : 크레인 인형뽑기
<문제 제시>
추가설명
<예시 입출력>
<문제 해결 과정>
이 문제는 얼핏 문제설명그림만 보면 막막할 수 있는 문제이지만 사실 Stack이라는 구조를 사용하면 쉽게 구현할 수 있었습니다.
먼저 프로그래머스와 백준의 데이터 입출력 방식이 다른 것을 감안하여
연산이 되는 메소드만 구현하면 되는 것 같습니다.
int answer = 0;
을 이용해 터뜨려 사라진 인형의 개수를 표현합니다. 나중에 이 값이 결과값이 됩니다.
Stack은 pop(), push(), peek() 등의 메소드를 활용해 데이터를 다룹니다.
먼저 push()는 스택에 데이터를 쌓는 기능입니다. pop()은 스택에서 데이터를 빼는 기능이고, peek()는 쌓인 스택의 최상단 값을 가져오는 기능입니다.
Stack<Integer> st = new Stack<>(); // 바구니 선언
주어진 solution에서는 현재 인형뽑기 크레인의 정보가 board 2차원 배열에 담기고,
인형의 이동을 moves 1차원 배열에 담기게 됩니다.
중첩 반복문을 통해 2차원 배열에 접근하고자 하였습니다.
외부 반복문에서는 인형이 이동한 moves.length 만큼 반복하며
내부 반복문에서는 인형이 담겨있는 board.length 만큼 반복하게 됩니다.
중첩 반복문 안에는 조건문을 통해 연산을 하게 되는데, board[j][moves[i] - 1] 을 통해 인형이 있는지 없는지 판별합니다.
인형이 있으면 (=0이 아니라면)
Stack(바구니)의 가장 최상단 요소를 st.peek();으로 뽑아내어 board[j][moves[i] - 1] 요소와 비교하게 됩니다.
1) 같으면 인형이 터지기 때문에 Stack(바구니)에서 pop() (=제거) 하고 answer에 2를 더하게 됩니다.
2) 다르면 인형은 그대로 쌓이는 것이므로 Stack(바구니)에 board[j][moves[i] - 1을 push() (=삽입) 합니다.
결과값은 answer로 리턴하게됩니다.
<핵심코드>
int answer = 0; // 터뜨려 사라진 인형의 개수
Stack<Integer> st = new Stack<>(); // 바구니 선언
st.push(0); // 0 쌓기, stack의 최상단 값과 비교하기 위해서 아무것도 없는 오류를 방지하기 위함
if(board[j][moves[i] - 1] != 0) { // board의 가장 상단의 수가 0이 아닐때
if(board[j][moves[i] - 1] == st.peek()) { // board의 가장 상단의 수가 스택의 '가장 상단의 수(peek)'와 같다면
answer+=2;
st.pop(); // 그 수 제거
}
// 그렇지 않은 경우 board 상단의 수를 stack에 쌓기
else st.push(board[j][moves[i] - 1]);
board[j][moves[i] - 1] = 0; // stack에 쌓았으므로 그 수는 0으로 초기화
break;
}
이번 캐치스터디를 진행하며 팀원 간 동기부여가 확실히 되는 것을 알게되었고,
정해진 과제가 있다보니 알고리즘 공부를 꾸준히 하게되는 것을 느꼈습니다.
팀원과 코드리뷰를 해보며 제가 작성한 코드와 유사하면 따로 문항을 나눠서 포스트하지는 않았습니다.
코드리뷰를 하는 것 자체만으로 값진 시간이었으며, 좋은 경험이었습니다.
'Recording > 온라인 세미나' 카테고리의 다른 글
[캐치 멘토링] "IT기업 개발자가 알려주는 인턴맞춤 코딩테스트" 후기 (0) | 2023.10.14 |
---|---|
[멘토링] 코딩테스트 합격 전략 계획하기 (0) | 2023.09.08 |
제 3회 스파르톤 - 코딩 마라톤 생존일지 (+회고) (0) | 2022.09.17 |
[캐치 멘토링] "처음 만드는 신입 블록체인 개발자의 포트폴리오" 후기 (0) | 2022.01.25 |