제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

어렸을 때에 수학 문제를 풀 때에 가장자리에 1씩 두고 중간 값은 같은 열 위의 행과 같은 행 왼쪽 열의 합을 더해주는 방식을 사용했습니다.

소스코드

import java.util.*;

class Solution {
    private final int MOD = 1000000007;
    private final int DISABLE = -1;

    public int solution(int m, int n, int[][] puddles) {
        int[][] dp = new int[n][m];
        for(int[] puddle: puddles) {
            dp[puddle[1]-1][puddle[0]-1] = DISABLE;
        }
        
        // init
        dp[0][0] = 1;
        for(int i = 1; i < m; i++) { // 가로
            if (dp[0][i] == DISABLE || dp[0][i-1] == DISABLE) {
                break;
            } else {
                dp[0][i] = 1;
            }
        }
        for(int i = 1; i < n; i++) { // 세로
            if (dp[i][0] == DISABLE || dp[i-1][0] == DISABLE) {
                break;
            } else {
                dp[i][0] = 1;
            }
        }
        
        for(int i = 1; i < n; i++) {
            for(int j = 1; j < m; j++) {
                if(dp[i][j] == DISABLE) continue;
                
                if (dp[i-1][j] == DISABLE) {
                    dp[i][j] = dp[i][j-1];
                } else if (dp[i][j-1] == DISABLE) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = (dp[i-1][j] % MOD + dp[i][j-1] % MOD) % MOD;
                }
            }
        }
        
        return dp[n-1][m-1] % MOD;
    }
}

코드 설명

  • int[][] dp = new int[n][m]; for(int[] puddle: puddles) { dp[puddle[1]-1][puddle[0]-1] = DISABLE; }
    • dp배열을 초기화합니다.
    • 웅덩이 위치에는 상수로 정의한 DISABLE로 설정합니다.
  • 가장자리 초기화
    • 가로와 세로의 값을 초기화합니다.
    • 만약 웅덩이가 있다면 그 이후로는 처리를 하지 않습니다.
for(int i = 1; i < n; i++) {
    for(int j = 1; j < m; j++) {
        if(dp[i][j] == DISABLE) continue;
        
        if (dp[i-1][j] == DISABLE) {
            dp[i][j] = dp[i][j-1];
        } else if (dp[i][j-1] == DISABLE) {
            dp[i][j] = dp[i-1][j];
        } else {
            dp[i][j] = (dp[i-1][j] % MOD + dp[i][j-1] % MOD) % MOD;
        }
    }
}
  • 현재 칸 (i, j)는 위 칸(dp[i-1][j])과 왼쪽 칸(dp[i][j-1])에서 올 수 있습니다.
  • 웅덩이(DISABLE)가 있는 경우
    • 위 칸이 웅덩이면 왼쪽 경로만 고려합니다.
    • 왼쪽 칸이 웅덩이면 위쪽 경로만 고려합니다.
  • dp[i][j]=(dp[i-1][j]%MOD+dp[i][j-1]%MOD)%MOD
    • 위와 왼쪽 모두 웅덩이가 아니라면 두 경로의 합을 계산합니다.
  • return dp[n-1][m-1] % MOD;
    • 결과를 반환합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

삼각형 형태로 있는 숫자를 이차원 배열로 생각하여 문제를 풀었습니다. 현재 행의 i-1에 j열 또는 j-1의 합으로 문제를 풀었습니다.

여기에서 ArrayIndexOutOfBoundsException이 나오지 않게 체크를 해줍니다.

소스코드

int solution(int[][] triangle) {
        int len = triangle.length;
        int[][] ans = new int[len][len];
        for(int i = 0; i < len; i++) {
            for(int j = 0; j <= i; j++) {
                ans[i][j] = triangle[i][j];
            }
        }

        // init
        for(int i = 1; i < len; i++) {
            ans[i][0] = ans[i - 1][0] + triangle[i][0];
            ans[i][i] = ans[i - 1][i - 1] + triangle[i][i];
        }

        for(int i = 2; i < len; i++) {
            for(int j = 1; j < i; j++) {
                ans[i][j] = Math.max(ans[i-1][j-1], ans[i-1][j]) + triangle[i][j];
            }
        }

        int answer = Integer.MIN_VALUE;
        for(int i = 0; i < len; i++) {
            answer = Math.max(answer, ans[len-1][i]);
        }

        return answer;
    }
}

