글과 사진, 그리고 이야기

IE & SWCON/Data Structure (C++)

연속된 자료 구조와 연결된 자료구조

뱃놀이가자 2024. 2. 7. 16:55
728x90

https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=257288899

 

코딩 테스트를 위한 자료 구조와 알고리즘 with C++

C++ 자료 구조부터 그리디 알고리즘, 분할 정복 알고리즘, 그래프 알고리즘, 동적 계획법과 같은 다양한 알고리즘을 설명한다. 또한, 전통적인 자료 구조와 C++ STL 클래스 구현 사이의 관계를 설명

www.aladin.co.kr

본 책을 사용하여 공부하는 내용이고 해당 글은 상업적으로 배포하고자 하지 않고 개인 공부용입니다

 

  • 자료구조를 제대로 이해하고 있으면 응용 프로그램의 성능 향상, 표준화, 가독성, 유지 보수 등의 관점에서 유리하게 데이터를 관리할 수 있음
  • 선형 자료 구조(linear data structure)를 다루고 크게 연속된 구조와 연결된 구조로 나뉨

 

연속된 자료 구조와 연결된 자료 구조

 

  • 시간복잡도를 통해 특정 작업을 수행하는 데 걸리는 시간을 데이터 크기에 대한 수식으로 표현함으로써 데이터 크기가 변경되면 연산 시간이 어떻게 변하는지 보여줌
  • 즉, 내부에서 데이터를 어떻게 저장하여 사용하는가에 따라 연산의 시간 복잡도가 달라짐

 

연속된 자료 구조(Contiguous data structures)

  • 모든 원소를 단일 메모리 청크에 저장함

  • 4개의 상자를 곧 단일메모리 청크로 생각하면 되고 각 상자는 각 원소가 저장된 메모리 공간을 의미함
  • 각 원소는 모두 같은 데이터 타입으로 이루어짐
  • 같은 데이터 타입은 같은 크기의 메모리를 사용하므로 i번째 원소에 접근하고자 하면 BA+i * sizeof(type) 수식을 사용함
  • 배열의 크기에 상관없이 접근할 수 있어서 데이터 접근 시간은 항상 일정하고 이 경우 빅오(Big-O)표기법에 의해 O(1)로 표시함
  • 한편, 배열은 정적배열과 동적배열로 나눌수 있음
  • 정적 배열은 선언된 블록이 끝나면 소멸되는 반면, 동적 배열은 프로그래머가 생성할 시점과 해제할 시점을 자유롭게 결정할 수 있음
  • 두 배열의 유형 모두 다양한 연산에서 동일한 성능을 나타냄
  • 정적배열 int arr[size];
  • 동적배열 int* arr=new int[size];
  • cf) 정적배열은 스택(stack)메모리 영역에 할당되기 때문에 함수를 벗어날 때 자동으로 해제되지만, 동적배열은 힙(heap) 영역에 할당되기 때문에 사용자가 직접 해제하기 전까지 유지됨
  • 배열과 같은 연속된 자료 구조에서 각각의 원소는 인접하여 있어서 한 원소에 접근할 때 그 옆에 있는 원소 몇 개도 함께 캐시(cache)로 가져옴 -> 캐시 지역성(cache locality)
  • 이는 연산의 점근적 시간 복잡도(asympototic time complexity) 계산에는 영향을 주지 않지만 실제 동작에서 배열처럼 연속된 원소에 매우 빠르게 접근할 수 있다는 점이 큰 장점으로 작용함
  • 배열에서 모든 원소에 순차적으로 접근한다면 인접 원소에 바로 참조할 수 있어서 배열 자료형은 캐시지역성이 좋다고 말할 수 있음

 

연결된 자료 구조(Linked data structure)

 

 

  • 노드라고 하는 여러 개의 메모리 청크에 데이터를 저장하고 서로 다른 메모리 위치에 저장됨
  • 아마, 연속된 자료구조에서 +로 수식화된다는 것과는 다르다고 말하는 것 같음
  • 위 그림과 같은 형태를 연결 리스트(linked list)라고 함
  • 연결 리스트의 기본 구조에서 각 노드는 저장하는 데이터와 다음 노드를 가리키는 포인터(next)를 가지고 있음
  • 맨 마지막 노드는 NULL을 가짐
  • 따라서 앞서와 다르게 i번째에 접근하기 위해서는 연결 리스트 내부에서 i 번 이동하는 작업이 필요하고 원소 접근 시간은 노드 개수에 비례하며, 시간 복잡도로 표현하면 O(i)임
  • i-1번 이동하는게 아닌가?
  • 만약 연결 리스트에 새로운 원소를 추가하고 싶다면? 포인터를 이용하면 매우빠르게 삽입(그리고 삭제) 수행할 수 있음

  • 새로운 원소를 삽입하기 위해서 일단 새로운 노드(박스)를 먼저 생성해야 함
  • 각 노드의 next 포인터를 수정해야 함
  • 새로 추가한 노드의 포인터가 다음 노드를 가리키게 하고
  • 이전의 1=3 관계를 제거하고
  • 1이 2를 가리키도록 설정함
  • 추가가 아니고 제거라면 비슷한 과정으로 진행하면 됨
  • 연결 리스트는 연속적으로 저장되지 않기 때문에 캐시 지역성을 기대할 수 없음
  • 인접성을 보장할 수 없고 다음 노드에 방문하기 위해서는 이전 노드에 방문을 해야한다는 뜻이지
  • 배열과 같은 연결 리스트에서 모든 원소를 차례대로 방문하는 작업은 이론상 같은 시간복잡도를 가지지만 실제로는 연결 리스트의 성능이 떨어지는 것임
연속된 자료 구조 연결된 자료 구조
모든 데이터가 메모리에 연속적으로 저장됩니다. 데이터는 노드에 저장되고, 노드는 메모리 곳곳에 흩어져 있을 수 있습니다.
임의 원소에 즉각적으로 접근할 수 있습니다. 임의 원소에 접근하는 것은 선형 시간 복잡도를 가지며 느린 편입니다.
데이터가 연속적으로 저장되어 있고, 캐시 지역성 효과로 인해 모든 데이터를 순회하는 것이 매우 빠릅니다. 캐시 지역성 효과가 없으므로 모든 데이터를 순회하는 것이 느린 편입니다.
데이터 저장을 위해 정확하게 데이터 크기만큼의 메모리를 사용합니다. 각 노드에서 포인터 저장을 위해 여분의 메모리를 사용합니다.

▲ 그림 1-4 연속된 자료 구조와 연결된 자료 구조의 비교

 

파라미터 배열 연결 리스트
임의 접근 O(1) O(n)
맨 뒤에 원소 삽입 O(1) O(1)
중간에 원소 삽입 O(n) O(1)
캐시 지역성 있음 없음

▲ 그림 1-5 다양한 연산에 대한 배열과 연결 리스트의 시간 복잡도

 

 

728x90

'IE & SWCON > Data Structure (C++)' 카테고리의 다른 글

vector  (2) 2024.02.07
array class  (1) 2024.02.07
[C++ 기초] part 3. Loop  (0) 2024.01.31
[C++기초] Part 2. Complex Data  (0) 2024.01.28
[C++기초] part 1. Dealing with Data  (1) 2024.01.28