728x90

들어가기에 앞서

해당 Post는 이진 트리에 대한 기초 지식을 요구합니다.

만약 이진트리를 모른다면 이 글을 읽고 난 이후 봐주시기 바랍니다.

이진 탐색 트리 값 삽입

이진트리 중 중복 값은 허용하지 않고, 왼쪽은 부모 노드보다 작은 값을 오른쪽 자식노드는 부모노드 보다 큰 조건을 충족하는 트리를 이진 탐색 트리라고 합니다.

 

위와 같이 데이터가 삽입되며, 왼쪽은 currnet보다 작은 값, 오른쪽은 큰 값을 배치하게 됩니다.

public class BinaryTree {
    TreeNode<Integer> root;

    public void add(Integer value) {
        root = add(root, value);
    }

    private TreeNode<Integer> add(TreeNode<Integer> current, Integer value) {
        if(current == null) {
            return new TreeNode<>(value);
        }

        if(current.getValue() < value) {
            current.setRight(add(current.getRight(), value));
        } else {
            current.setLeft(add(current.getLeft(), value));
        }

        return current;
    }
}

이진 탐색 트리의 삭제

이진 탐색 트리의 삭제는 어떤 상태를 가진 노드를 삭제하느냐로 3가지로 나눠볼 수 있습니다.

Leaf Node 삭제

가장 생각할 게 없는 단순한 삭제입니다.

 

자식 노드가 존재하지 않기 때문에 균형 이진트리가 아니라면 따로 큰 작업이 이뤄지지 않는 삭제입니다.

자식이 1개인 부모 Node 삭제

Leaf Node삭제보다 살짝 작업이 추가된 삭제라고 생각합니다.

 

자식이 1개일 경우 삭제할 노드의 부모 노드에 자식 노드의 Instance정보를 연결해 주고, 본인은 참조되지 않는 방법으로 간단하게 삭제처리할 수 있습니다.

자식이 2개인 부모 Node 삭제

자식이 2개인 경우 어떤 정책을 통해 부모 노드를 대체할 것인지를 고민해야 합니다.

  • 중위 전임자(In-order Predecessor): 삭제할 노드의 왼쪽 서브트리에서 가장 큰 값을 가진 노드.
  • 중위 후속자(In-order Successor): 삭제할 노드의 오른쪽 서브트리에서 가장 작은 값을 가진 노드.

위와 같이 정책을 정하여 삭제를 진행할 경우 데이터의 쏠림 현상으로 인해 시간복잡도가 차이 날 수 있는데,

평균적으로 O(log n)의 시간복잡도를 가지며, 최악의 경우 O(n)의 시간복잡도를 가지게 됩니다.

 

중위 전임자 정책을 사용하여 삭제하였을 경우

public class BinaryTree {
    TreeNode<Integer> root;

    private TreeNode<Integer> add(TreeNode<Integer> current, TreeNode<Integer> prevNode) {
        if(current == null) {
            return prevNode;
        }

        if(current.getValue() < prevNode.getValue()) {
            current.setRight(add(current.getRight(), prevNode));
        } else {
            current.setLeft(add(current.getLeft(), prevNode));
        }

        return current;
    }

    public void remove(Integer value) {
        root = remove(root, value);
    }

    private TreeNode<Integer> remove(TreeNode<Integer> current, Integer value) {
        if(current == null) return null;
        // 중위 전임자(In-order Predecessor)
        if(Objects.equals(current.getValue(), value)) {
            TreeNode<Integer> predecessor = predecessor(current.getLeft());
            predecessor.setRight(current.getRight());
            if(current.getLeft() != null)
                add(predecessor, current.getLeft());
            return predecessor;
        }

        current.setLeft(remove(current.getLeft(), value));
        current.setRight(remove(current.getRight(), value));

        return current;
    }

    // 중위 전임자(In-order Predecessor): 삭제할 노드의 왼쪽 서브트리에서 가장 큰 값을 가진 노드.
    private TreeNode<Integer> predecessor(TreeNode<Integer> current) {
        if(current.getRight() == null)
            return current;

        TreeNode<Integer> predecessor =  predecessor(current.getRight());
        if(Objects.equals(current.getRight().getValue(), predecessor.getValue()))
            current.setRight(null);

        return predecessor;
    }
}


