관리 메뉴

개발공부기록

Java - 하샤드 수, 두 정수 사이의 합, 서울에서 김서방 찾기, 나누어 떨어지는 숫자 배열 / SQL(MySQL) - DATETIME 에서 DATE로 형변환, 흉부외과 또는 일반외과 의사 목록 출력, 가격이 제일 비싼 식품 정보 출력, 조건에 맞는 회원수 구하기 본문

기타 개발 공부/온라인 코딩 테스트 회고

Java - 하샤드 수, 두 정수 사이의 합, 서울에서 김서방 찾기, 나누어 떨어지는 숫자 배열 / SQL(MySQL) - DATETIME 에서 DATE로 형변환, 흉부외과 또는 일반외과 의사 목록 출력, 가격이 제일 비싼 식품 정보 출력, 조건에 맞는 회원수 구하기

소소한나구리 2025. 3. 7. 16:27
728x90

Java

하샤드 수를 판별하는 메서드 만들기

문제

  • 프로그래머스 - https://school.programmers.co.kr/learn/courses/30/lessons/12947
  • 양의 정수 x가 하샤드 수이려면 x의 자릿수의 합으로 x가 나누어져야 함
  • 예를 들어 18의 자릿수 합은 1 + 8 = 9이고, x가 18이라면  18은 9로 나누어 떨어지므로 18은 하샤드 수
  • 자연수 x를 입력받아 x가 하샤드 수인지 아닌지 검증하는 함수를 완성

제한 조건

  • x는 1이상, 10000 이하인 정수

출력 예시

나의 풀이

package test;

