문제정리

참여한 선수들의 이름이 담긴 배열 participant, 완주한 선수들의 이름이 담긴 배열 completion
완주하지 못한 선수이름은 return
이름은 알파벳 소문자로 이루어져 있으며, 참가자 중에는 동명이인 존재

 

이 문제의 포인트 중 하나는 completion의 길이는 participant의 길이보다 1 작다 라는 부분인 것 같다. 그러면 무조건 완주하지 못한 선수가 1명이라는 뜻이기에 그걸 처음에 잡아내지 못함

처음 작성한 코드

import java.util.Arrays;

class Solution {
    public String solution(String[] participant, String[] completion) {
        Arrays.sort(participant);
        Arrays.sort(completion);

        int i = 0;
        for(i=0;i<completion.length;i++)
            if(!participant[i].equals(completion[i]))
                break;

        return participant[i];
    }
}

 

두 배열을 전부 오름차순으로 정렬해주어 이름이 동일한 위치 오게끔 해주었고, 각 위치에 동일한 이름이 들어있지 않을 시 바로 break 하여 반복문을 종료하고 해당 위치에 있는 참가자를 반환시켰다.

시간은

 

HashMap 구현

import java.util.HashMap;

class Solution {
    public String solution(String[] participant, String[] completion) {
        HashMap<String, Integer> map = new HashMap<>();

        // 참가자 명단을 HashMap에 저장 (이름별 개수 카운트)
        for (String p : participant) {
            map.put(p, map.getOrDefault(p, 0) + 1);
        }

        // 완주자 명단을 확인하면서 참가자 명단에서 제거
        for (String c : completion) {
            map.put(c, map.get(c) - 1);
        }

        // value가 1인 참가자가 완주하지 못한 사람
        for (String key : map.keySet()) {
            if (map.get(key) > 0) {
                return key;
            }
        }

        return ""; // 정상적으로 동작하면 여기에 도달하지 않음
    }
}

 

HashMap은 Key-Value의 Pair를 관리하는 클래스로,
HashMap<String,Integer>로 지정하면 Key는 String, Value는 Integer 형태로 정의하는 것이다.

map.getOrDefault(p, 0) -> HashMap에 값이 있으면 기존 값 반환하고, 없으면 기본값 0을 반환

keySet -> HashMap 에 저장된 모든 키 값들을 가져오는 메서드

 


 

이전 코드와 비교했을때 HashMap을 사용하면 정렬 없이 더 빠르게 해결 가능하며, 더 많은 참가자가 있을 경우에도 빠르게 찾을 수 있음.

문제정리

N마리의 폰켓몬 중 N/2마리를 가져가도 좋다
최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택, N 마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아 종류의 개수의 최댓값 하나만 return
nums 길이는 항상 짝수

 

처음 작성한 코드

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

class Solution {
    public int solution(int[] nums) {
        int answer = 0;

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

        for(int i = 0; i < nums.length; i++) {
            if(list.contains(nums[i])) {
                continue;
            }
            list.add(nums[i]);
            if(list.size() == nums.length / 2 ) {
                break;
            }
        }
        answer = list.size();
        return answer;
    }
}

 

기존에 자주 사용했던 ArrayList를 활용해서 문제 해결 list.contains(num[i])를 사용해서 이미 리스트에 있는 폰켓몬이면 continue 하고 아니면 add 리스트 최대 길이가 실제 가져갈 수 있는 최대 개수를 넘어가게 되면 반복문을 멈추고, 그 값을 return해서 값을 얻어낸다.

결과적으로는 성공으로 떴지만, 다른 사람들 코드를 찾아보니 보통 HashSet을 사용해서 해결

 

최종 코드

class Solution {
    public int solution(int[] nums) {
        int answer = 0;
        HashSet<Integer> set = new HashSet<>();

        for(int i = 0; i < nums.length; i++){
            set.add(nums[i]);
        }

        int get = nums.length / 2 ;

        if(set.size() > get){
           answer = get;
        }else {
            answer = set.size();
        }

        return answer;
    }
}

 

ArrayList.contains()는 시간 복잡도가 O(N)으로 list.contains(num[i])는 리스트의 크기가 커질수록 느려진다고 한다. HashSet을 사용해서 O(1)로 중복 검사를 수행한다.
(set.add(num)을 사용하면 중복이 자동으로 제거됨 -> O(1) 연산)

시간은..

 

코드 개선

// for(int i = 0; i < nums.length; i++){
//            set.add(nums[i]);
//        }
        

for (int num : nums) { // 모든 폰켓몬을 순회하며 중복 제거
	set.add(num);
}


//        int get = nums.length / 2 ;