중위 후속자 정책을 사용하여 삭제하였을 경우

public class BinaryTree {
    TreeNode<Integer> root;

    private TreeNode<Integer> add(TreeNode<Integer> current, TreeNode<Integer> prevNode) {
        if(current == null) {
            return prevNode;
        }

        if(current.getValue() < prevNode.getValue()) {
            current.setRight(add(current.getRight(), prevNode));
        } else {
            current.setLeft(add(current.getLeft(), prevNode));
        }

        return current;
    }

    public void remove(Integer value) {
        root = remove(root, value);
    }

    private TreeNode<Integer> remove(TreeNode<Integer> current, Integer value) {
        if(current == null) return null;

        // 중위 후속자(In-order Successor)
        if(Objects.equals(current.getValue(), value)) {
            TreeNode<Integer> successor = successor(current.getRight());
            successor.setLeft(current.getLeft());
            if(current.getRight() != null)
                add(successor, current.getRight());
            return successor;
        }

        current.setLeft(remove(current.getLeft(), value));
        current.setRight(remove(current.getRight(), value));

        return current;
    }

    // 중위 후속자(In-order Successor): 삭제할 노드의 오른쪽 서브트리에서 가장 작은 값을 가진 노드.
    private TreeNode<Integer> successor(TreeNode<Integer> current) {
        if(current.getLeft() == null)
            return current;

        TreeNode<Integer> successor =  successor(current.getLeft());
        if(Objects.equals(current.getLeft().getValue(), successor.getValue()))
            current.setLeft(null);
        return successor;
    }
}


최종 코드

public class BinaryTree {
    TreeNode<Integer> root;

    public BinaryTree() {
        root = null;
    }

    public void add(Integer value) {
        root = add(root, value);
    }

    private TreeNode<Integer> add(TreeNode<Integer> current, Integer value) {
        if(current == null) {
            return new TreeNode<>(value);
        }

        if(current.getValue() < value) {
            current.setRight(add(current.getRight(), value));
        } else {
            current.setLeft(add(current.getLeft(), value));
        }

        return current;
    }

    private TreeNode<Integer> add(TreeNode<Integer> current, TreeNode<Integer> prevNode) {
        if(current == null) {
            return prevNode;
        }

        if(current.getValue() < prevNode.getValue()) {
            current.setRight(add(current.getRight(), prevNode));
        } else {
            current.setLeft(add(current.getLeft(), prevNode));
        }

        return current;
    }

    public void remove(Integer value) {
        root = remove(root, value);
    }

    private TreeNode<Integer> remove(TreeNode<Integer> current, Integer value) {
        if(current == null) return null;

        if(!Objects.equals(current.getValue(), value)) {
            current.setLeft(remove(current.getLeft(), value));
            current.setRight(remove(current.getRight(), value));
            return current;
        }

        // Leaf일 때 삭제
        if(current.getRight() == null && current.getLeft() == null)
            return null;

        if(current.getLeft() == null) return current.getRight();
        if(current.getRight() == null) return current.getLeft();

//        // 중위 전임자(In-order Predecessor)
//        TreeNode<Integer> predecessor = predecessor(current.getLeft());
//        predecessor.setRight(current.getRight());
//        if(current.getLeft() != null)
//           add(predecessor, current.getLeft());
//        return predecessor;

        // 중위 후속자(In-order Successor)
        TreeNode<Integer> successor = successor(current.getRight());
        successor.setLeft(current.getLeft());
        if(current.getRight() != null)
            add(successor, current.getRight());

        return successor;

    }

    // 중위 전임자(In-order Predecessor): 삭제할 노드의 왼쪽 서브트리에서 가장 큰 값을 가진 노드.
    private TreeNode<Integer> predecessor(TreeNode<Integer> current) {
        if(current.getRight() == null)
            return current;

        TreeNode<Integer> predecessor =  predecessor(current.getRight());
        if(Objects.equals(current.getRight().getValue(), predecessor.getValue()))
            current.setRight(null);

        return predecessor;
    }

