본문 바로가기

항해99

[항해99] 3주차 프로그래머스 알고리즘 연습하기

3주차에서는 알고리즘 연습을 진행했다. 

페어프로그래밍 형식으로 진행되었고 풀지 못했던 문제나 풀었지만 어려웠던 문제를 위주로 리뷰를 해보고자 한다.

(마라톤 35문제)

 

제일 작은 수 제거하기

- 풀지못함

 

작성했던 코드

int[] arr = {4,3,2,1};
        List<Integer> answer = new ArrayList<>();

        Arrays.sort(arr);
        for(int i =0; i<arr.length; i++){
            answer.add((arr[i]));
        }

        Arrays.sort(arr);
        answer.remove(0);

        for (int i : arr) {
            answer.remove((Integer.valueOf(0)));
        }

        for (int i : answer){
            System.out.print(i);
        }

 

GPT에서 리뷰 받은 결과

1. Arrays.sort(arr); : 두 번 호출할 필요가 없다. - > 정렬된 배열을 한 번 만들고 나면 그대로 사용가능

2. 이 방식은 remove 메서드가 리스트를 재배열해야 해서 효율적이지 않음

3. remove(0)을 호출해서 첫 번째 요소를 제거하고 있지만, 이것은 항상 작은 값을 제거하는게 아님.

(첫 번째 요소가 중복 값이라면, 중복되는 값 중 가장 작은 값이 아닐 수 있음)

 

Solution

- 리스트에서 첫 번째 요소를 제거하는 대신, 가장 작은 값과 가은 값을 찾아 제거해야함

- 배열 정렬 후 요소를 리스트에 추가하는 것보다 바로 리스트에 추가하고 나중에 정렬하는 것이 효율적

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

public class Main {
    public static void main(String[] args) {
        int[] arr = {4, 3, 2, 1};

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

        // 배열의 요소를 리스트에 추가
        for (int i : arr) {
            answer.add(i);
        }

        // 배열을 정렬
        Arrays.sort(arr);

        // 정렬된 배열의 첫 번째 요소(가장 작은 값)을 찾음
        int min = arr[0];

        // 리스트에서 가장 작은 값을 제외한 나머지 값을 찾아 제거
        answer.remove(Integer.valueOf(min));

        // 결과 출력
        for (int i : answer) {
            System.out.print(i + " ");
        }
    }
}

 

다른 방식의 코드

package projext;

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

public class q19 {
    private static int[] solution(int[] arr) {
        // -1 반환하는 코드
        if (arr.length == 1) {return new int[]{-1};}

	// arr의 복사본을 만들고 정렬, 가장 작은 값 찾기
        int[] tmp = Arrays.copyOf(arr,arr.length);
        Arrays.sort(arr);
        int min = arr[0];
		
        // 빈 리스트를 생성 (가장 작은 값을 제외한 값들을 저장)
        List<Integer> list = new ArrayList<>();
        
        // tmp를 i로 반복해준다.
        for(int i : tmp){
        // 값이 가장 작은 경우에만 추가해준다.
            if(i!=min){list.add(i);}
        }

        return list.stream().mapToInt(i -> i).toArray();

    }

    public static void main(String[] args) {
        System.out.println(Arrays.toString(q19.solution(new int[]{4,3,2,1})));
    }
}

 

 stream

 컬렉션, 배열 등에 저장된 요소들을 하나씩 참조하면서 코드를 실행할 수 있는 기능

스트림을 사용하면 불필요한 for문을 사용하지 않을 수 있고, 람다식을 활용할 수 있어서 코드를 직관적으로 처리 가능

 

- list에 저장된 값을 stream 변환 -> mapToInt() 으로 각 요소에 정수로 매핑해준다.

- toArray 메소드를 활용해서 스트림의 요소를 정수(배열)로 반환한다.

→ 리스트에 저장된 값들을 정수(배열)로 변환해서 반환해준다.

 

* Arrays.toString() 메소드를 사용해서 정수 배열을 문자열로 변환하고 출력해준다.

 

최대공약수와 최소공배수

- 풀긴했지만 어려움

 

작성 코드

public static void main(String[] args) {
        int a = 2;
        int b = 5;

        System.out.println(gcd(a,b));
        System.out.println(lcm(a,b));
    }

    public static int gcd(int a, int b){
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

    public static int lcm(int a, int b) {
        return a * b / gcd(a, b);
    }
}

 

이 문제는 접근 방식을 모르겠어서 검색을 해봤는데 공식같은 것이 있었다.

유클리드 호제법을 이용하는 방식인데 유클리드 호제법은 최대 공약수를 구하는 효율적인 방법이다.

유클리드 호제법?

큰 수에서 작은 수를 빼도 두 수의 최대공약수(GCD)가 변하지 않는다는 원리에 기초한다.

이 과정은 두 숫자가 동일해질 때까지 계속되며, 이는 최대 공약수이다.

  1. 두 개의 양의 정수 a와 b ( a는 b보다 크거나 같다.)
  2. a를 b로 나누고 나머지를 r로 둔다. ( r은 b보다 작다.)
  3. a를 b로, b를 r로 바꾼다.
  4. b가 0이 될 때까지 2번과 3번을 반복한다.
  5. 이 시점에서 a의 값은 원래 두 정수 a와 b의 최대 공약수.