코드 설명

  • 초기화
    • len: 삼각형의 높이.
    • ans: 각 위치까지의 최대 합을 저장하는 2차원 DP 배열.
    • 삼각형의 값을 그대로 ans 배열에 복사합니다.
for (int i = 1; i < len; i++) {
    ans[i][0] = ans[i - 1][0] + triangle[i][0]; // 삼각형 맨 왼쪽 경로
    ans[i][i] = ans[i - 1][i - 1] + triangle[i][i]; // 삼각형 맨 오른쪽 경로
}

삼각형의 왼쪽 끝 경로오른쪽 끝 경로는 고정된 값만 참조하므로 초기화합니다.

  • 왼쪽 끝 경로는 위쪽 요소에서만 내려옵니다.
  • 오른쪽 끝 경로도 마찬가지입니다.
for (int i = 2; i < len; i++) {
    for (int j = 1; j < i; j++) {
        ans[i][j] = Math.max(ans[i - 1][j - 1], ans[i - 1][j]) + triangle[i][j];
    }
}
  • 중간 경로 계산
    • 위치 ans[i][j]는 위에서 내려올 수 있는 두 가지 경로 중 더 큰 값을 선택합니다:
      • ans[i-1][j-1]: 왼쪽 대각선 위에서 내려오는 경로.
      • ans[i-1][j]: 바로 위에서 내려오는 경로.
    • 이 중 더 큰 값을 선택해 현재 위치의 값을 갱신합니다.
  • 마지막 행에서 가장 큰 값을 조회 후 반환합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

이 주어진 숫자 N을 여러 번 사용하여 특정 숫자 number를 만들기 위해 필요한 최소 사용 횟수를 찾는 문제를 해결합니다.
이것은 동적 계획법(Dynamic Programming, DP)을 활용하여 숫자 N을 점진적으로 조합해 나가는 방식입니다.

소스코드

import java.util.*;

class Solution {
    public int solution(int N, int number) {
        // N을 i번 사용해서 나온 수 => dp[i] 에 저장
        List<Set<Integer>> dp = new ArrayList<>();
        
        for(int i = 0; i <= 8; i++) {
            dp.add(new HashSet<>());
        }
        
        for(int i = 1; i <= 8; i++) {
            // 5, 55, 555 ....
            int repeatN = Integer.parseInt(String.valueOf(N).repeat(i));
            dp.get(i).add(repeatN);
            
            // 사칙연산으로 찾기
            for (int j = 1; j < i; j++) {
                for (int a: dp.get(j)) {
                    for (int b : dp.get(i - j)) {
                        // +
                        dp.get(i).add(a + b);
                        // -
                        dp.get(i).add(a - b);
                        dp.get(i).add(b - a);
                        // *
                        dp.get(i).add(a * b);
                        // /
                        if (b != 0) dp.get(i).add(a / b);
                        if (a != 0) dp.get(i).add(b / a);
                    }
                }
            }
            
            if (dp.get(i).contains(number)) {
                return i;
            }
        }
        
        return -1;
    }
}

코드 설명

  • 입력값 및 변수 초기화
    • dp[i]: 숫자 N을 정확히 i번 사용해서 만들 수 있는 모든 숫자의 집합입니다.
    • 문제에 나왔던대로 답은 8보다 클 수 없기에 dp는 최대 8번까지 숫자를 사용한 결과를 저장할 수 있도록 초기화합니다.
  • int repeatN = Integer.parseInt(String.valueOf(N).repeat(i)); dp.get(i).add(repeatN);
    String.valueOf(N).repeat(i)를 통해 N을 i번 반복합니다.
    • String.valueOf(N).repeat(i): 숫자 N을 i번 반복한 문자열을 생성합니다.
    • 숫자 repeatN을 dp[i]에 추가합니다.
  • 사칙연산으로 숫자 조합
    • j와 i - j로 숫자 N을 각각 j번, i-j번 사용해서 만든 숫자들을 조합하여 dp[i]를 채웁니다.
    • 각 숫자 쌍 (a, b)에 대해 사칙연산을 수행하고 결과를 dp[i]에 추가:
      • 덧셈: a + b
      • 뺄셈: a - b, b - a
      • 곱셈: a * b
      • 나눗셈: a / b, b / a (0으로 나누는 경우를 제외).
  • if (dp.get(i).contains(number)) { return i; }
    • targetNumber가 있으면 현재 i를 반환합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 @Controller, @Service, @Repository의 역할과 특징 정리입니다.

 