    // 중위 후속자(In-order Successor): 삭제할 노드의 오른쪽 서브트리에서 가장 작은 값을 가진 노드.
    private TreeNode<Integer> successor(TreeNode<Integer> current) {
        if(current.getLeft() == null)
            return current;

        TreeNode<Integer> successor =  successor(current.getLeft());
        if(Objects.equals(current.getLeft().getValue(), successor.getValue()))
            current.setLeft(null);
        return successor;
    }


    // 전위 순회 (Pre-order Traversal) 출력 메서드
    public void traversePreOrder(TreeNode<Integer> node) {
        if (node != null) {
            System.out.print(" " + node.getValue());
            traversePreOrder(node.getLeft());
            traversePreOrder(node.getRight());
        }
    }

    // 중위 순회 (In-order Traversal) 출력 메서드
    public void traverseInOrder(TreeNode<Integer> node) {
        if (node != null) {
            traverseInOrder(node.getLeft());
            System.out.print(" " + node.getValue());
            traverseInOrder(node.getRight());
        }
    }

    // 후위 순회 (Post-order Traversal) 출력 메서드
    public void traversePostOrder(TreeNode node) {
        if (node != null) {
            traversePostOrder(node.getLeft());
            traversePostOrder(node.getRight());
            System.out.print(" " + node.getValue());
        }
    }

    // 레벨 순회 (Level-order Traversal) 출력 메서드
    public void traverseLevelOrder(TreeNode root) {
        if (root == null) return;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            System.out.print(" " + node.getValue());

            if (node.getLeft() != null) queue.add(node.getLeft());
            if (node.getRight() != null) queue.add(node.getRight());
        }
    }

    public static void main(String[] args) {
        BinaryTree bt = new BinaryTree();
        bt.add(5);
        bt.add(2);
        bt.add(3);
        bt.add(1);
        bt.add(0);
        bt.add(7);
        bt.add(8);
        bt.add(6);

        bt.remove(5);

        // 순회 결과 출력
        System.out.print("Pre-order traversal:");
        bt.traversePreOrder(bt.root);
        System.out.println();

        System.out.print("In-order traversal:");
        bt.traverseInOrder(bt.root);
        System.out.println();

        System.out.print("Post-order traversal:");
        bt.traversePostOrder(bt.root);
        System.out.println();

        System.out.print("Level-order traversal:");
        bt.traverseLevelOrder(bt.root);
        System.out.println();
    }
}
728x90

'알고리즘 문제풀이 > 자료구조' 카테고리의 다른 글

[Tree] 이진 트리 순회  (1) 2024.11.08
[Tree] 이진 트리 ( Binary Tree )  (0) 2024.11.07
[자료구조] 스택(Stack)  (0) 2024.10.21
728x90

 

바보같이 너무 많이 생각해서 어려웠던 문제였습니다.

 

문제자체는 단순합니다.

parent 값들을 주고 해당 값으로 생성된 node들 중 하나를 삭제하고, 이후 leaf의 갯수를 알아내면 되는 문제입니다.

 

parent와 index값을 저장을 하고,

index를 부모로 가지는 node들을 child로 저장,

삭제된 노드들의 정보를 가지게 된다면 간단하게 해결할 수 있는 문제입니다.

 

다만 주의할 점은 현재 삭제된 노드들만 가지고 있는 노드 또한 leaf라는 점입니다.