public class HarshadNumber {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println("10 = " + solution.solution(10));
        System.out.println("11 = " + solution.solution(11));
        System.out.println("12 = " + solution.solution(12));
        System.out.println("13 = " + solution.solution(13));
        System.out.println("100 = " + solution.solution(100));
        System.out.println("101 = " + solution.solution(101));

    }

    static class Solution {
        public boolean solution(int x) {

            String str = "" + x;
            int[] array = str.chars().map(num -> Character.getNumericValue(num)).toArray();
            int sum = 0;
            for (int num : array) {
                sum += num;
            }
            return x % sum == 0;
        }
    }
}
/* 
10 = true
11 = false
12 = true
13 = false
100 = true
101 = false
*/
  • 먼저 x를 각 자릿수로 나누기 위해서는 문자열로 변경하여 숫자의 자릿수를 잘라서 배열에 담아야겠다고 생각하고 접근했다.
  • 그래서 우선 int x를 빈 문자열로 덧셈 연산하여 문자열을 만든 후 str.chars()로 문자열의 각 요소의 ASCII 코드 값을 스트림 형태로 뽑아서 .map(Character.getNumericValue(num)을 통해 실제 숫자로 반환하도록 했다.
    • 이렇게 하지 않으면 각 문자의 ASCII 값이 int로 반환된다
  • 이후에 toArray()를 통해 int[] 배열로 반환하면 x로 입력받는 숫자의 자릿수를 배열의 각 요소로 사용할 수 있게 됨
  • 향상된 반복문을 통해 배열의 각 요소를 더한 값과 x의 값을 나머지 연산하고 그 결과가 0이라면 나누어 떨어진다는 뜻이기 때문에 return 문을 x % sum == 0;으로 작성하였음
    • 메서드의 반환타입이 boolean인 경우 return 문에 true로 반환할 조건을 작성해 주면 조건이 맞으면 true, 조건이 맞지 않으면 false가 반환되어 불필요하게 if - else 문을 작성하지 않아도 됨
  • 실행 결과를 보면 인자로 전달된 x의 값들이 하샤드 수인 경우에만 true로 반환된다

다른 풀이

class Solution {
    public boolean solution(int x) {
        int sum = String.valueOf(x).chars().map(ch -> ch - '0').sum();
        return x % sum == 0;
    }
}
  • 입력받은 수를 String으로 변환하고 .chars()을 통해 스트림을 통해 아스키 코드 값을 활용하는 것은 동일하다
  • 그러나 map을 통해 변환하는 로직이 다른데 스트림의 요소로 반환된 값을 '0' 아스키 코드 값으로 빼서 숫자로 반환하는 로직도 직관적이라는 것을 깨달았다
    • 문자 '0'의 아스키 코드 값은 10진수로 48이고, '1'은 49이다.
    • 즉, '1' 아스키 코드 값인 49에 '0'을 뺄셈처리하면(49 - 48 = 1)이 되어 1이라는 숫자(정수타입)를 반환하게 된다
    • 자바에서는 자동 형변환을 통해 문자 타입과 정수 타입이 가능하며, 이럴 경우 문자의 아스키 코드값으로 연산을 적용한다
  • 그리고 굳이 배열로 반환하지 않고 스트림에서 제공하는 sum()함수를 사용하면 훨씬 간결하게 코드를 작성할 수 있다는 것을 깨달았다.
  • 아직 스트림 문법이 익숙하지 않아서 조금 더 스트림을 다뤄봐야 할 것 같다

두 정수 사이의 합

문제

제한 조건

  • a와 b가 같은 경우는 둘 중 아무 수나 리턴
  • a와 b는 -10,000,000 ~ 10,000,000 정수
  • a와 b의 대소관계는 정해져 있지 않음

입출력 예시

나의 풀이

class Solution {
    public long solution(int a, int b) {
        
        long x = 0;
        long y = 0;
        long answer = 0;
            
        if (a < b) {
            x = a;
            y = b;
        } else {
            x = b;
            y = a;
        }        
        
        while (x <= y) {
            answer += x;
            x++;
        }
        
        return answer;
    }
}
  • 먼저 입력받은 두 수의 사이의 값을 모두 더해야 하기 때문에 반복문을 통해서 a부터  b까지 a를 1씩 증가하는 반복문을 통해서 문제를 해결해보려고 하였다
  • 그러나 a가 작고 b가 클때는 문제가 없었는데, b가 작고 a가 클 경우에는 단순히 반복문으로만 해결하기에는 문제가 있어서 별도로 x, y 변수를 선언하여 조건문을 통해 작은 값을 x에 큰 값을 y에 대입했다
  • 그리고 이렇게 대입한 수를 반복문을 통해 x가 y보다 클 때까지 x를 1씩 증가하면서 answer에 x의 값을 대입시키도록 하여 문제를 해결했다

다른 풀이

class Solution {

    public long solution(int a, int b) {
        return sumAtoB(Math.min(a, b), Math.max(b, a));
    }

    private long sumAtoB(long a, long b) {
        return (b - a + 1) * (a + b) / 2;
    }
}
  • 프로그래머스의 다른 풀이를 확인해보았는데, 대부분의 아이디어는 조건문과 반복문으로 해결하여 비슷했다
  • 하지만 이렇게 등차수열 수학 공식을 메서드화 하고, Math.min()과 Math.max() 함수를 통해 작은 값, 큰 값을 구해서 등차수열에 대입하여 문제를 해결이 가능하다는 것이 신기해서 가져와보았다
  • 물론.. 수학을 잘 못하는 나로서는 이렇게 해결하는 것은 불가능하겠지만 Math 클래스의 함수를 활용하는 것을 바로 떠올릴 수 있을 것 같다

서울에서 김서방 찾기

문제

  • 프로그래머스 - https://school.programmers.co.kr/learn/courses/30/lessons/12919
  • String형 배열 seoul의 element 중 "Kim"의 위치 x를 찾아 "김서방은 x에 있다"는 String을 반환하는 함수를 완성
  • seoul에 "Kim'은 오직 한 번만 나타나며 잘못된 값이 입력되는 경우는 없음

제한 조건

  • seoul은 길이1 이상 1000 이하인 배열
  • seoul의 원소는 길이 1이상 20 이하인 문자열
  • "Kim"은 반드시 seoul 안에 포함되어 있음

입출력 예시

나의 풀이

package test;

public class SearchKim {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.solution(new String[]{"Jane", "Kim"}));
    }


    static class Solution {
        public String solution(String[] seoul) {

            String answer = "";
            for (int i = 0; i < seoul.length; i++) {
                if (seoul[i].equals("Kim")) {
                    answer = "김서방은 " + i + "에 있다";
                    break; // break; 추가 하면 끝까지 안돎
                }
            }
            return answer;
        }
    }
}
  • 문자열 배열을 첫 번째 요소부터 순서대로 하나씩 "Kim"이라는 문자열과 같은지 비교하고 같으면 원하는 문자열을 생성하여 반환하도록 작성하였다
  • 배열에서 "Kim"이라는 문자열을 찾으면 중간에 반복문을 종료하도록 break; 문을 추가하여 불필요한 반복 횟수를 줄였다