코드 리뷰

1. gcd 메서드에서는 유클리드 호제법을 사용해 최대 공약수를 구함

2. lcm 메서드에서는 최소 공배수를 구함

  • 최소 공배수는 ( a * b ) / gcd( a * b)로 구할 수 있음

 

최소직사각형

- 이해하기 어려워서 풀기 어려웠음

 

작성코드

public static void main(String[] args) {
        // [[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]]	133
        // [[14, 4], [19, 6], [16, 6], [18, 7], 11, 7]]
        int[][] sizes = {{60, 50}, {30, 70}, {60, 30}, {80, 40}};
        int max_h = 0;
        int max_v =0;

        for (int i = 0; i < sizes.length; i++) {
            int v = Math.max(sizes[i][0], sizes[i][1]); // 최대값 비교
            int h = Math.min(sizes[i][0], sizes[i][1]); // 최소값 비교
            max_v = Math.max(max_v, v);
            max_h = Math.max(max_h, h);
        }
        System.out.println(max_v * max_h);
    }

 

이 문제는 문제 자체를 해석하는데 어려움이 있었다. 그래서 스펙테이터를 맡으신 분의 도움을 받아 문제를 풀게 되었다.

  1.  각 배열의 큰 값과 큰 값을 비교해서 가장 큰 값을 뽑는다.
  2.  각 배열의 작은 값과 작은 값을 비교해서 그 중 큰 값을 뽑는다.

배열의 작은 값들을 비교했을 때 큰 값을 뽑는 이유는

예를 들어 길이가 60*50 인 것과 70*30인 것이 있으면 70*50으로 만들면 두 개의 명함이 모두 들어 갈 수 있지만 70*30으로 만든다면 60*50의 명함은 넣을 수 없게된다. 

그렇기 때문에 작은 값 중 큰 값을 넣어준다.

 

코드 리뷰

1. for문을 사용하여 주어진 사각형의 개수만큼 반복해서 각 사각형의 가로와 세로 길이를 비교

2. max_v와 max_h에서 최대 가로 길이와 최대 세로 길이를 업데이트

 

※ 시저암호

- 풀지 못함

 

작성코드

public static void main(String[] args) {
        String s = "AB";
        int n = 1;
        System.out.println(solution(s, n));
    }

    public static String solution(String s, int n) {
        String answer = "";

        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);

            if (Character.isLowerCase(ch)) { //소문자
                ch = (char) ((ch - 'a' + n) % 26 + 'a');
            } else if (Character.isUpperCase(ch)) { //대문자
                ch = (char) ((ch - 'A' + n) % 26 + 'A');
            }
            answer += ch;
        }
        return answer;
    }
}

 

이 문제는 아스키코드로 접근을 해야겠다고 까진 생각을 했는데 코드를 어떻게 짜야 할 지 감이 안잡혔다.

여기서는 스펙테이터를 맡아주신 분이 본인이 작성했던 코드를 가지고 설명을 해주셨는데 몇 개는 테스트 통과가 됐지만 몇 문제는 실패해서 결국엔 공식이 있는 것 같아 그것을 사용해서 풀었다.

 

코드 리뷰

1. for문 : 주어진 문자열의 문자를 각각 확인하기 위해 반복문을 사용

2. if문 : 현재 문자가 소문자인 경우, 소문자 알파벳을 이동시킴

  • 소문자 알파벳의 이동에는 아스키 코드를 사용
  • + n % 26(알파벳의 개수) : 알파벳 범위 내에서만 이동이 되도록 하는 것
  • 이동 결과를 소문자 알파벳으로 변환
  • 대문자도 동일한 방식으로 해줌

3. 문자를 answer 문자열에 추가

저와 페어분 모두 자바가 처음이라 초반에는 많이 어렵고 헤맸었다. (1레벨 14문제 푸는데 8시간이나 걸렸다.ㅠㅠ)
한 문제에 30분 이상 잡고 있지 않는게 좋다고 하셨지만, 조금만 풀면 되는데.. 이것만 하면 될 것 같은데.. 이런 생각에 계속 진행을 하게 되었는데 사실 이것 때문에 공부가 많이 됐던 것 같다.
베이스가 거의 없어서 그런건지 처음에는 이렇게 풀면 될 것 같은데 이런 생각조차 안들었던 문제가 많았다.
그래도 계속 풀다보니까 몇몇 문제를 제외하면 이건 이런식으로 풀면 되겠다. 이정도 틀은 잡히기 시작했고 예상보다 빠르게 마라톤 문제를 완주 할 수 있었다. 
한 문제 풀 때마다 성취감을 느낄 수 있는 점이 너무 좋았고, 다음에 비슷한 유형을 봤을 때 제대로 잘 풀 수 있는 내 모습을 보고 발전 했다고 느껴져서 알고리즘 공부는 좋은 경험이였다.