동적 배열은 배열과 유사하게 요소에 순차적으로 접근할 수 있으며, 필요에 따라 크기를 동적으로 조절할 수 있는 자료 구조이다. 배열의 장점을 유지하면서도, 요소의 추가 및 삭제 연산을 효율적으로 처리할 수 있도록 설계되었다. 동적 배열은 크기 조정에 따른 성능 저하를 상쇄하기 위해, 일반적으로 공간을 미리 확보하는 방식으로 구현되며, 다양한 프로그래밍 언어에서 지원된다.
더 읽어볼만한 페이지
분할 상환 자료 구조 - 스플레이 트리 스플레이 트리는 스플레이 연산을 통해 자가 균형을 유지하며 최근 접근 노드의 빠른 접근을 가능하게 하는 이진 탐색 트리로, 분할 상환 분석 시 평균적으로 O(log n)의 시간 복잡도를 가진다.
분할 상환 자료 구조 - 피보나치 힙 피보나치 힙은 최소 힙 속성을 가진 트리들의 집합으로, 각 노드의 차수를 특정 로그 값 이하로 유지하여 효율적인 삽입, 병합, 최소값 검색 연산을 지원하며, 다익스트라 알고리즘과 같은 그래프 알고리즘의 성능 향상에 활용된다.
배열 - 원형 버퍼 원형 버퍼는 고정 크기의 자료구조로, 처음과 끝이 연결되어 데이터가 순환하며 저장되는 FIFO 방식의 버퍼이며, 큐 구현, 멀티미디어 스트리밍, 데이터 압축 등에 활용된다.
배열 - 할바흐 배열 할바흐 배열은 특정 면에 자기장을 집중시키고 다른 면에서는 상쇄시키는 배열로, 자장 격리에 유용하며 다양한 산업 및 첨단 기술 분야에 응용된다.
동적 배열
개요
자료 구조 유형
배열
접근
Θ(1)
탐색
Θ(n)
삽입
Θ(n)
삭제
Θ(n)
공간 복잡도
Θ(n)
상세 정보
별칭
동적 테이블 확장 가능한 배열 동적 배열
사용 사례
해시 테이블 힙
2. 작동 원리 및 특징
동적 배열은 그 크기가 고정되지 않고, 필요에 따라 자동으로 늘어나거나 줄어드는 배열이다. 이러한 특징은 데이터의 개수를 미리 예측하기 어렵거나, 데이터가 계속 추가/삭제되는 상황에서 유용하다.
동적 배열은 내부적으로 고정 크기 배열을 사용한다. 이 고정 크기 배열에 데이터를 순차적으로 저장하며, 남는 공간은 예비 공간으로 남겨둔다. 만약 데이터를 추가하다가 예비 공간이 부족해지면, 더 큰 크기의 새로운 배열을 할당하고 기존 데이터를 복사하는 방식으로 작동한다.[2] 이 과정을 '크기 조정'이라고 하며, 일반적으로 현재 크기의 두 배로 늘리는 방식을 사용한다.
동적 배열의 끝에 요소를 추가하는 작업은 예비 공간이 충분하다면 상수 시간에 가능하다. 하지만 크기 조정이 필요한 경우에는 모든 요소를 복사해야 하므로 시간이 더 오래 걸린다. 이러한 크기 조정 비용에도 불구하고, 여러 번의 추가 작업을 고려하면 평균적으로 상수 시간에 가까운 성능을 보인다. 이를 상각 상수 시간이라고 한다.[5][6]
동적 배열은 배열과 마찬가지로 특정 위치의 요소를 빠르게 읽거나 변경할 수 있다. 또한, 요소들이 메모리에 연속적으로 저장되기 때문에 캐시 효율성이 높다는 장점도 있다. 하지만 중간에 요소를 삽입하거나 삭제하는 경우에는 뒤쪽의 요소들을 모두 이동시켜야 하므로 시간이 오래 걸린다.
간단한 동적 배열은 고정 크기 배열을 할당하여 구성할 수 있으며, 일반적으로 필요한 요소 수보다 크게 할당한다. 동적 배열의 요소는 기본 배열의 시작 부분에 연속적으로 저장되며, 나머지 위치는 예약되거나 사용되지 않는다. 동적 배열의 끝에 요소를 추가하는 것은 예약된 공간을 사용하여 상수 시간에 가능하다. 모든 공간이 소모된 경우, 기본 고정 크기 배열의 크기를 늘려야 한다. 일반적으로 크기 조정은 새 배열을 할당하고 원래 배열에서 각 요소를 복사해야 하므로 비용이 많이 든다. 크기 조정이 필요하지 않으므로 동적 배열의 끝에서 요소를 상수 시간에 제거할 수 있다.[2]
동적 배열 내용에서 사용되는 요소의 수는 ''논리적 크기'' 또는 ''크기''이며, 기본 배열의 크기는 동적 배열의 ''용량'' 또는 ''물리적 크기''라고 하며, 이는 데이터를 재배치하지 않고 가능한 최대 크기이다.[2]
동적 배열은 크기를 두 배로 늘리는 등 큰 폭으로 크기를 조정하며, 미래 확장을 위해 예약된 공간을 사용한다. ''n''개의 요소가 삽입되면, 용량은 기하 수열을 형성한다. 배열을 임의의 상수 비율 ''a''로 확장하면 ''n''개의 요소를 삽입하는 데 전체적으로 ''O''(''n'') 시간이 걸리도록 보장하며, 이는 각 삽입이 상각 상수 시간이 걸린다는 의미이다. 많은 동적 배열은 크기가 용량의 특정 임계값, 예를 들어 30% 미만으로 떨어지면 기본 스토리지를 일부 할당 해제한다.
동적 배열의 성장 인자는 공간-시간 트레이드 오프 및 메모리 할당기 자체에 사용되는 알고리즘을 포함한 여러 요인에 따라 달라진다. 성장 인자 ''a''에 대해 삽입 연산당 평균 시간은 약 ''a''/(''a''−1)이며, 낭비되는 셀 수는 (''a''−1)''n''로 상한을 둔다. 황금비율과 1.5 값을 포함하여 이상적인 성장 인자 값에 대한 다양한 논의가 있어왔다.[4] 그러나 많은 교과서에서는 단순성과 분석 목적으로 ''a'' = 2를 사용한다.[5][6]
동적 배열은 크기를 조절할 때, 일반적으로 현재 크기의 두 배로 늘리는 방식을 사용한다. 이는 잦은 크기 조정으로 인한 비용을 줄이고, 미래에 추가될 요소를 위한 공간을 확보하기 위함이다.[5][6]
예를 들어, ''n''개의 요소를 동적 배열의 끝에 추가하면, 배열의 용량은 기하 수열 형태로 증가한다. 배열의 크기를 상수 비율 ''a''로 늘리면, ''n''개의 요소를 추가하는 데 걸리는 총 시간은 ''O''(''n'')이 된다. 즉, 각 요소 추가 작업은 상각 상수 시간이 걸린다.
동적 배열의 성장 인자는 공간-시간 트레이드 오프 등 여러 요인에 따라 결정된다. 성장 인자 ''a''에 대해 삽입 연산당 평균 시간은 약 ''a'' / (''a'' - 1)이며, 낭비되는 셀 수는 (''a'' - 1)''n''을 상한으로 한다. 메모리 할당 알고리즘에 따라서는 성장 인자 값 ''a'' = 2와 같은 값은 상당한 양의 메모리가 여전히 사용 가능하더라도 동적 배열 확장이 메모리 부족으로 실행될 수 있다.[3] 황금비율과 1.5 값을 포함하여 이상적인 성장 인자 값에 대한 다양한 논의가 있어왔다.[4] 그러나 많은 교과서에서는 단순성과 분석 목적으로 ''a'' = 2를 사용한다.[5][6]
갭 버퍼는 동적 배열과 유사하지만 동일한 임의 위치 근처에서 삽입 및 삭제 작업을 효율적으로 수행할 수 있게 해준다. 일부 데크 구현은 배열 데크를 사용하는데, 이는 한쪽 끝뿐만 아니라 양쪽 끝에서도 분할 상환 상수 시간으로 삽입/제거를 할 수 있게 해준다.
Goodrich[16]는 배열의 어느 곳에서나 삽입 및 삭제에 대해 ''O''(''n''1/''k'') 성능을 제공하고, ''O''(''k'')의 접근 및 설정 시간을 가지는 '계층적 벡터'(''tiered vectors'')라는 동적 배열 알고리즘을 제시했다. 여기서 ''k'' ≥ 2는 상수 매개변수이다.
1999년 논문[18]에서 Brodnik et al.은 매 시점 ''n''개 요소에 대해 ''n''1/2 공간만 낭비하는 계층화된 동적 배열 데이터 구조를 설명하고, 모든 동적 배열이 분할 상환 상수 시간을 유지하려면 이만큼의 공간을 낭비해야 한다는 하한을 증명했다. 또한 버퍼를 확장 및 축소하는 것이 분할 상환뿐만 아니라 최악의 경우에도 상수 시간이 걸리는 변형을 제시했다.
단순하게 크기를 조절할 수 있는 배열은 배열에 추가되는 모든 항목에 대해 realloc을 호출하여 배열에 포함된 모든 데이터에 정확히 충분한 할당된 크기를 유지한다. 이는 C에서 크기를 조절할 수 있는 배열을 구현하는 가장 간단한 방법이지만, 메모리를 낭비하지 않는 대신 배열의 끝에 추가하는 데 항상 Θ(''n'') 시간이 걸린다.[17][20][21][22][23]
선형으로 증가하는 배열은 배열의 크기를 조정할 때마다 Θ(1) 공간을 미리 할당("낭비")하므로 단순하게 크기를 조절할 수 있는 배열보다 훨씬 빠르다. 배열의 끝에 추가하는 데 여전히 Θ(''n'') 시간이 걸리지만, 상수가 훨씬 작다.
단순하게 크기를 조절할 수 있는 배열과 선형으로 증가하는 배열은 공간이 제한된 응용 프로그램에서 많은 소규모의 크기를 조절할 수 있는 배열이 필요할 때 유용할 수 있으며, 지수적으로 증가하는 동적 배열로 이어지는 교육적인 예제로도 흔히 사용된다.[24]
3. 1. 갭 버퍼 (Gap Buffer)
갭 버퍼는 동적 배열과 유사하지만 동일한 임의 위치 근처에서 클러스터링된 삽입 및 삭제 작업을 효율적으로 수행할 수 있다. 일부 데크 구현은 배열 데크를 사용하는데, 이는 한쪽 끝뿐만 아니라 양쪽 끝에서도 분할 상환 상수 시간으로 삽입/제거를 할 수 있다.[16]
3. 2. 계층적 벡터 (Tiered Vectors)
Goodrich[16]는 배열의 어느 곳에서나 삽입 및 삭제에 대해 ''O''(''n''1/''k'') 성능을 제공하고 ''O''(''k'')를 얻고 설정하는 '계층적 벡터'(''tiered vectors'')라는 동적 배열 알고리즘을 제시했으며, 여기서 ''k'' ≥ 2는 상수 매개변수이다.
해시 배열 트리(HAT)는 1996년 Sitarski에 의해 발표된 동적 배열 알고리즘이다.[17] 해시 배열 트리는 배열의 요소 수가 ''n''일 때 ''n''1/2 차수의 저장 공간을 낭비한다. 이 알고리즘은 해시 배열 트리의 끝에 일련의 객체를 추가할 때 ''O''(1) 분할 상환 성능을 갖는다.
1999년 논문[18]에서 Brodnik et al.은 매 시점 ''n''개 요소에 대해 ''n''1/2 공간만 낭비하는 계층화된 동적 배열 데이터 구조를 설명하고, 모든 동적 배열이 분할 상환 상수 시간을 유지하려면 이만큼의 공간을 낭비해야 한다는 하한을 증명했다. 또한 버퍼를 확장 및 축소하는 것이 분할 상환뿐만 아니라 최악의 경우에도 상수 시간이 걸리는 변형을 제시했다.
Bagwell (2002)[19]은 동적 배열을 구현하는 데 적용할 수 있는 VList 알고리즘을 제시했다.
3. 3. 해시 배열 트리 (Hashed Array Tree, HAT)
해시 배열 트리(HAT)는 1996년 시타르스키(Sitarski)가 발표한 동적 배열 알고리즘이다.[17] 해시 배열 트리는 배열의 요소 수가 ''n''일 때 ''n''1/2 크기의 저장 공간을 낭비한다. 이 알고리즘은 해시 배열 트리의 끝에 일련의 객체를 추가할 때 ''O''(1) 분할 상환 성능을 갖는다.
3. 4. VList
배그웰(Bagwell, 2002)은 동적 배열을 구현하는 데 사용할 수 있는 VList 알고리즘을 제시했다.[19]
4. 언어 지원
C++의 `std::vector`와 러스트의 `std::vec::Vec`은 동적 배열의 구현이며, 자바 API[26]와 .NET Framework에서 제공되는 `ArrayList`[25] 클래스도 동적 배열의 구현이다.[27][28]
.NET Framework 버전 2.0에서 제공되는 제네릭 `List<>` 클래스 또한 동적 배열로 구현된다. 스몰토크의 `OrderedCollection`은 동적 시작 및 종료 인덱스를 가진 동적 배열로, 첫 번째 요소 제거도 O(1)이다.
파이썬의 `list` 데이터 타입 구현은 동적 배열이며, 증가 패턴은 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ...[29]이다.
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.