배열에서 여러 요소를 제거하고 남은 요소들을 새로운 배열에 올바르게 재배치하는 작업은 C++ 프로그래밍에서 자주 발생하는 메모리 조작 문제입니다. 예를 들어, 다음과 같은 동적 배열이 있다고 가정합시다:
int* original = new int[10]{3, 5, 1, 11, 99, 66, 22, 2, 8, 6};
여기서 인덱스 0과 5에 위치한 값(각각 3과 66)을 제거하고, 나머지 8개의 요소로 구성된 새 배열을 생성해야 한다면 어떻게 해야 할까요? 아래는 이를 구현하는 방법과 핵심 개념인 오프셋(offset)의 의미를 설명합니다.
알고리즘 설계
동적 배열에서 특정 인덱스의 요소를 제거하려면 다음 단계를 따릅니다:
- 제거 후 남을 요소 수만큼의 새 배열을 동적 할당합니다 (이 경우 10 - 2 = 8).
- 기존 배열을 순회하면서, 제거 대상이 아닌 요소만 새 배열에 복사합니다.
- 복사 과정에서 인덱스 불일치를 보정하기 위해 오프셋 값을 사용합니다.
- 기존 배열의 메모리를 해제하고, 필요 시 포인터를 새 배열로 재할당합니다.
코드 구현 및 분석
#include <iostream>
using namespace std;
int main() {
// 원본 배열 생성
int* original = new int[10]{3, 5, 1, 11, 99, 66, 22, 2, 8, 6};
// 두 개의 요소 제거 → 새 배열 크기는 8
int* result = new int[8];
// 오프셋 초기화: 건너뛴 요소 수를 기록
int skipCount = 0;
for (int idx = 0; idx < 10; ++idx) {
// 인덱스 0 또는 5인 경우 건너뜀
if (idx == 0 || idx == 5) {
skipCount++; // 건너뛴 횟수 증가
continue;
}
// 현재 요소를 새 배열의 올바른 위치에 저장
result[idx - skipCount] = original[idx];
}
// 이전 배열 메모리 해제
delete[] original;
// 포인터 재할당 (선택 사항)
original = result;
// 결과 출력
for (int i = 0; i < 8; ++i) {
cout << result[i] << endl;
}
return 0;
}
오프셋(skipCount)의 역할
skipCount 변수는 제거된 요소의 누적 개수를 추적합니다. 기존 배열의 인덱스 idx를 사용할 때, 실제로 새 배열에 저장될 위치는 idx - skipCount입니다. 이는 다음과 같은 이유에서 중요합니다:
- 인덱스 0에서 첫 번째 요소가 제거되면, 이후 모든 요소는 왼쪽으로 한 칸씩 당겨져야 합니다.
- 두 번째 제거(인덱스 5)에서는 이미 1칸의 오프셋이 존재하므로, 그 뒤 요소들은 총 2칸 앞당겨집니다.
- 예: 원래 인덱스 6에 있던 값 22는
6 - 2 = 4위치에 저장됩니다.
이러한 방식은 조건부로 요소를 건너뛸 때도 정확한 위치 계산을 보장하며, 추가적인 임시 저장소 없이 선형 시간 내에 처리할 수 있습니다.
출력 결과
위 코드를 실행하면 다음과 같은 출력이 나타납니다:
5
1
11
99
22
2
8
6
원본 배열에서 인덱스 0(값 3)과 인덱스 5(값 66)가 성공적으로 제거되었으며, 나머지 요소들이 순서를 유지한 채 새 배열에 저장된 것을 확인할 수 있습니다.
메모리 관리 주의사항
new[]로 할당한 메모리는 반드시delete[]로 해제해야 합니다.- 포인터 재할당 후에도 여전히 유효한 참조를 유지하도록 주의해야 하며, 중복 해제나 메모리 누수가 발생하지 않도록 해야 합니다.