//        if(set.size() > get){
//           answer = get;
//        }else {
//            answer = set.size();
//        }

//        return answer;


// 최대로 가질 수 있는 폰켓몬 종류 수
return Math.min(set.size(), num.length / 2);
 

내가 작성한 최종 코드도 기능적으로는 문제가 없지만 Math.min()을 사용하니 확실히 간결해짐..

JPA(Java Persistence API)

Java 객체와 DB 엔티티(테이블) 자체를 그대로 매핑해서 처리할 수 있는 접근 방식을 채택한 새로운 기술표준

JPA의 접근 방식은 ORM(Object-Relational Mapping) 기술을 의미한다. 즉, 객체와 데이터베이스 간의 매핑 기술을 의미, Java 개발자가 좀 더 객체지향 관점에서 개발할 수 있게 하고, 개발을 용이하게 해주어서 DB와 Java간의 불일치를 해소해 준다.

MyBatis와 다르게 SQL 문의 작성이 불필요하며 ORM 내부적으로 java 메소드에 적합한 SQL 문이 자동으로 생성이 되어 실행되게 된다. Java 개발자는 클래스만 만들어서 사용하고, ORM 프레임워크가 자동으로 관련된 SQL을 만들어 준다고 한다.

그래서 찾아봤는데
자동으로 SQL을 만들어 준다고 했으니 설정 값을 어떻게 해야하나..?

spring:
  jpa:
    hibernate:
      ddl-auto: update  # or create

 

update 또는 create 옵션을 사용하면 JPA가 엔티티 기반으로 테이블을 자동으로 생성해준다고 한다. => DBeaver에서 수동으로 테이블을 만들 필요없음!

그동안에는 직접 테이블을 생성하고 연결했는데 이렇게 되면 테스트 데이터도 프로젝트 실행 시 자동으로 데이터 삽입도 가능하게 된다.

근데 주의해야할 점은 복잡한 쿼리를 만들어야 하는데 여러 테이블을 Join해서 데이터 결과를 가져와야 하는 경우에, JPA로는 복잡한 쿼리를 만드는 데는 용이하지 않음

뭐.. 일단 JPA로 사용하다가, 복잡해질때는 MyBatis를 같이 사용해보자 가능하다고 함!

사실 어떻게 쓰이는지 배우려고 적용하는거니 거기에 의미를 두자

 

 


@Entity
@Table(name = "member")  // 테이블명 지정
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 자동 생성
    private Long id;

    @Column(nullable = false, unique = true, length = 50)
    private String loginId;

 

이렇게 해서 JPA 사용하고 있고,

잘들어갔나 DBeaver에서 SELECT * FROM member; 해서 확인해봤는데

 

잘 매핑되는 것은 확인되었지만, 카멜 표기법으로 작성한 속성이 자동으로 login_id 처럼 변환되는 것을 확인할 수 있다. 혹시 나중에 에러가 발생할 수 있으니 이 점을 잘 기억해두고, 만약 'DB 속성값이 일치하지 않는다'는 에러가 발생하면 1순위로 확인해 보자

테스트 데이터 자동 삽입

@Component
public class DataInitializer implements CommandLineRunner {

    @Autowired
    private MemberRepository memberRepository;

