52. [Java] 무작위로 K개의 수 뽑기
https://school.programmers.co.kr/learn/courses/30/lessons/181859
- 무작위로 뽑은 수들이
arr
배열에 순서대로 주어짐 - 이 수들을 앞에서부터 확인하면서 중복 없이 k개까지만 저장
- 만약 중복을 제거한 결과가
k
개보다 작으면, 남는 자리는-1
로 채움 - 최종적으로 길이가 정확히 k인 배열을 만들어 반환
- 1 ≤ arr의 길이 ≤ 100,000
- 0 ≤ arr의 원소 ≤ 100,000
- 1 ≤ k ≤ 1,000
정답코드 (List)
import java.util.*;
class Solution {
public int[] solution(int[] arr, int k) {
List<Integer> result = new ArrayList<>(); // 결과를 저장할 리스트
Set<Integer> seen = new HashSet<>(); // 중복 체크를 위한 Set
// arr 배열을 앞에서부터 하나씩 확인
for (int num : arr) {
// 아직 나온 적 없는 숫자라면
if (!seen.contains(num)) {
seen.add(num); // 중복 체크용 Set에 추가
result.add(num); // 결과 리스트에도 추가
// k개를 모두 모았으면 더 이상 반복할 필요 없음
if (result.size() == k) break;
}
}
// 만약 결과 리스트가 k개보다 작으면
// 부족한 부분을 -1로 채움
while (result.size() < k) {
result.add(-1);
}
// List<Integer> → int[]
return result.stream().mapToInt(Integer::intValue).toArray();
}
}
- 입력:
정수 배열 arr
, 정수k
- 출력: 중복 없이 k개의 수가 들어간 배열
(k개가 안 되면-1
로 채운 길이 k 배열)
정답코드 (Array, 내가 푼 코드)
import java.util.*;
class Solution {
public int[] solution(int[] arr, int k) {
int[] answer = new int[k]; // k칸짜리 배열 생성
Arrays.fill(answer, -1); // 일단 전부 -1로 초기화
Set<Integer> used = new HashSet<>(); // 중복 체크용 Set
int idx = 0; // answer에 값을 채워넣을 인덱스
for (int num : arr) {
if (!used.contains(num)) {
used.add(num); // 중복 체크용으로 추가
answer[idx++] = num; // 배열에 값 넣고 인덱스 증가
if (idx == k) break; // k칸 모두 채웠으면 중단
}
}
return answer;
}
}
- k칸짜리 배열 만들기
- 배열 모두 -1로 초기화
-
arr
에서 중복 없이 앞에서부터 수를 골라 채우기
→ 중복 아닌 숫자만 set에 추가
→ 배열에 값 넣고 인덱스 추가
→ k칸 모두 채웠으면 중단
idx 범위
if (idx == k) break; // k칸 모두 채웠으면 중단
if (idx == k) break;
가 왜 k - 1
이 아닌지 헷갈렸다.
idx가 배열 인덱스로 쓰였기 때문에 0부터 시작에서 k-1까지라고 착각하기 쉽다.
하지만 실제로 idx
는 인덱스의 역할이 아니라 "몇 개 넣었는지"를 세는 변수이다.
즉, idx == k라는 건 k개의 값을 이미 다 넣었다는 뜻이다.
int[] answer = new int[k]; // 길이 k인 배열
int idx = 0; // 0개 넣은 상태로 시작
값을 넣을 때:
answer[idx++] = num; // num을 넣고 idx는 1 증가
- 첫 번째 값 넣고 → idx == 1
- 두 번째 값 넣고 → idx == 2
- 세 번째 값 넣고 → idx == 3
그리고 k == 3이면, 값을 3개 넣은 시점이 idx == k이다.
그럼 더는 넣으면 안 되니까 break한다.
idx == k
는 "k개 넣었으니 그만 넣자"는 뜻 → ✔️ 맞는 코드idx == k - 1
이면 "마지막 인덱스 전까지만 넣자"는 뜻 → ❌ 틀린 기준
Stream을 사용해서 푼 코드 (최석현 님)
import java.util.Arrays;
import java.util.stream.IntStream;
class Solution {
public int[] solution(int[] arr, int k) {
return IntStream.concat(
Arrays.stream(arr).distinct(), // 1. arr 배열에서 중복 제거된 스트림 생성
IntStream.range(0, k).map(i -> -1) // 2. -1로 채울 k개 크기의 스트림 생성
).limit(k) // 3. 두 스트림을 합친 후, 최대 k개까지만 자름
.toArray(); // 4. 최종 결과를 int[] 배열로 변환하여 반환
}
}
Arrays.stream(arr).distinct()
arr
배열을 스트림으로 만들고, 중복되는 숫자는 한 번만 나오도록 필터링한다.
IntStream.range(0, k).map(i -> -1)
- 0부터 k-1까지 숫자를 순서대로 만드는 스트림을 만들고,
- 각 숫자를 모두
-1
로 바꿔서 길이가 k인-1
스트림을 만든다.
IntStream.concat(...)
- 중복 제거된
arr
스트림과-1
로 채운 스트림을 앞뒤로 이어붙여서 하나의 스트림으로 만든다.
- 중복 제거된
.limit(k)
- 스트림 길이를 최대 k개로 제한해서, 만약 중복 제거된 수가 k보다 적으면 나머지를
-1
로 채우고, - 많으면 처음 k개까지만 가져오도록 한다
- 스트림 길이를 최대 k개로 제한해서, 만약 중복 제거된 수가 k보다 적으면 나머지를
.toArray()
- 스트림을 다시 정수 배열로 변환해서 반환하면 끝