이점은 코드를 확인해보며 찾아보시기 바랍니다.

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int N = Integer.parseInt(br.readLine());

        int[] parents = new int[N];
        boolean[] isDeleted = new boolean[N];
        List<List<Integer>> graph = new ArrayList<>();

        for(int i = 0; i <= N; i++) {
            graph.add(new ArrayList<>());
        }

        int root = -1;
        StringTokenizer st = new StringTokenizer(br.readLine());

        for(int i = 0; i < N; i++) {
            parents[i] = Integer.parseInt(st.nextToken());
            if(parents[i] == -1) {
                root = i;
            } else {
                graph.get(parents[i]).add(i);
            }
        }
        int deleteNode = Integer.parseInt(br.readLine());
        if(parents[deleteNode] > -1) {
            delete(graph.get(parents[deleteNode]), isDeleted, deleteNode);
        } else {
            isDeleted[root] = true;
        }
        bw.write(countLeafNode(graph, isDeleted, root) + "");
        bw.flush();
        br.close();
        bw.close();
    }

    public static void delete(List<Integer> graph, boolean[] isDeleted, int node) {
        for(int child : graph) {
            if(child == node) {
                isDeleted[child] = true;
                return;
            }
        }
    }

    public static int countLeafNode(List<List<Integer>> graph, boolean[] isDeleted, int node) {
        if(isDeleted[node]) return 0;
        if(graph.get(node).isEmpty()) return 1;
        int count = 0;

        for(int child : graph.get(node)) {
            count += countLeafNode(graph, isDeleted, child);
        }

        return count == 0 ? 1 : count;
    }
}
728x90
728x90

문제를 보면 모든 노드들은 연결이 되어있고, 모든 노드들을 돌아볼 때 가장 적은 종류의 비행기( 간선 정보 )를 타고 움직이는 수를 알아내야하는 것이 문제의 의도 입니다.

 

이때 우리는 모든 노드가 연결되어있고, 최소한의 경로로 모든 노드를 방문해야한다는 조건에서 최소신장 트리를 떠올릴 수 있어야 합니다.

 

최소신장 트리의 모든 노드를 연결하면서 가중치의 합을 구하면 되겠는데, 이때 값은 N - 1로써 가장 간단하게 구할 수 있습니다.

(N은 노드 수입니다.)

 

import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader br;
    static BufferedWriter bw;
    static StringTokenizer st;
    public static void main(String[] args) throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int T = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();
        while (T-- > 0) {
            st = new StringTokenizer(br.readLine());
            int n = Integer.parseInt(st.nextToken());
            int m = Integer.parseInt(st.nextToken());
            for (int i = 0; i < m; i++) {
                br.readLine();
            }
            sb.append(n - 1).append("\n");
        }
        bw.write(sb.toString());
        bw.flush();
        br.close();
        bw.close();
    }

}
  • 시간복잡도: O(1)
  • 공간복잡도: O(1)
728x90
728x90

1. Mysql 접속

기본적으로 데이터베이스 작업을 하거나 테이블 작업을 하려면 설치한 mysql에 접속해야합니다.

 

만약 처음 설치하였다면 기본적으로 root라는 계정을 할당해주고, 해당 계정은 초기 설치할 때 설정한 비밀번호로 설정이 되게 됩니다. 

mysql -u root -p

2. Database

2-1. 생성

CREATE DATABASE 데이터베이스명;

2-2. 목록 확인

SHOW DATABASES;

2-3. 데이터베이스 선택

USE 데이터베이스명;

2-4. 데이터베이스 삭제

DROP DATABASE 데이터베이스명;

3. 사용자 및 권한 설정

3-1. 사용자 생성

CREATE USER '사용자명'@'호스트' IDENTIFIED BY '비밀번호';

호스트: 접속할 수 있는 정보로 로컬만 허용할 경우 localhost, 모두 허용할 경우 %로 지정

3-2. 권한 부여

GRANT 권한종류 ON 데이터베이스명.* TO '사용자명'@'호스트';
FLUSH PRIVILEGES;

권한 종류: ALL PRIVILEGES, SELECT, INSERT, UPDATE, DELETE, CREATE, DROP

3 - 2. 권한 확인

SHOW GRANTS FOR '사용자명'@'호스트';

3-3. 사용자 삭제

DROP USER '사용자명'@'호스트';
728x90

'CS > Database' 카테고리의 다른 글

[Index] 구축한 서버 Query분석 및 성능 측정 후 Index 구축  (2) 2024.08.08
Redis는 어떤걸까?  (0) 2024.04.08
728x90

