hoon's bLog

[Java] 프로그래머스 신고 결과 받기 자바 본문

코딩테스트/프로그래머스

[Java] 프로그래머스 신고 결과 받기 자바

개발한기발자 2022. 8. 2. 16:11
반응형

문제 출처 : https://programmers.co.kr/learn/courses/30/lessons/92334

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


응?! 정확성 테스트?!

뭔가 프로그램이 모든 케이스를 충족하면서,

빨리 돌아가야 한다는 건가?!!??!

일단 가보자구~~~

[나의 풀이]

- 신고인과 피신고인 정보를 담을 map과 id별 신고 횟수 정보를 담을 idxMap 선언

- for문으로 report의 정보를 split 메서드로 쪼게 map 안에 set에 데이터 추가

- map에 담긴 set 데이터를 다시 send set에 담아, for문을 통해 idxMap의 이름을 가져와 해당 index로 배열 구성

import java.util.*;
class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];
        Map<String, HashSet<String>> map = new HashMap<>();
        Map<String, Integer> idxMap = new HashMap<>();
        
        //id_list의 이름을 name 변수에 저장하여 map과 idxMap에 저장
        for (int i = 0; i < id_list.length; i++) {
            String name = id_list[i];
            map.put(name, new HashSet<>());
            idxMap.put(name, i);
        }
        
        //report 배열의 인덱스를 split으로 쪼개고, map의 신고당한 유저(to)에 선언된
        //Set에 신고한 유저(from) 추가
        for (String s : report) {
            String[] str = s.split(" ");	//"muzi frodo"
            String from = str[0];			//str[0] = "muzi"
            String to = str[1];				//str[1] = "frodo"
            map.get(to).add(from);
        }
        
        //map에서 id_list 배열의 값을 키로 하여 값을 가져와
        //send Set에 담아주고, 2회 이상 등록된 유저의 인덱스를
        //idxMap에서 가져와 answer 배열의 값을 +!
        for (int i = 0; i < id_list.length; i++) {
            HashSet<String> send = map.get(id_list[i]);
            if (send.size() >= k) {
                for (String name : send) {
                    answer[idxMap.get(name)]++;
                }
            }
        }
        return answer;
    }
}

for문으로 코딩을 하면 역시 직관적이지만,

3번이나 따로따로 도는 건 역시 미관상 보기도 안 좋고,

그다지 유쾌한 방법은 아니다ㅠㅠ

이번 문제는 좋은 풀이들이 많은 것 같아 좀 가져와봤다!

그럼 고수님들의 풀이를 GoGo~~

[다른 사람의 풀이-1]

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
    	//report 배열에 값을 꺼내 List타입으로 변환하여 저장
        List<String> list = Arrays.stream(report).distinct().collect(Collectors.toList());
        HashMap<String, Integer> count = new HashMap<>();
        
        //list의 값을 꺼내 인덱스 1번째 값(신고당한 유저)을 target에 저장후
        //count map에 target 값 put
        //getOrDefault : 찾는 key가 존재한다면 찾는 key의 value를 반환, 없거나 null이면 default 값을 반환
        for (String s : list) {
            String target = s.split(" ")[1];
            count.put(target, count.getOrDefault(target, 0) + 1);
        }
        
        //id_list 배열의 원소들로 stream을 생성 후 요소들을
        //_user에 담아 user에 저장하여
        //그 값을 공백이 뒤에 붙은 user 값을 list stream으로 생성후 reportList에 저장한 값을
        //stream으로 생성 후 count map에 k이상인 user를 count한 값 return 후
        //mapToInt로 int로 변환한 요소들을 배열로 retrun
        return Arrays.stream(id_list).map(_user -> {
            final String user = _user;
            List<String> reportList = list.stream().filter(s -> s.startsWith(user + " ")).collect(Collectors.toList());
            return reportList.stream().filter(s -> count.getOrDefault(s.split(" ")[1], 0) >= k).count();
        }).mapToInt(Long::intValue).toArray();
    }
}

stream이 확실히 가독성도 좋아 보이고,

멋있어 보이는 건 사실이지만,

굳이 이렇게까지?

라는 생각이 드는 코드였ㄷ...?(절대 Stream을 잘 못써서가 아님ㅠㅠ)

굳이 이렇게까지?

[다른 사람의 풀이-2]

import java.util.*;

class Solution {
    public int[] solution(String[] id_list, String[] report, int k) {
        int[] answer = new int[id_list.length];
        ArrayList<User> users = new ArrayList<>();
        HashMap<String,Integer> suspendedList = new HashMap<>();
        HashMap<String,Integer> idIdx = new HashMap<String,Integer>();
        int idx = 0;
        
        //각 유저에 대한 고유 index를 setting후 users ArrayList에 User 객체 추가
        for(String name : id_list) {
            idIdx.put(name, idx++);
            users.add(new User(name));
        }
        
        //users의 idIdx.get(str[0])번째 index에 reportList에 피신고인 추가
        //users의 idIdx.get(str[1])번째 index에 reportedList에 신고인 추가
        for(String re : report){
            String[] str = re.split(" ");
            users.get(idIdx.get(str[0])).reportList.add(str[1]);
            users.get(idIdx.get(str[1])).reportedList.add(str[0]);
        }
        
        //reportedList의 크기가 주어진 k와 같거나 크면 suspendedList의 피신고인 put
        for(User user : users){
            if(user.reportedList.size() >= k)
                suspendedList.put(user.name,1);
        }
        
        //answer 배열 각 index에 해당하는 유저의 신고당한 횟수를 count up
        for(User user : users){
             for(String nameReport : user.reportList){
                 if(suspendedList.get(nameReport) != null){
                     answer[idIdx.get(user.name)]++;
                 }
             }
        }
        return answer;
    }
}

class User{
    String name;                        //해당 유저명
    HashSet<String> reportList;		//피신고인 리스트
    HashSet<String> reportedList;	//신고인 리스트
    public User(String name){
        this.name = name;
        reportList = new HashSet<>();
        reportedList = new HashSet<>();
    }
}

진짜 박수가 절로 나오는 풀이...

언뜻 보면 길어 보일 수 있겠지만,

User라는 클래스 객체별도로 선언함으로써,

내부 필드의 데이터들을 한 번에 다루는,

전형적인 객체지향적 풀이라고 볼 수 있다...

속도 측면에서도, 설계 측면에서도 매우 훌륭한 코드로

앞으로 우리가 실무에서 지향해야 할 풀이라고 해도 과언이 아니다...

 

[정리]

- 핵심은 반복문을 통해 List, Map, Set 등을 활용하여 데이터 정리

- 각 유저별 고유의 index를 부여하는 풀이는 자주 나오니 꼭 기억할 것!

- 클래스 객체를 통해 반복되는 변수의 선언 최소화!

 

개인 피셜 난이도 : ★☆☆

간단해 보였지만, 결국 반복문을 잘 활용해야 했던 문제,

2번 풀이와 같이 객체지향적 방식의 코딩을 좀 더 공부를 해야겠다.

코딩 테스트 문제들은 대부분 반복문과 조건문에 의해,

풀이가 되는 건 사실이지만,

2번 풀이와 같은 체계적인 설계를 구사할 때,

현업에서 좀 더 창의적으로 코드를 구현할 수

있을 것 같다.

 

개인 피셜 난이도는 낮았지만,

생각이 많아지는 고수님들의

풀이들이었다...ㅎㅎㅎ

 

끝!

728x90
반응형