다른 풀이

import java.util.Arrays;
public class FindKim {
    public String findKim(String[] seoul){
        int x = Arrays.asList(seoul).indexOf("Kim");        
        return "김서방은 "+ x + "에 있다";
    }
}
  • Arrays.asList()로 String 배열을 List로 변환하여 indexOf() 메서드를 사용하여 인덱스의 위치를 찾는 방법도 나쁘지 않은 것 같다
  • 다만 indexOf() 함수도 내부에서 순회해서 찾는 걸로 알고 있어서 성능 면에서는 그냥 문자열 배열에서 바로 찾는 것이 좋을 것 같다.

나누어 떨어지는 숫자 배열

문제

  • 프로그래머스 - https://school.programmers.co.kr/learn/courses/30/lessons/12910
  • array의 각 element 중 divisor로 나누어 떨어지는 값을 오름차순으로 정렬한 배열을 반환하는 함수를 완성
  • divisor로 나누어 떨어지는 element가 하나도 없다면 배열에 -1을 담아 반환

제한 조건

  • arr은 자연수를 담은 배열임
  • 정수 i, j에 대해 i != j 이면 arr[i] != arr[j]임
  • divisor는 자연수이며 array는 길이 1 이상인 배열임

입출력 예시

나의 풀이

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Solution {

    public int[] solution(int[] arr, int divisor) {

        List<Integer> buffer = new ArrayList<>();

        int count = 0;

        for (int num : arr) {
            if (num % divisor == 0) {
                buffer.add(num);
                count++;
            }
        }

        if (buffer.isEmpty()) {
            return new int[]{-1};
        }

        Collections.sort(buffer);

        int[] answer = new int[count];
        for (int i = 0; i < buffer.size(); i++) {
            answer[i] = buffer.get(i);
        }
        return answer;
    }
}
  • 처음 접근은 단순히 int[] answer 변수를 선언만 하고 이후에 반복문으로 num % diviser == 0 인 조건이 맞을 때 num을 배열 answer의 인덱스를 하나씩 증가하면서 대입하려고 했으나 배열을 먼저 선언하지 않아서 문제에 봉착했다.
  • 반복문을 2번 돌려야 할지, 아니면 배열을 우선 크게 잡아두고 조건문에 따라서 값을 초기화 한 다음 Arrays.copyOf() 메서드를 통해 배열에 초기화한 개수만큼 새로운 배열을 만들어서 반환할지 고민했다.
  • 하지만 그냥 가변길이의 리스트를 하나 만들어서 조건에 맞는 값들을 해당 리스트에 저장해 두고, 저장한 개수를 센 다음 저장한 개수만큼 int 배열을 만들고 해당 배열에 list에 저장하는 방법을 택했다.
  • 중간에 요구사항을 지키기 위해 조건에 맞는 숫자를 저장하는 List(buffer)가 비어있으면 divisor로 나눌 수 없다는 뜻이므로 새로운 인트 배열에 -1을 초기화하여 반환하고, 만약 buffer에 값이 있으면 오름차순으로 정렬을 위해 Collections.sort()로 리스트를 정렬시켰다.