도입

Spring 프레임워크는 계층구조로 코드를 나누어 각 계층마다 역할을 분리하였습니다.(객체 지향적 특징)
이와 같은 계층 구조는 코드의 역할 분리를 명확히 하고 유지보수성을 높이는 데 중요한 역할을 합니다.

  • Presentation Layer: 클라이언트의 요청 처리
  • Business Layer: 비즈니스 로직 처리
  • Persistence Layer: 데이터 접근 처리

클라이언트와의 요청 처리는 @Controller, 비즈니스 로직 처리는 @Service, 데이터베이스와의 통신은 @Repository를 통해 코드를 구성합니다. 

 

이번 포스팅에서는 @Controller, @Service, @Repository의 역할과 특징을 살펴보겠습니다.

 

@Controller

@Controller는 Spring MVC에서 컨트롤러 클래스를 정의할 때 사용되는 어노테이션입니다. 일반적으로 뷰 템플릿을 반환하는 방식으로 클라이언트의 요청을 처리합니다. 이 어노테이션이 있는 클래스는 HTTP 요청을 처리하고, Model 객체를 사용하여 데이터를 뷰에 전달합니다. @Controller는 주로 웹 애플리케이션에서 서버 사이드 렌더링을 위한 컨트롤러로 사용됩니다.

@Controller 구조

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. Dispatcher Servlet은 해당 뷰 네임에 해당하는 뷰를 ViewResolver를 통해서 뷰를 반환합니다.
@Controller
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping("/users")
    public String getUsers(Model model) {
        model.addAttribute("users", userService.findAll());
        return "userList"; // "userList" 뷰 반환
    }
}

userList.html

 

@Controller + @RequestBody

@RequestBody는 HTTP 요청 본문을 자바 객체로 변환하여 매핑해주는 어노테이션입니다. 이 어노테이션을 @Controller와 함께 사용하면, 클라이언트에서 전달한 JSON, XML 등의 데이터를 객체로 받을 수 있습니다. @Controller는 뷰를 반환하는 역할을 계속하고, @RequestBody는 데이터를 뷰에 매핑하는 역할을 합니다.

@Controller + @RequestBody 구조

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환됩니다. 이는 @ResponseBody 어노테이션으로 개발됩니다.
@Controller
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    /**
     * Handles POST requests to create a new user from JSON data and renders the "userDetail" view.
     *
     * @param user the user data from the request body
     * @param model the model to hold attributes for the view
     * @return the name of the view to be rendered
     */
    @PostMapping("/users")
    public String createUser(@RequestBody User user, Model model) {
        User savedUser = userService.save(user);
        model.addAttribute("user", savedUser);
        return "userDetail"; // Return the name of the view
    }
}

성공적인 response

 

@RestController

@RestController는 @Controller와 @ResponseBody를 결합한 어노테이션입니다. @RestController가 적용된 클래스는 RESTful 웹 서비스를 제공하며, 메서드의 반환 값이 바로 HTTP 응답 본문으로 전송됩니다. 따라서 @RestController는 일반적으로 JSON, XML과 같은 데이터를 직접 반환하는 데 사용됩니다. 뷰를 반환하지 않으며, 주로 API 서버에서 사용됩니다.

@RestController

  1. 클라이언트로부터 받은 요청을 통해 Handler 어댑터를 통해 해당 요청을 처리할 수 있는 controller를 찾습니다.
  2. controller는 service와 repository의 비즈니스 로직을 처리한 후 뷰 이름을 handlerAdapter에게 전달합니다.
  3. 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환됩니다. 이는 @RestController 어노테이션으로 개발됩니다.
@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    /**
     * Handles GET requests to retrieve the list of users as JSON.
     *
     * @return a list of users
     */
    @GetMapping("/json-users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}

 

@Service

@Service 어노테이션은 서비스 계층에 사용됩니다. 서비스 계층은 비즈니스 로직을 처리하는 곳으로, 일반적으로 데이터베이스에서 데이터를 조회하고, 가공하여 컨트롤러로 전달하는 역할을 합니다. @Service는 주로 서비스 클래스를 나타내며, @Component의 특성을 그대로 가지고 있어 Spring 컨테이너에 의해 관리됩니다.

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    public List<User> findAll() {
        return userRepository.findAll();
    }
}

 

 

@Repository

