[프로그래머스] 96. 배열의 길이를 2의 거듭제곱으로 만들기

최재원's avatar
Oct 11, 2025
[프로그래머스] 96. 배열의 길이를 2의 거듭제곱으로 만들기
💡

문제 설명

정수 배열 arr이 매개변수로 주어집니다. arr의 길이가 2의 정수 거듭제곱이 되도록 arr 뒤에 정수 0을 추가하려고 합니다. arr에 최소한의 개수로 0을 추가한 배열을 return 하는 solution 함수를 작성해 주세요.
💡

제한사항

  • 1 ≤ arr의 길이 ≤ 1,000
  • 1 ≤ arr의 원소 ≤ 1,000

입출력 예

arr
result
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 0, 0]
[58, 172, 746, 89]
[58, 172, 746, 89]

입출력 예 설명

입출력 예 #1
  • 예제 1번의 arr의 길이는 6입니다. arr의 길이를 2의 정수 거듭제곱으로 만드는 방법은 0을 2개, 10개, 26개,..., 추가하는 방법이 있고 그중 최소한으로 0을 추가하는 방법은 2개를 추가하는 것입니다. 따라서 [1, 2, 3, 4, 5, 6, 0, 0]을 return 합니다.
입출력 예 #2
  • 예제 2번의 arr의 길이는 4이고 이미 2의 정수 거듭제곱입니다. 따라서 뒤에 0을 추가하지 않아도 되므로 [58, 172, 746, 89]를 return 합니다.
 

코드 - 시간 초과 실

import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.stream.Collectors; class Solution { public int[] solution(int[] arr) { int arrLength = arr.length; int diffLength = 0; int pow = 0; while(true) { int value = (int) Math.pow(2, pow); if(arrLength == value) { break; } else if(arrLength < value) { diffLength = value - arrLength; } else { pow++; } } List<Integer> result = Arrays.stream(arr) .boxed() .collect(Collectors.toList()); for(int i = 0; i < diffLength; i++) { result.add(0); } return result.stream() .mapToInt(i->i) .toArray(); } }
notion image
  • 빨간줄 while 문이 시간 초과됨
  • Math.pow()double 반환: 이 메서드는 정수 거듭제곱이 아닌 부동 소수점(실수, double) 연산을 수행하도록 설계되었습니다.
  • CPU 비용: CPU는 정수 연산(덧셈, 곱셈, 비트 연산)보다 부동 소수점 연산을 처리하는 데 훨씬 더 많은 시간과 자원을 소모합니다. 이 복잡한 계산을 루프가 돌 때마다 반복하는 것은 엄청난 성능 저하를 초래합니다.
 

코드 - 비트 연산자를 사용해서 해결

import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.stream.Collectors; class Solution { /** * 배열의 길이를 2의 거듭제곱으로 맞추기 위해 0을 채워넣는 메서드 (패딩) * @param arr 입력 배열 * @return 길이가 2의 거듭제곱인 새로운 배열 */ public int[] solution(int[] arr) { int arrLength = arr.length; // 1. [해결책 1] Math.pow() 대신 비트 연산으로 2의 거듭제곱 목표 길이 찾기 (최적화) // targetLength를 1로 시작하여 arrLength보다 크거나 같아질 때까지 2배씩 곱합니다. int targetLength = 1; while (targetLength < arrLength) { targetLength <<= 1; // targetLength *= 2 와 동일. 가장 빠르고 효율적입니다. } int diffLength = targetLength - arrLength; // 2. [해결책 2] 안전한 가변 리스트 생성 (컬렉션 안정성 확보) // Collectors.toCollection(ArrayList::new)를 사용하여 'add'가 확실하게 가능한 ArrayList를 만듭니다. List<Integer> resultList = Arrays.stream(arr) .boxed() .collect(Collectors.toCollection(ArrayList::new)); // 3. 부족한 길이만큼 0 추가 (패딩) for (int i = 0; i < diffLength; i++) { resultList.add(0); } // 4. List<Integer>를 int[]로 변환하여 반환 // mapToInt와 toArray는 깔끔하고 효율적인 방식입니다. return resultList.stream() .mapToInt(i -> i) .toArray(); } }
notion image
정수형 데이터에서 2n을 구하는 것은 컴퓨터 아키텍처에서 가장 기본적이고 빠른 연산에 해당합니다.
<<= 1 은 CPU의 레지스터 내에서 비트를 왼쪽으로 한 칸 이동시키는 단일 기계어 명령으로 처리되지만,
Math.pow() 는 복잡한 함수 호출, 실수 계산, 정수 변환을 거치므로 성능 차이가 매우 큽니다.
 

💡 비트 연산 코드 동작 원리 설명

코드 개요
int targetLength = 1; // 2의 0승 (2^0)으로 시작 while (targetLength < arrLength) { targetLength <<= 1; // targetLength를 2배로 만듭니다. (왼쪽 시프트) }
1. 초기값 설정: int targetLength = 1;
  • targetLength는 2의 거듭제곱(2n)을 저장할 변수입니다.
  • 초기값 1은 20과 같습니다.
2. 반복 조건: while (targetLength < arrLength)
  • 루프는 현재의 targetLength가 우리가 원하는 길이(arrLength)보다 작을 때만 계속됩니다.
  • targetLengtharrLength넘어서거나 같아지면 루프가 종료됩니다.
3. 핵심 연산: targetLength <<= 1; (왼쪽 시프트)
이 부분이 가장 중요합니다.
  • << (왼쪽 시프트 연산자): 정수의 모든 비트를 왼쪽으로 이동시킵니다.
  • targetLength <<= 1의 의미: targetLength의 모든 2진 비트를 왼쪽으로 1칸 이동시킵니다.
  • 수학적 효과: 컴퓨터에서 숫자의 비트를 왼쪽으로 한 칸 이동시키는 것은 그 숫자에 2를 곱하는 것과 정확히 같습니다.
2진수 (8비트 예시)
10진수
연산
0000 0001
1 (20)
초기값
0000 0010
2 (21)
1 << 1
0000 0100
4 (22)
2 << 1
0000 1000
8 (23)
4 << 1
결론적으로,while 루프는 targetLength1부터 시작하여 2, 4, 8, 16, 32... 순서로 계속 2배씩 늘려가면서, 마침내 arrLength보다 크거나 같은 지점에서 멈추게 됩니다.
 
Share article

jjack1