다른 풀이

import java.util.Arrays;

class Solution {
    public int[] solution(int[] arr, int divisor) {
        int[] result = Arrays.stream(arr)
            .filter(num -> num % divisor == 0)
            .sorted()
            .toArray();
        return result.length == 0 ? new int[]{-1} : result;
    }
}
  • 확실히 스트림을 사용하면 코드가 매우 간결해지고 제공되는 기능도 많아서 쉽게 해결되는 것 같다
  • .filter().sorted().toArray()다 아는 기능이지만 막상 하려고 하면 많이 다루어 보질 않아서 한 번에 생각이 나질 않아서 반복 숙달이 좀 필요할 것 같다.

SQL(MySQL)

DATETIME에서 DATE로 변경하기

문제 및 테이블 예시

  • ANIMAL_INS 테이블에 등록된 모든 레코드에 대해 각 동물의 아이디와 이름, 들어온 날짜를 조회하는 SQL문을 작성
  • 결과는 아이디 순으로 조회

 

입출력 예시

나의 풀이

SELECT
    ANIMAL_ID,
    NAME,
    DATE_FORMAT(DATETIME, '%Y-%m-%d') AS 날짜
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
  • 다른 부분은 쉬웠지만 날짜 형식을 바꾸는 문법을 몰랐었다
  • 검색을 통해서 DATE_FORMAT(date, format) 문법으로 첫 번째 값에 변경할 날짜 칼럼을 입력하고, 그 이후에 형식을 입력하면 해당 형식으로 변경되어 출력된다.
  • 형식 문자열(format) 내에서 사용하는 주요 포맷 지정자는 아래와 같다
    • %Y: 4자리 연도
      • 예시: 2025
    • %y: 2자리 연도
      • 예시: 25
    • %m: 2자리 월
      • 예시: 01 ~ 12
    • %c: 월, (앞에 0 없음)
      • 예시: 1 ~ 12
    • %d: 2자리 일
      • 예시: 01 ~ 31
    • %e: 일 (앞에 0 없음)
      • 예시: 1 ~ 31
    • %H: 24시간 형식의 시간
      • 예시: 00 ~ 23
    • %h 또는 %l(소문자 L): 12시간 형식의 시간
      • 예시: 01 ~ 12
    • %i: 분, %s:
      • 예시: 00 ~ 59
    • %p: AM 또는 PM
  • STR_TO_DATE(string, format)으로 문자열로 입력된 날짜 형식을 날짜 형식으로 변경할 수도 있다
    • SELECT STR_TO_DATE('21-05-2023', '%d-%m-%Y') -> 2023-05-21

흉부외과 또는 일반외과 의사 목록 출력하기(중복 조건)

문제 및 테이블 예시

  • DOCTOR 테이블에서 진료과가 흉부외과(CS)이거나 일반외과(GS)인 의사의 이름, 의사 ID, 진료과, 고용일자를 조회하는 SQL 작성
  • 결과는 고용일자를 기준으로 내림차순 정렬하고 고용일자가 같다면 이름을 기준으로 오름차순 정렬

 

출력 예시

나의 풀이

SELECT
    DR_NAME,
    DR_ID,
    MCDP_CD,
    DATE_FORMAT(HIRE_YMD, '%Y-%m-%d')
FROM DOCTOR
WHERE
    MCDP_CD IN ('GS', 'CS')