    @Override
    public void run(String... args) throws Exception {
        if (memberRepository.count() == 0) {  // 기존 데이터가 없을 때만 실행
            memberRepository.save(Member.builder()
                    .loginId("user01")
                    .name("김민준")
                    .email("minjun01@example.com")
                    .password("password123")
                    .authLevel(1)  // 일반 사용자
                    .build());

 

확인 완료!!

HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect'

 

application.yml에서 hibernate 설정을 제거해서 해결
Hibernate 6.x 이상에서는 PostgreSQL이 자동으로 감지되므로 설정이 불필요하다.

 

spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering.

 

open-in-view 설정이 기본적으로 true로 되어있어서, View 렌더링 중에도 데이터베이스 쿼리가 실행될 수 있음 / yml에서 비활성화하면 View 렌더링 중 발생할 수 있는 LazyInitializationException 와 같은 문제를 방지할 수 있다.

LazyInitializationException은 데이터베이스에서 가져온 객체를 나중에 필요한 부분만 로드하는 Lazy Loading (지연 로딩) 기능을 사용할 때, Spring이 DB 연결을 이미 끊어버린 상태에서 추가 데이터를 가져오려 할때 발생하는 에러

문제정리

문자열 S에서 같은 알파벳이 2개 붙어있는 짝을 찾고, 제거한 뒤 앞뒤로 문자열을 이어 붙인다.
성공적으로 수행할 수 있으면 1, 아닐 경우 0

처음 작성한 코드

class Solution {
    public int solution(String s) {
        while (!s.equals("")) {
            String[] str = s.split("");
            for (int i = 0; i < str.length - 1; i++) {
                if (str[i].equals(str[i + 1])) {
                    s = "";
                    for (int j = 0; j < str.length ; j++) {
                        if (i == j) {
                            j++;
                            continue;
                        }
                        s += str[j];
                    }
                    break;
                }
                if(i == str.length -2){
                    if(!s.equals("")){
                        return 0;
                    }
                }
            }
        }
        return 1;
    }
}

 

코드를 평소와 같이 익숙한 형태와 문법으로 해결하려고 했으나

 

시간초과..... 아무리 다르게 생각을 해도 다른 해결법이 생각이 나지않아 구글링해서 다른 사람들 코드 참고해봄..;;

하긴 반복문이 삼중으로 있으니 시간이 오래 걸릴수밖에..반복문말고도 다른 해결책으로 생각을 바꿔야할듯

최종코드

class Solution {
    public int solution(String s) {
        int answer = 0;
        Stack<Character> stack = new Stack<>(); // 스택 생성 (문자 저장)

        for(char c : s.toCharArray()){ // 문자열의 각 문자를 하나씩 확인
            if(stack.size()>0 && stack.peek() == c){ // 스택이 비어있지 않고, 스택의 맨 위 문자가 현재 문자와 같다면 true
                stack.pop(); // 스택의 맨 위 문자 제거 
            }else{
                stack.push(c); // 쌍을 이루지 않으면 현재 문자를 스택에 추가 
            }
        }
        if(stack.isEmpty()){ // 반복문 종료 후 스택이 비어 있으면 
            return 1; // 문자열이 완전히 제거되어있음을 표시 
        }
        return answer; // 기본값 0 반환
    }
}

 

시간은

 

Stack이란?

Stack은 데이터를 쌓아 올리는 자료구조

LIFO (Lask In, First Out) -> 나중에 들어간 데이터가 먼저 나온다.

  • push : 스택에 데이터를 추가
  • pop : 스택의 가장 위 데이터를 꺼냄
  • peek : 스택의 가장 위 데이터를 확인 (꺼내지는 않음)

 

문제정리

피보나치 수는 1 이상의 n에 대하여 F(n) = F(n-1) + F(n-2) 가 적용되는 수
2 이상의 n이 입력되었을 때, n번째 피보나치 수를 1234567으로 나눈 나머지를 리턴한 함수

 

이라는데 문제를 이해하는데 시간이 좀 걸림..;; 각각 2이상의 수에 나눠서 나머지를 구하라는 건지..

작성한 코드

class Solution {
    public int solution(int n) {
        int[] answer = new int[n + 1]; 

        for (int i = 0; i <= n; i++) {
            if(i == 0){
                answer[i] = 0;
            }else if(i == 1){
                answer[i] = 1;
            }else {
                int sum = answer[i - 1] + answer[i -2];
                answer[i] = sum % 1234567;
            }
        }

        return answer[n];
    }
}

 

문제를 이해하고 나서는 해결하는 데에는 오래걸리지 않음!

시간은

+ 추가🧐

피보나치 수열은 대표적인 동적 프로그래밍의 예라고 한다.
동적 프로그래밍(DP)은 큰 문제를 작은 문제로 나누어 푸는 알고리즘 설계 기법이다. 한 번 계산한 작은 문제의 답을 저장(메모이제이션, Memoization)해서, 나중에 동일한 문제가 다시 나타났을 때 계산을 반복하지 않고 저장된 값을 활용해 효율성을 높이는 것이 핵심이라고 한다.

위 코드를 보았을 대 각 숫자 F(1)부터 F(n)까지 한 번씩만 계산하기 때문제 중복 계산이 제거되는 것을 확인할 수 있다. (시간 복잡도가 줄어든다.)
반복문을 사용하기 때문에 함수 호출 스택을 사용하지 않아 메모리 효율이 더 좋다고 한다.

문제정리

0과 1로만 이루어진 문자열 s
s의 모든 0을 제거, s의 길이를 2진법으로 표현한 문자열
이진변환의 횟수와 변환 과정에서 제거된 모든 0의 개수를 배열에 담아 return

처음 작성한 코드

class Solution {
    public int[] solution(String s) {
        int[] answer = {};
        String[] re = s.split("");

        int count = 0; // 이진 변환의 횟수
        int remove = 0; // 제거된 모든 0의 개수

        String result = ""; // 이진 변환 결과

        while(!result.equals("1")){

            int length = 0; // 0 제거 후 길이

            for(int i = 0; i <= re.length; i++){
                int a = Integer.parseInt(re[i]);
                if (a == 1){
                    result = a + "";
                    length++;
                }else {
                    remove++;
                }
            }

            // 0 제거 후 길이를 이진 변환
            for(int j = 1; j <= length; j++){
                
            }


        }


        return answer;
    }
}

 

위 코드를 보면 이진 변환하려는 코드를 직접 계산하려고 for문 써서 생각하고 있는데.. 아무리 생각해도 어떻게 만들어도 노가다로 코드가 엄청 길어질거 같은데.. 그리고 어떻게 해야하지도 생각이 너무 안나서.. 결국 인터넷 검색해봤다..

검색해보니 역시 이진변환 해주는 코드가 따로 있었구나 그럴줄알았어!!!😓

자바에서 2진수, 10진수, 16진수 간 변환

  1. Integer.parseInt(String s, int radix) : 문자열을 특정 진수에서 10진수로 변환
  2. Integer.toBinaryString(int i) : 10진수를 2진수 문자열로 변환
  3. Integer.toHexString(int i) : 10진수를 16진수 문자열로 변환

최종 코드

class Solution {
    public int[] solution(String s) {

        int count = 0; // 이진 변환의 횟수
        int remove = 0; // 제거된 모든 0의 개수

        String result = s; // 이진 변환 결과

        while(!result.equals("1")){
            String[] re = result.split("");
            int length = 0; // 0 제거 후 길이

            for(int i = 0; i < re.length; i++){
                int a = Integer.parseInt(re[i]);
                if (a == 1){
                    result = a + "";
                    length++;
                }else {
                    remove++;
                }
            }

            // 0 제거 후 길이를 이진 변환
            result = Integer.toBinaryString(length);
            count++;
        }

        int[] answer = {count, remove};

        return answer;
    }
}

 

위 코드처럼 마무리는 지었지만.. 코드 길이가 너무 길고, 사실 좀 더 간결하게 적을 수 있는 방법이 있을텐데 현재로서는 최선..이였다... 다른 사람 코드 도 참고 해봐야할 것 같다.

시간은

 

ㅎㅎㅎㅎ.. 이진법에서 막힘..

다른 사람들의 코드

보통 배열을 사용하지 않고, replaceAll 사용해서 0을 제거 한 후 toBinaryString 이용해서 2진 변환하는 방식으로 해서 코드가 간결 했다..

매번 문제를 풀때 배열을 먼저 적용해서 접근하는 방식에 너무 익숙해져버린건 아닐까 생각을 하면서 풀어야겠다..
그래도 이번에는 계속 for문에서 갇혀서 풀었었는데 이 전 문제에서 while문 필요성을 느껴서 바로 적용했는데.. 이진변환만 아니였으면 좀 더 빠르게 풀 수 있었을텐데!

아쉽다 그래도 이번 문제를 통해서 toBinaryString 알 수 있었음!

문제정리

구명보트는 최대 2명씩 (그러면 한명도 탈 수 있음), 무게 제한은 100kg
모든 사람을 구출하기 위해 필요한 구명보트 개수의 최솟값을 return

처음 작성한 코드

import java.util.Arrays;

class Solution {
    public int solution(int[] people, int limit) {
        int answer = 0;
        Arrays.sort(people);

        int a = 0;
        int b = people.length - 1;

        while(a < b){
            int sum = people[a] + people[b];
            if(limit >= sum){
                answer++;
                b--;
                a++;
            }else{
                answer++;
                b--;
            }
        }
        return answer;
    }
}

위와 같이 했는데 else에도 answer++;를 적용한 이유는 b-- 라는 부분 큰 수 값도 더하고 그 총 값을 return 했는데 근데 이제 문제는 people[0]이 만약 다른 값과 더했을 때 limit값을 넘어가서 혼자남아있게 되면 이 값은 포함되지 않는다는것..

최종 코드

class Solution {
    public int solution(int[] people, int limit) {
        int answer = 0;
        Arrays.sort(people);

        int a = 0;
        int b = people.length - 1;

        while(a < b){
            int sum = people[a] + people[b];


            if(limit >= sum){
                answer++;
                b--;
                a++;
            }else{
                b--;
            }
        }
        return people.length - answer;
    }
}

 

쉽게 생각해서..sum 값이 limit보다 작거나 같을때 answer 값을 하나씩 올리고, 그 값을 전체 인원수에서 빼기만 하면 모든 사람을 구출하기 위해 필요한 구명보트 개수의 최솟값을 구할 수 있잖아..?

한번 막혀가지고.. Array.list도 사용해보고 이것저것 해보느라

 

시간은 1시간.. 넘게걸림...;;;

(변수명도 좀 생각하면서 해야할까..?)

+ Recent posts