수업 시간으로 되돌아 갈 시간입니다. 오늘날 우리의 멋진 관리되는 언어에서는 이러한 것들에 대해 많이 생각하지 않지만, 동일한 기초 위에 구축되었으므로 C에서 메모리가 관리되는 방식을 살펴 보겠습니다.
내가 들어가기 전에 " 포인터 "라는 용어에 대한 간단한 설명 " 의미에 . 포인터는 단순히 메모리의 위치를 "가리키는"변수입니다. 이 메모리 영역의 실제 값은 포함하지 않으며 메모리 주소도 포함됩니다. 메모리 블록을 사서함으로 생각하십시오. 포인터는 해당 사서함의 주소입니다.
C에서 배열은 단순히 오프셋이있는 포인터이며 오프셋은 메모리에서 얼마나 멀리 보이는지를 지정합니다. 이것은 O (1) 액세스 시간을 제공합니다.
MyArray [5]
^ ^
Pointer Offset
다른 모든 데이터 구조는이를 기반으로하거나 스토리지에 인접한 메모리를 사용하지 않으므로 무작위 액세스 조회 시간이 줄어 듭니다 (순차 메모리를 사용하지 않으면 다른 이점이 있음).
예를 들어 6 개의 숫자 (6,4,2,3,1,5)가있는 배열이 메모리에 있다고 가정 해 봅시다.
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
배열에서 우리는 각 요소가 메모리에서 서로 옆에 있다는 것을 알고 있습니다. AC 배열 ( MyArray
여기에서 호출 )은 단순히 첫 번째 요소에 대한 포인터입니다.
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^
MyArray
MyArray[4]
내부적 으로 조회하려면 다음 과 같이 액세스하십시오.
0 1 2 3 4
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^
MyArray + 4 ---------------/
(Pointer + Offset)
포인터에 오프셋을 추가하여 배열의 모든 요소에 직접 액세스 할 수 있기 때문에 배열의 크기에 관계없이 같은 시간에 모든 요소를 찾을 수 있습니다. 이것은 얻는 MyArray[1000]
것과 같은 시간이 걸리는 것을 의미합니다 MyArray[5]
.
대체 데이터 구조는 연결된 목록입니다. 이것은 다음 노드를 가리키는 선형 포인터 목록입니다.
======== ======== ======== ======== ========
| Data | | Data | | Data | | Data | | Data |
| | -> | | -> | | -> | | -> | |
| P1 | | P2 | | P3 | | P4 | | P5 |
======== ======== ======== ======== ========
P(X) stands for Pointer to next node.
각 "노드"를 자체 블록으로 만들었습니다. 메모리에 인접 해 있다고 보장되지 않기 때문입니다.
P3에 액세스하려면 메모리의 어디에 있는지 모르기 때문에 P3에 직접 액세스 할 수 없습니다. 내가 아는 것은 루트 (P1)가있는 곳이므로 P1에서 시작하여 원하는 노드에 대한 각 포인터를 따라야합니다.
이것은 O (N) 조회 시간입니다 (각 요소가 추가 될 때 조회 비용이 증가합니다). P4에 비해 P1000에 도착하는 것이 훨씬 비쌉니다.
해시 테이블, 스택 및 대기열과 같은 상위 수준의 데이터 구조는 모두 내부적으로 배열 (또는 여러 배열)을 사용할 수있는 반면, 링크 된 목록 및 이진 트리는 일반적으로 노드와 포인터를 사용합니다.
왜 누군가가 배열을 사용하는 대신 값을 찾아보기 위해 선형 순회가 필요한 데이터 구조를 사용해야하는지 궁금해 할 수 있습니다.
우리의 배열을 다시 가져 가라. 이번에는 값 '5'를 보유하는 배열 요소를 찾고 싶습니다.
=====================================
| 6 | 4 | 2 | 3 | 1 | 5 |
=====================================
^ ^ ^ ^ ^ FOUND!
이 상황에서 포인터를 찾기 위해 포인터에 추가 할 오프셋을 모르므로 0에서 시작하여 찾을 때까지 진행해야합니다. 이것은 내가 6 점검을 수행해야 함을 의미합니다.
이 때문에 배열에서 값을 검색하는 것은 O (N)으로 간주됩니다. 배열이 커질수록 검색 비용이 증가합니다.
때때로 비 순차적 데이터 구조를 사용하면 이점이있을 수 있다고 말한 것을 기억하십시오. 데이터 검색은 이러한 장점 중 하나이며 가장 좋은 예 중 하나는 이진 트리입니다.
이진 트리는 연결된 목록과 유사한 데이터 구조이지만 단일 노드에 연결하는 대신 각 노드는 두 개의 자식 노드에 연결할 수 있습니다.
==========
| Root |
==========
/ \
========= =========
| Child | | Child |
========= =========
/ \
========= =========
| Child | | Child |
========= =========
Assume that each connector is really a Pointer
이진 트리에 데이터가 삽입되면 몇 가지 규칙을 사용하여 새 노드를 배치 할 위치를 결정합니다. 기본 개념은 새 값이 부모보다 크면 왼쪽에 삽입하고, 더 낮 으면 오른쪽에 삽입한다는 것입니다.
이것은 이진 트리의 값이 다음과 같을 수 있음을 의미합니다.
==========
| 100 |
==========
/ \
========= =========
| 200 | | 50 |
========= =========
/ \
========= =========
| 75 | | 25 |
========= =========
값이 75 인 이진 트리를 검색 할 때이 구조로 인해 3 개의 노드 (O (log N)) 만 방문하면됩니다.
- 75가 100보다 작습니까? 오른쪽 노드를보십시오
- 75가 50보다 큽니까? 왼쪽 노드를보십시오
- 75가 있습니다!
트리에는 5 개의 노드가 있지만 나머지 두 노드를 살펴볼 필요는 없습니다. 노드와 그 자식이 원하는 값을 포함 할 수 없다는 것을 알았 기 때문입니다. 이는 최악의 경우 모든 노드를 방문해야한다는 것을 의미하는 검색 시간을 제공하지만 최상의 경우에는 노드의 작은 부분 만 방문해야합니다.
그것은 배열이 이길 곳이며, O (1) 액세스 시간에도 불구하고 선형 O (N) 검색 시간을 제공합니다.
이것은 메모리의 데이터 구조에 대한 엄청나게 높은 수준의 개요이며 많은 세부 사항을 건너 뛰지 만 다른 데이터 구조와 비교하여 배열의 강점과 약점을 보여주기를 바랍니다.