ORDER BY
    HIRE_YMD DESC,
    DR_NAME
  • 처음에는 막연하게 조건이 두 개니까 WHERE 문에 MCDP_CD = 'GS' OR MCDP_CD = 'CS' 처럼 무식하게 작성하였으나, 같은 컬럼에서 조회하는데 두 번이나 컬럼명을 쓰는 게 어색해서 이럴 때 쓰는 문법이 있었던 것 같아 생각해 보았는데 생각이 나질 않았다.
  • 그래서 검색해 봤는데 IN 절을 활용하면 여러 조건을 검색할 수 있다는 것을 다시 한번 상기시킬 수 있었다.
  • SQLD를 벼락치기로 공부해서 취득하다 보니 확실히 자주 사용하지 않으면 쿼리문을 까먹게 되는 것 같아서 SQL문도 조금씩 다뤄봐야겠다.

가격이 제일 비싼 식품 정보 출력

문제 및 테이블 예시

  • FOOD_PRODUCT 테이블에서 가격이 제일 비싼 식품을 조회하는 SQL 작성
  • 전체 컬럼이 조회되어야 함

 

출력 예시

나의 풀이

SELECT  
    *
FROM
    FOOD_PRODUCT
WHERE
    PRICE = (SELECT MAX(PRICE) FROM FOOD_PRODUCT)
  • 역시 처음엔 단순한 방법으로 SELECT 문에 모든 컬럼을 입력하고 마지막에 MAX(PRICE)를 입력하여 결과를 출력하도록 했으나 코드 실행에는 제대로 뜬 것 같았지만 채점에서는 실패가 떠서 이런 방식이 아니라고 생각이 들었다.
  • 그래서 메인 쿼리의 PRICE의 값과 서브 쿼리를 통해 조회된 PRICE의 값을 조회해서 비교하여 접근할까 생각했는데, 이건 정렬이고 서브 쿼리에서 MAX(PRICE)의 값을 조회하여 해당 값으로 메인 쿼리의 WHERE 절에서 PRICE를 찾으면 될 것 같다고 생각하여 문제를 해결하였음

다른 방안

SELECT  
    *
FROM
    FOOD_PRODUCT
ORDER BY
    PRICE DESC
LIMIT 1;
  • 서브 쿼리 없이 PRICE를 내림 차순으로 정렬한 다음 LIMIT으로 1개만 반환해도 가장 비싼 상품을 반환할 수 있다

조건에 맞는 회원수 구하기

문제 및 테이블 예시

  • USER_INFO 테이블에서 2021년에 가입한 회원 중 나이가 20세 이상 29세 이하인 회원이 몇 명인지 출력하는 SQL문 작성

출력 예시

나의 풀이

SELECT
    COUNT(*)
FROM USER_INFO
WHERE
    JOINED LIKE '2021%'
AND
    19 < age && age < 30
  • 날짜 컬럼인 JOINED 컬럼 중 2021년 데이터를 먼저 조회해야 하기 때문에 LIKE '2021%'을 사용하여 2021로 시작하는 데이터를 먼저 조회했다
  • 그 이후 자바 문법과 동일한 문법으로 19 < age && age < 30 을 통해 추가 조건문을 삽입하여 조회된 결과의 개수를 반환하도록 작성했다

다른 방안

SELECT
    COUNT(*)
FROM USER_INFO
WHERE
    JOINED YEAR(JOINED) = 2021
AND
    AGE BETWEEN 20 AND 29
  • BETWEEN을 사용하여 특정 조건 사이의 데이터를 반환받을 수 있으며, BETWEEN 비교 시에는 조건의 값이 조건에 포함된다(이상, 이하)
  • 날짜를 조회할 때에도 타입이 날짜타입이므로 YEAR(날짜컬럼) = 조회할 년도를 통해서 조회할 수 있으며 이것도 BETWEEN이나 '2021-01-01' <= JOINED && JOINED <= '2021-12-31처럼 직접 비교하여 값을 조회할 수도 있다
  • 비슷한 기능을 하는 MONTH(), DAY()도 존재하며 날짜 관련 함수 사용 시 인덱스 활용에 불리할 수 있다고 하며 SQL에서 && 대신 표준 문법인 AND, OR를 사용하는 것을 권장한다고 한다.
728x90