개요

개발을 하거나 면접을 보게 되면 Spring Data Jpa관련 단골 질문으로 가장 많이 나오는 내용이라 생각합니다.

 

그럼 도대체 OSIV가 뭐길래 JPA에서 계속 말이 나오게 되는 걸까? 한번 알아보도록 하겠습니다.


OSIV란

OSIV란 Open Session In View의 약자로써 사실 JPA가 도입되면서 Open EntityManager In View로 개념이 확립되었으나 편의상 지금까지 OSIV라고 부르고 있습니다.

말 그대로 OSIV는 View까지 EntityManager를 열어두는 것을 의미합니다.

 

그럼 JPA에서 EntityManager이라 함은 무엇일까요?

바로 영속성 컨텍스트를 의미합니다.

 

영속성 컨텍스트는 요청이 들어오면 생성되고 사라지는 휘발성 콘텍스트입니다.

해당 콘텍스트는 기본적으로 요청이 들어오면 생성되고, 요청이 끝나면 사라지게끔 되어있는데, 이는 OSIV 설정이 기본으로 True로 설정되어 있어 가능한 일입니다.

 

만약 OSIV가 False로 설정되어 있다면 영속성 콘텍스트는 Transaction범위에 따라 생성되고 Transaction이 끝날 시 사라지게 됩니다.

 

이로써 OSIV가 어떤 설정인지 알았으니 해당 설정이 기능과 성능에 미치는 영향에 대해 알아보겠습니다.


OSIV가 미치는 영향

OSIV 설정은 아래 그림으로 한 번에 확인할 수 있습니다.

위와 같이 데이터를 생성, 수정, 삭제 기능들은 트랜잭션 범위 내에서 작업이 가능합니다.

다만 읽기 기능의 경우 영속성 콘텍스트 범위 내에 있을 경우 읽기가 가능한데,

OSIV는 영속성 콘텍스트를 view까지 늘려 트랜잭션이 아닌 요청의 생존 범위까지 늘리는 것을 의미합니다.

 

즉 생성, 수정, 삭제는 OSIV의 목적은 아니고, 읽기 범위를 늘리는 것이 주목적이라고 보시면 됩니다.

 

그럼 왜! 읽기 범위를 늘리는 것인가?

이는 Lazy Loading과 밀접한 관련이 있습니다.

 

Lazy Loading이란 지연 읽기 기능인데, 처음 데이터베이스에서 조회하는 것이 아닌 추후 해당 데이터를 사용할 때 조회하는 기능입니다.

 

해당 기능을 사용할 때, 영속성 콘텍스트의 생존범위 내에서 로딩하는지가 중요한데 이를 어디까지 허용할 것인지를 조절할 수 있다고 보시면 좋을 것 같습니다.

 

그럼 해당 기능이 왜 성능 향상에 기여할 수 있는가? 영속성 콘텍스트를 유지하는 것 또한 많은 자원을 소모하게 됩니다.

이를 View까지 유지하지 않고 트랜잭션 범위에 맞춰 종료하게 되면 OSIV가 TRUE인 상태보다 좀 더 효율적으로 자원을 사용할 수 있게 됩니다.

 

해당 옵션은 위에서 말했듯 default True인 옵션입니다.

만약 애플리케이션 성능 향상에 있어 관심이 많다면 테스트해보시길 바랍니다.


Reference

https://docs.spring.io/spring-boot/reference/data/sql.html#data.sql.jpa-and-spring-data.open-entity-manager-in-view

 

SQL Databases :: Spring Boot

Spring’s JdbcTemplate and NamedParameterJdbcTemplate classes are auto-configured, and you can autowire them directly into your own beans, as shown in the following example: import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.ste

docs.spring.io

 

 

728x90
728x90

 

입력 예제를 확인해보면 N개의 노드와 N - 1개의 간선 정보가 주어지는 것을 확인해 볼 수 있습니다.

