Today's Codekata
// 귤 고르기
class Solution {
public int solution(int k, int[] tangerine) {
Map<Integer, Integer> map = new HashMap<>();
// 귤 크기별 개수 세기
for (int size : tangerine) {
map.put(size, map.getOrDefault(size, 0) + 1);
}
// 개수 내림차순
List<Integer> list = new ArrayList<>(map.values());
list.sort(Collections.reverseOrder());
int sum = 0;
int answer = 0;
// 많이 나온 크기부터 더해서 k개 이상 될 때까지 반복
for (Integer i : list) {
sum += i;
answer++;
if (sum >= k) {
break;
}
}
return answer;
}
}
귤의 크기가 담긴 배열 `tangerine`에서 `k`개의 귤을 선택할 때, 서로 다른 크기 종류의 수를 최소화하는 문제이다.
자료구조 정리
자료구조는 데이터를 어떤 방식으로 저장하고 관리할지 결정하는 설계다.
효율적인 자료구조를 선택하면 코드의 속도, 메모리 사용량, 유지보수성이 달라진다.
배열 (Array)
가장 기본적인 자료구조로, 고정된 크기의 연속된 메모리 공간에 데이터를 순서대로 저장한다.
인덱스를 통해 빠르게 접근할 수 있지만 크기를 바꿀 수 없고, 삽입과 삭제가 비효율적이다.
int[] numbers = {1, 2, 3, 4};
System.out.println(numbers[2]); // 3
배열은 크기가 변하지 않는 정적 데이터에 적합하다.
예를 들어 요일, 월별 데이터, 시험 점수표 같은 고정 길이 데이터를 다룰 때 유용하다.
리스트 (List)
배열의 한계를 개선한 자료구조로, 크기가 가변적이다.
대표적인 구현체는 `ArrayList`로 내부적으로 배열을 사용해 인덱스 접근이 빠르다는 장점이 있다.
List<String> names = new ArrayList<>();
names.add("Java");
names.add("Spring");
names.remove("Java");
System.out.println(names);
리스트는 데이터의 개수가 유동적일 때 가장 많이 사용된다.
대부분의 컬렉션 로직의 기본이 되는 구조이기도 하다.
맵 (Map)
`Map`은 키(Key)와 값(Value)의 쌍으로 데이터를 저장한다.
같은 키를 중복해서 저장할 수 없으며, 키를 이용해 빠르게 값을 검색할 수 있다.
가장 많이 사용하는 구현체는 `HashMap`이다.
Map<String, Integer> map = new HashMap<>();
map.put("apple", 3);
map.put("banana", 2);
System.out.println(map.get("apple")); // 3
맵은 데이터의 식별자가 명확할 때 강력하다.
예를 들어 사용자 ID와 이름, 상품 코드와 재고 수량을 관리할 때 자주 사용된다.
셋 (Set)
`Set`은 중복을 허용하지 않는 자료구조다.
한 번 추가된 데이터가 다시 들어와도 저장되지 않는다.
대표적인 구현체는 `HashSet`, `TreeSet`이다.
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Java");
System.out.println(set.size()); // 1
셋은 “고유한 값”을 관리할 때 적합하다.
예를 들어 중복 없는 태그 목록, 로그인한 사용자 ID 목록 등을 관리할 때 유용하다.
스택 (Stack)
스택은 나중에 들어온 데이터가 먼저 나가는 LIFO(Last In, First Out) 구조다.
예를 들어 웹 브라우저의 뒤로가기 기능이나 재귀 호출의 실행 순서가 이에 해당한다.
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.pop(); // 20 제거
스택은 되돌리기, 수식 계산, 괄호 검사 같은 이전 상태로 돌아가는 기능을 구현할 때 자주 쓰인다.
큐 (Queue)
큐는 먼저 들어온 데이터가 먼저 나가는 FIFO(First In, First Out) 구조다.
은행 대기열, 작업 처리 순서, 비동기 메시지 처리 등 순서가 중요한 상황에서 쓰인다.
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
System.out.println(queue.poll()); // A
큐는 순차적으로 데이터를 처리해야 하는 경우에 매우 유용하다.
예를 들어 서버 요청 처리, 메시지 큐 시스템, 작업 스케줄링 로직 등이 큐의 개념을 활용한다.
마치며
배열은 가장 단순하고, 리스트는 가장 유연하다.
맵은 키 중심의 빠른 탐색에 강하고, 셋은 중복 없는 데이터 관리에 적합하다.
스택은 되돌리기 구조에, 큐는 순차적 처리에 최적화되어 있다.
데이터의 성격에 따라 구조를 고를 수 있는 개발자는 효율적인 코드를 작성할 수 있다.