@Repository는 데이터 접근 계층에 사용됩니다. 이 어노테이션은 주로 데이터베이스와 상호작용하는 클래스를 나타냅니다. @Repository 어노테이션을 사용하면 Spring이 해당 클래스를 자동으로 데이터 접근 객체(DAO)로 인식하고, 예외 변환 기능을 제공합니다. @Repository는 @Service와 동일하게 @Component의 특성을 그대로 가지고 있어 Spring 컨테이너에 의해 관리되며, 데이터베이스와의 상호작용을 담당합니다.

// In-memory Database
@Repository
public class ItemRepository {

    private static final Map<Long, Item> store = new HashMap<>(); //static
    private static long sequence = 0L; //static

    public Item save(Item item) {
        item.setId(++sequence);
        store.put(item.getId(), item);
        return item;
    }
}


// JPA
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findAll();
}

 

 

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

스프링 정리를 위한 포스팅입니다.
해당 포스팅은 Lombok 어노테이션 입니다.

 

Lombok이란?

Lombok은 Java 애플리케이션에서 보일러플레이트 코드(반복적인 코드)를 줄여주는 라이브러리입니다.

이를 통해 코드의 가독성을 높이고 유지보수를 쉽게 만들어 줍니다.

왜 Lombok을 사용해야 하는가?

  • Java의 기본 클래스는 자주 사용되는 메서드(생성자, getter, setter, toString 등)수동으로 작성해야 합니다.
  • Lombok은 이러한 반복적인 코드 생성을 자동화하여 개발 생산성을 크게 향상시킵니다.

Lombok 어노테이션 종류

@Getter / @Setter

  • 클래스의 모든 필드에 대해 getter와 setter 메서드를 자동으로 생성합니다.
@Getter @Setter
public class Person {
    private String name;
    private int age;
}

@ToString

  • 객체의 필드를 문자열로 변환하는 toString() 메서드를 자동으로 생성합니다.
@ToString 
public class Person { 
    private String name; 
    private int age; 
}

@NoArgsConstructor / @AllArgsConstructor

  • 기본 생성자 및 모든 필드를 초기화하는 생성자를 자동으로 생성합니다.
@NoArgsConstructor
@AllArgsConstructor
public class Animal {
    int age;
    String name;
}

@Builder

  • 빌더 패턴을 적용하여 객체 생성 시 가독성을 높입니다.
@Builder
public class Animal {
    int age;
    String name;
}

 

@Data

  • @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 모두 포함하는 어노테이션입니다. 주로 DTO 객체에 사용됩니다.
@Data
public class Plant {
    private int height;
    private String origin;

    public Plant(int height, String origin) {
        this.height = height;
        this.origin = origin;
    }
}

Lombok 설치방법

spring에서 lombok을 설치하기 위해서는 maven 또는 gradle을 사용하면 됩니다. 저는 gradle을 사용하겠습니다. 

build.gradle에서 compileOnly 'org.projectlombok:lombok'annotationProcessor 'org.projectlombok:lombok'

를 설정한 후에 gradle 프로젝트를 reload 하면 됩니다.