그림으로 표현하면 위와 같이 표현할 수 있으며, 해당 정보를 통해 2번부터 N번 까지 노드들의 부모정보를 출력하면 되는 문제입니다.

 

즉 2번의 부모 4, 3번의 부모 6, 4번의 부모 1, 5번의 부모 3, 6번의 부모 1, 7번의 부모 4번 해서 결과는 4, 6, 1, 3, 1, 4가 나오면 되는 것 입니다.

 

여기서 주의해야하는 점은 해당 트리는 자식 노드가 최대 2개가 주어진다는 말이 없으니 일반 트리이며, 자식 노드는 N개가 될 수 있다는 말입니다.

 

문제 풀이는 생각보다 간단합니다. 

문제에서 트리의 루트를 1번으로 잡는다 하였으니, 1번의 자식들의 부모를 Bfs 탐색을 통해 확인한 다음 2번노드 부터 부모 번호를 출력해주면 됩니다.

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        StringTokenizer st;

        List<Integer>[] arr = new List[N + 1];
        for( int i = 1; i <= N; i++ ) {
            arr[i] = new ArrayList<>();
        }

        for (int i = 1; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            int from = Integer.parseInt(st.nextToken());
            int to = Integer.parseInt(st.nextToken());

            arr[from].add(to);
            arr[to].add(from);
        }

        int[] parent = new int[N + 1];
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        parent[1] = 1;

        while (!queue.isEmpty()) {
            int current = queue.poll();
            for(int child: arr[current]){
                if (parent[child] == 0) {
                    parent[child] = current;
                    queue.add(child);
                }
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 2; i <= N; i++) {
            sb.append(parent[i]);
            sb.append("\n");
        }
        System.out.println(sb);
    }
}
  • 시간복잡도: O(N)
  • 공간복잡도: O(N)
728x90

'알고리즘 문제풀이' 카테고리의 다른 글

[ 백준 - 1068 ] 트리  (0) 2024.11.14
[ 백준 - 9372 ]상준이의 여행  (2) 2024.11.13
[ 백준 - 29701 ] 모스 부호  (0) 2024.11.11
[ 백준 - 25325 ] 학생 인기도 측정  (0) 2024.11.10
[ 백준 - 2358 ] 평행선  (2) 2024.11.09
728x90

모스 부호 문제입니다.

매우 간단한 문제이며, 우선 시간제한과 메모리 제한을 확인해 보겠습니다.

 

입력 예제를 보면  N은 최대 100 이하의 개수가 주어진다 합니다.

그럼 이 문제를 접할 때 최악의 경우 모스부호 100개를 문자열로 변환해야 하는 문제라고 볼 수 있습니다.

 

예제로 준 모스 부호표를 보면 41개를 명시한 표를 통해 답을 만들 수 있습니다.

최악의 경우 100개의 모스 부호를 41개 중 찾기 위해 반복문을 돌린다 하면 4100번 돌려 해결을 할 수 있습니다.

 

다만, 4100번 돌려 답을 찾는 것은 O(n)의 복잡도를 가지게 되지만 41이란 상수는 빅오표기법상 표기를 안 하니 딱히 효율적인 알고리즘은 아니라 생각합니다.

거기서 우리는 Map이라는 자료구조를 활용하여 O(1) 시간복잡도로 모스 부호를 검색할 수 있는 방법을 떠올릴 수 있습니다.

public class Problem29701 {