dependencies {
	// ...
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

예제 확인

public class Test {
    public static void main(String[] args) {
        // @Getter, @Setter
        Person person = new Person(1, 3, 4, 5, 8);
        System.out.println(person.getShoes()); // getShoes() automatically generated
        person.setShoes(84); // setShoes() automatically generated
        System.out.println(person.getShoes());

        // @ToString
        System.out.println("person.toString: " + person.toString()); // toString() automatically generated

        // @NoArgsConstructor / @AllArgsConstructor
        Animal tomy = new Animal(20, "tomy");
        Animal aymee = new Animal(); // Default constructor automatically generated
        System.out.println("tomy = " + tomy); // toString() automatically generated
        System.out.println("aymee = " + aymee); // toString() automatically generated

        // @Builder
        Animal hiro = Animal.builder().age(7).name("hiro").build(); // Using builder pattern
        System.out.println("hiro.toString() = " + hiro); // toString() automatically generated

        // @EqualsAndHashCode, @RequiredArgsConstructor
        Plant plant1 = new Plant(2, "korea");
        Plant plant2 = new Plant(2, "korea");

        // @Data
        System.out.println("plant1 equals plant2: " + plant1.equals(plant2)); // true, since fields are the same
        System.out.println("plant1 hashCode: " + plant1.hashCode()); // Same hashCode for equal objects
        System.out.println("plant2 hashCode: " + plant2.hashCode()); // Same hashCode for equal objects

        // @RequiredArgsConstructor automatically generates a constructor with final fields
        Plant plant3 = new Plant(3, "japan"); // Constructor with height and origin fields
        System.out.println("plant3: " + plant3); // toString() automatically generated
    }
}

결과 확인

다음과 같이 모든 어노테이션에 대해 결과를 확인할 수 있습니다.

 

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁

자바 정리를 위한 포스팅입니다.
해당 포스팅은 어노테이션 정리입니다.

 

등장 배경

과거에는 소스파일은 java파일 설정 값을 XML파일에 명시하여 관리했습니다. 변경될 수 있는 데이터들을 코드가 아닌 외부 설정 파일(XML)에 분리하기 때문에, 재컴파일 없이도 쉽게 변경사항을 저장할 수 있었습니다.

하지만, 프로그램 작성을 위해 매번 많은 설정을 해야하며, 수 많은 설정파일들을 관리해야 했습니다.

 

그에 따라서 설정파일을 업데이트를 하지 않아 코드의 실행동작과 같은 부분들이 달라지거나 개발에 대해서 불편함이 많았습니다.

source code + 설정 파일

이렇듯 하나의 파일 내에서 코드와 설정을 관리할 수 있는 어노테이션이 등장했는데

 

자바 어노테이션(Java Annotation)은 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종으로. @기호를 앞에 붙여서 사용하고, JDK 1.5 버전 이상에서 사용 가능합니다. 

 

어노테이션의 역할로는 컴파일러에게 코드 작성 문법 에러 체크하도록 정보제공, 런타임에 특정 기능을 실행하도록 정보 제공 등 하고 있습니다.

 

어노테이션 종류

어노테이션의 종류로는 세가지가 있는데,

  1. 표준 어노테이션
  2. 메타 어노테이션
  3. 사용자 정의 어노테이션

이 존재합니다

표준 어노테이션

표준 어노테이션은 자바가 기본적으로 제공해주는 어노테이션을 의미하는 것으로,

@Override, @Deprecated, @SuppressWarning, @FunctionIInterface 네 가지 어노테이션이 가장 많이 사용됩니다.

/**
 * {@code @Override} 어노테이션은 자식 클래스에서 부모 클래스의 메서드를 오버라이드할 때 사용됩니다.
 * 부모 클래스의 메서드와 일치하지 않으면 컴파일 에러가 발생합니다.
 */
class Parent {
    void run() {}
}

class Child extends Parent {
    @Override // 컴파일 에러! 잘못된 오버라이드
    void wrongOverride() {}
}


/**
 * {@code @Deprecated} 어노테이션은 해당 메서드, 클래스, 또는 필드가 더 이상 사용되지 않음을 나타냅니다.
 * 이 어노테이션이 붙은 요소는 향후 버전에서 제거될 수 있으므로, 사용을 피해야 합니다.
 */
@Deprecated
public int getInt() {
    return 1;
}

 

오버라이드 어노테이션은 잘못된 오버라이딩을 검사해주어 컴파일 에러를 야기하는 어노테이션이고, 

Deprecated 어노테이션은 개발자에게 사용하지 않는 것을 권장하는 필드나 메서드를 알리는 데에 사용하는 어노테이션입니다. 

메타 어노테이션

어노테이션을 위한 어노테이션으로 어노테이션을 정의할 때 사용 그 예시로 Target, Retention, Documented등이 있습니다.

 

Target은 어노테이션을 정의할 때 적용대상을 지정하는데 사용하는 어노테이션이며 Retention은 어노테이션이 유지되는 기간을 지정하는데 사용하는 어노테이션입니다.

/**
 * {@code @CustomAnnotation}은 메타 어노테이션 {@code @Retention}과 {@code @Target}을 사용한 예제입니다.
 * 이 어노테이션은 클래스에만 적용될 수 있으며, 런타임 동안 유지됩니다.
 */
@Retention(RetentionPolicy.RUNTIME)  // 이 어노테이션은 런타임까지 유지됩니다.
@Target(ElementType.TYPE)           // 이 어노테이션은 클래스, 인터페이스 또는 열거형에만 적용됩니다.
@interface CustomAnnotation {
    String value() default "Default Value";
}

@CustomAnnotation(value = "Hello, Metaannotations!")  // CustomAnnotation을 클래스에 적용
public class MetaAnnotation {
    public void display() {
        System.out.println("Sample class method.");
    }
}

Source는 소스파일에만 존재하는것. Runtime은 클래스파일에만 존재하는것. 실행시에 사용가능하고 Inherited는 어노테이션의 상속에 필요한 어노테이션입니다.

사용자 정의 어노테이션

어노테이션은 커스텀하여 사용할 수도 있는데, 다음과 같이 어노테이션을 생성하고, 사용하면됩니다

만약에 다른 폴더에 존재하는 파일이면 어노테이션을 import 하면 됩니다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * {@code @LogExecutionTime} 어노테이션은 메서드의 실행 시간을 측정하고 로그로 출력하는 데 사용됩니다.
 * 이 어노테이션은 메서드에만 적용됩니다.
 */
@Retention(RetentionPolicy.RUNTIME)  // 이 어노테이션은 런타임 동안 유지됩니다.
@Target(ElementType.METHOD)         // 이 어노테이션은 메서드에만 적용됩니다.
public @interface LogExecutionTime {
}


// 다른 폴더
public class Sample {

    /**
     * 이 메서드는 실행 시간을 측정할 대상입니다.
     * {@code @LogExecutionTime} 어노테이션을 통해 실행 시간을 로깅합니다.
     */
    @LogExecutionTime
    public void performTask() {
        try {
            // 임의의 작업 (2초 지연)
            Thread.sleep(2000);
            System.out.println("Task performed.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

특징

  • 적용시 값을 지정하지 않으면, 사용될 수 있는 기본값을 지정할 수 있다.
  • 요소가 하나이고 이름이 value일 때는 요소의 이름 생략가능하다.
  • 요소의 타입이 배열인 경우, 괄호{}를 사용해야 한다.

규칙

  • 요소의 타입은 기본형, String, enum, 어노테이션, Class만 허용된다.
  • 괄호()안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다.
  • 요소의 타입을 매개변수로 정의할 수 없다.(<T>)

코드

제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

차량들의 진입점과 출구점이 주어졌을 때, 각 차량들이 겹치지 않도록 선택하는 것은 최대한 적은 공간을 차지하면서 차량들을 배치하는 문제입니다. 이를 해결하기 위해서는 먼저 고속도로를 나간 지점이 빠른 차량을 우선적으로 선택하는 것이 중요합니다.

소스코드

import java.util.*;

class Solution {
    static class Car implements Comparable<Car> {
        int src, desc;
        public Car(int src, int desc) {
            this.src = src;
            this.desc = desc;
        }
        
        @Override
        public int compareTo(Car c) {
            return this.desc - c.desc;
        }
    }
    
    public int solution(int[][] routes) {
        List<Car> cars = new ArrayList<>();
        for(int[] route: routes) {
            Car newCar = new Car(route[0], route[1]);
            cars.add(newCar);
        }
        
        Collections.sort(cars);
        
        int answer = 0;
        int last = -1;
        for(Car cur: cars) {
            if(last == -1) {
                last = cur.desc;
                answer++;
            } else if (cur.src <= last && cur.desc >= last) {
            } else {
                last = cur.desc;
                answer++;
            }
        }
        
        return answer++;
    }
}

코드 설명

  • Car 클래스
    Car 클래스는 각 차량의 진입 구간(src)과 출구 구간(desc)을 나타냅니다.
    • 이 클래스는 Comparable<Car>를 구현하여 Car 객체들을 desc (출구 구간) 기준으로 오름차순 정렬하도록 정의됩니다.
    • compareTo 메서드는 출구 구간 desc 값 기준으로 비교하여 정렬 순서를 결정합니다.
  • Car 객체 생성 및 리스트에 추가:
    • routes 배열을 순회하여 Car 객체를 생성하고, 각 차의 진입점(src)과 출구점(desc)을 Car 객체에 저장한 후 cars 리스트에 추가합니다.
  • 정렬:
    • cars 리스트를 desc 값(출구 지점)을 기준으로 오름차순 정렬합니다.
    • 정렬 기준이 desc 값이므로, 먼저 출구가 빠른 차량부터 선택하게 됩니다. 이는 그리디 알고리즘에서 가장 중요한 부분입니다.
  • 차량 선택:
    • 차량을 하나씩 순차적으로 살펴보면서, 겹치지 않도록 선택합니다.
    • last는 마지막으로 선택된 차량의 출구점을 추적하는 변수입니다. 처음에는 -1로 초기화합니다.
    • for문을 통해 각 차량의 구간을 확인하면서, 다음 조건에 맞는 차량만 선택합니다:
      • last == -1: 첫 번째 차량이므로 last를 그 차량의 출구점으로 설정하고 차량을 선택합니다.
      • cur.src <= last && cur.desc >= last: 현재 차량의 진입점이 last보다 작거나 같고, 출구점이 last보다 크거나 같은 경우는 이미 다른 차량이 선택된 구간과 겹친다는 의미입니다. 이 경우에는 차량을 선택하지 않고 넘어갑니다.
      • 그렇지 않으면, 현재 차량을 선택하고, last를 해당 차량의 출구점(desc)으로 갱신합니다.
  • 결과 반환:
    • 최종적으로 선택된 차량의 수는 answer 변수에 저장됩니다.
    • 마지막에 answer++는 answer를 증가시키는 부분인데, 이 부분은 잘못된 코드입니다. return answer로 해야 합니다. answer++는 잘못된 값이 반환될 수 있습니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers 알고리즘 고득점 Kit입니다.

포인트

가장 최소의 코스트로 모든 다리를 연결하여야 하므로 최소 신장 트리를 구하는 문제입니다.

최소 신장 트리를 구하기 위한 크루스칼 알고리즘을 활용하여 문제를 풀어줍니다.

소스코드

import java.util.*;

class Solution {
    
    class Bridge implements Comparable<Bridge> {
        int src, dest, cost;
        
        public Bridge(int src, int dest, int cost) {
            this.src = src;
            this.dest = dest;
            this.cost = cost;
        }
        
        @Override
        public int compareTo(Bridge b) {
            if(this.cost == b.cost) {
                return this.src - b.src;
            }
            return this.cost - b.cost;
        }
    }
    int[] parents;
    
    public int findParent(int n) {
        if (parents[n] != n) {
            parents[n] = findParent(parents[n]);
        }
        return parents[n];
    }
    
    public void union(int a, int b){
        a = findParent(a);
        b = findParent(b);
        
        if (a < b) {
            parents[b] = a;
        } else {
            parents[a] = b;
        }
    }
    
    public int solution(int n, int[][] costs) {
        int answer = 0;
        int len = costs.length;
        List<Bridge> bridges = new ArrayList<>();
        
        for(int i = 0; i < len; i++){
            Bridge newBridge = new Bridge(costs[i][0], costs[i][1], costs[i][2]);
            bridges.add(newBridge);
        }
        Collections.sort(bridges);
        parents = new int[n+1];
        for(int i = 0; i <= n; i++) {
            parents[i] = i;
        }
        
        for(Bridge bridge: bridges) {
            if (findParent(bridge.src) != findParent(bridge.dest)) {
                union(bridge.src, bridge.dest);
                answer += bridge.cost;
            }
        }
        
        return answer;
    }
}

코드 설명

  • Bridge 클래스
    섬 간의 다리를 나타내는 클래스입니다.
    • src: 다리의 출발 섬.
    • dest: 다리의 도착 섬.
    • cost: 다리의 건설 비용.
    • Comparable 인터페이스를 구현하여, 비용이 낮은 순서대로 정렬되도록 합니다. 비용이 같을 경우 src의 값을 기준으로 정렬됩니다.
  • findParent 메서드
    findParent는 주어진 노드 n의 대표 부모 노드를 찾는 재귀 메서드입니다.
    • 경로 압축(Path Compression) 기법을 사용하여, 트리의 높이를 줄이고 효율성을 높입니다.
  • union 메서드
    • 두 노드 a와 b를 하나의 집합으로 합칩니다. 두 집합의 대표 부모를 찾은 후, 더 작은 번호의 노드가 부모가 되도록 합니다.
    • 이를 통해, 서로 다른 집합을 병합하면서 트리 구조를 형성합니다.
  • Solution 메서드
    • Bridge 객체 리스트 bridges에 다리 정보를 추가하고 비용 순으로 정렬합니다.
    • parents 배열을 초기화하여 각 섬의 부모를 자기 자신으로 설정합니다.
    • for 반복문을 통해 비용이 낮은 다리부터 탐색하여, 두 섬이 다른 집합에 속해 있을 경우 union 연산을 수행하고 다리 비용을 answer에 더합니다.
    • 최소 비용으로 모든 섬을 연결할 수 있는 다리들의 총 비용을 answer로 반환합니다.

+ Recent posts