    public static Map<String, String> initMorseCodeMap() {
        Map<String, String> morseCodeMap = new HashMap<>();
        morseCodeMap.put(".-", "A");
        morseCodeMap.put("-...", "B");
        morseCodeMap.put("-.-.", "C");
        morseCodeMap.put("-..", "D");
        morseCodeMap.put(".", "E");
        morseCodeMap.put("..-.", "F");
        morseCodeMap.put("--.", "G");
        morseCodeMap.put("....", "H");
        morseCodeMap.put("..", "I");
        morseCodeMap.put(".---", "J");
        morseCodeMap.put("-.-", "K");
        morseCodeMap.put(".-..", "L");
        morseCodeMap.put("--", "M");
        morseCodeMap.put("-.", "N");
        morseCodeMap.put("---", "O");
        morseCodeMap.put(".--.", "P");
        morseCodeMap.put("--.-", "Q");
        morseCodeMap.put(".-.", "R");
        morseCodeMap.put("...", "S");
        morseCodeMap.put("-", "T");
        morseCodeMap.put("..-", "U");
        morseCodeMap.put("...-", "V");
        morseCodeMap.put(".--", "W");
        morseCodeMap.put("-..-", "X");
        morseCodeMap.put("-.--", "Y");
        morseCodeMap.put("--..", "Z");
        morseCodeMap.put(".----", "1");
        morseCodeMap.put("..---", "2");
        morseCodeMap.put("...--", "3");
        morseCodeMap.put("....-", "4");
        morseCodeMap.put(".....", "5");
        morseCodeMap.put("-....", "6");
        morseCodeMap.put("--...", "7");
        morseCodeMap.put("---..", "8");
        morseCodeMap.put("----.", "9");
        morseCodeMap.put("-----", "0");
        morseCodeMap.put("--..--", ",");
        morseCodeMap.put(".-.-.-", ".");
        morseCodeMap.put("..--..", "?");
        morseCodeMap.put("---...", ":");
        morseCodeMap.put("-....-", "-");
        morseCodeMap.put(".--.-.", "@");
        return morseCodeMap;
    }

    public static void main(String[] args) throws IOException {
        Map<String, String> morseCodeMap = initMorseCodeMap();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());

        StringBuilder sb = new StringBuilder();
        while(N-- > 0) {
            String morseCode = st.nextToken();
            sb.append(morseCodeMap.get(morseCode));
        }
        System.out.println(sb);
    }
}
  • 시간 복잡도: O(n)
  • 공간 복잡도: O(n)

 

728x90
728x90

이번 문제 또한 매우 간단한 문제입니다.

N명의 학생이 존재하고, 3번째 줄부터 N개 나오는 학생들 이름을 가지고 인기도를 측정하여 출력하면 되는 문제입니다.

 

우선 여기서 봐야 할 점은 인기도 측정 방식정렬인데요.

인기도 측정 방식은 그냥 나오는 학생 이름들을 전부 인기도에 반영하면 되는 간단한 문제이고,

정렬은 주어진 조건에 맞춰 정렬하면 됩니다.

 

정렬의 경우 저는 Stream의 sorted함수를 사용하였는데요. 이때, sort조건을 커스텀함으로써 간단하게 정렬하였습니다.

이후 reduce를 활용하여 StringBuilder에 값들을 담게 했는데요, 이는 String과 달리 StringBuilder는 불변성이 아니어서 메모리 활용 측면에서 훨씬 효율적이라 판단하여 사용하였습니다.

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Map<String, Integer> map = new HashMap<>();
        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine());

        for(int i = 0; i < N; i++) {
            map.put(st.nextToken(), 0);
        }

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            while (st.hasMoreTokens()) {
                String to = st.nextToken();
                map.put(to, map.get(to) + 1);
            }
        }

        StringBuilder answer = map.entrySet().stream().sorted((prev, now) -> {
            if(prev.getValue() != now.getValue()) return now.getValue() - prev.getValue();
            return prev.getKey().compareTo(now.getKey());
        }).reduce(new StringBuilder(),(sb, entry) -> sb.append(entry.getKey()).append(" ").append(entry.getValue()).append("\n"),StringBuilder::append);
        System.out.println(answer);
    }
}
  • 시간복잡도: O(n)
  •  공간복잡도: O(n)
728x90

'알고리즘 문제풀이' 카테고리의 다른 글

[ 백준 - 11725 ] 트리의 부모 찾기  (0) 2024.11.12
[ 백준 - 29701 ] 모스 부호  (0) 2024.11.11
[ 백준 - 2358 ] 평행선  (2) 2024.11.09
[테코테코] 2주차 Stack  (0) 2024.09.23
Hackerrank 사이트 소개  (2) 2024.06.13

+ Recent posts