다른 데이터 구조 대신 배열을 사용하는 이유는 무엇입니까?


196

프로그래밍 할 때 배열이 다른 형식보다 정보를 저장하는 것이 더 좋은 인스턴스를 보지 못했습니다. 나는 실제로 프로그래밍 언어에서 추가 된 "기능"이 이것에 대해 개선되었고 그것들로 대체되었다고 생각했다. 나는 이제 그들이 대체되지 않고 오히려 새로운 삶을 얻었음을 알았습니다.

기본적으로 배열 사용의 요점은 무엇입니까?

이것은 컴퓨터 관점에서 배열을 사용하는 이유가 아니라 프로그래밍 관점에서 배열을 사용하는 이유입니다 (미묘한 차이). 컴퓨터가 배열을 사용하는 것은 문제의 핵심이 아닙니다.


2
컴퓨터가 어레이로 무엇을하는지 고려해 보지 않겠습니까? 우리는 STRAIGHT 거리 가 있기 때문에 집 번호 시스템을 가지고 있습니다. 배열도 마찬가지입니다.
lcn

" 다른 데이터 구조 "또는 " 다른 형식 "이란 무엇입니까? 그리고 어떤 목적으로?
tevemadar

답변:


771

수업 시간으로 되돌아 갈 시간입니다. 오늘날 우리의 멋진 관리되는 언어에서는 이러한 것들에 대해 많이 생각하지 않지만, 동일한 기초 위에 구축되었으므로 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) 검색 시간을 제공합니다.

이것은 메모리의 데이터 구조에 대한 엄청나게 높은 수준의 개요이며 많은 세부 사항을 건너 뛰지 만 다른 데이터 구조와 비교하여 배열의 강점과 약점을 보여주기를 바랍니다.


1
@Jonathan : 5 번째 요소를 가리 키도록 다이어그램을 업데이트했지만 MyArray [4]를 MyArray [5]로 변경하여 여전히 올바르지 않으므로 색인을 다시 4로 변경하고 다이어그램을 그대로 유지하십시오. .
Robert Gamble

54
이것은 무엇을 "커뮤니티 위키"에 대한 버그 날이 게시물 가치 "적절한"담당자입니다
Quibblesome

8
좋은 대답입니다. 그러나 설명하는 트리는 이진 검색 트리입니다. 이진 트리는 모든 노드에 최대 두 개의 자식이있는 트리입니다. 어떤 순서로든 요소가 포함 된 이진 트리를 가질 수 있습니다. 이진 검색 트리는 설명대로 구성됩니다.
gnud

1
좋은 설명이지만, nitpick하는 데 도움이되지 않습니다 ... 항목을 이진 검색 트리로 재정렬 할 수 있다면 왜 배열의 요소를 재정렬하여 이진 검색이 작동하도록 할 수 없습니까? 트리의 경우 O (n) 삽입 / 삭제에 대해서는 자세하게 설명하지만 배열의 경우 O (n)에 대해서는 자세히 설명 할 수 있습니다.
시장

2
액세스 시간이 데이터 세트의 크기와 관련하여 대수적으로 증가하기 때문에 이진 트리 표현이 O (log n)가 아닙니까?
Evan Plaice 2019

73

이길 수없는 O (1) 랜덤 액세스의 경우.


6
어느 시점에? O (1)는 무엇입니까? 랜덤 액세스 란 무엇입니까? 왜 이길 수 없습니까? 또 다른 점?
Jason

3
O (1)은 일정한 시간을 의미합니다. 예를 들어, 배열의 n-esim 요소를 얻으려면 인덱서 (배열 [n-1])를 통해 직접 연결하면됩니다. 헤드를 찾은 다음 순차적으로 다음 노드로 이동합니다. 선형 시간 인 O (n) 인 n-1 번입니다.
CMS

8
Big-O 표기법은 알고리즘의 속도가 입력 크기에 따라 어떻게 달라지는 지 설명합니다. O (n) 알고리즘은 두 배 많은 항목으로 실행하는 데 두 배 더 길고 8 배 많은 항목으로 실행하는 데 8 배 더 오래 걸립니다. 다시 말해 O (n) 알고리즘의 속도는 [cont ...]에 따라 다릅니다.
Gareth

8
입력 크기. O (1)은 입력의 크기 ( 'n')가 알고리즘의 속도에 영향을 미치지 않는다는 것을 의미합니다. 입력 크기에 상관없이 일정한 속도입니다
Gareth

9
O (1)을보고 O (0)을 올리십시오.
Chris Conway

23

모든 프로그램이 동일한 작업을 수행하거나 동일한 하드웨어에서 실행되는 것은 아닙니다.

이것은 대개 다양한 언어 기능이 존재하는 해답입니다. 배열은 핵심 컴퓨터 과학 개념입니다. 어레이를 목록 / 매트릭스 / 벡터 / 고급 데이터 구조로 교체하면 성능에 심각한 영향을 미치며 여러 시스템에서 완전히 불가능합니다. 문제의 프로그램으로 인해 이러한 "고급"데이터 수집 개체 중 하나를 사용해야하는 경우가 많습니다.

비즈니스 프로그래밍 (대부분의 사람들)은 비교적 강력한 하드웨어를 대상으로 할 수 있습니다. 이러한 상황에서 C # 또는 벡터에서 List를 사용하는 것이 올바른 선택입니다. 이러한 구조를 통해 개발자는 더 빨리 목표를 달성 할 수 있으므로 이러한 유형의 소프트웨어를보다 많이 사용할 수 있습니다.

임베디드 소프트웨어 또는 운영 체제를 작성할 때 어레이가 더 나은 선택 일 수 있습니다. 어레이는 기능이 적지 만 RAM을 덜 차지하며 컴파일러는 어레이를 조회하기 위해 코드를보다 효율적으로 최적화 할 수 있습니다.

나는이 경우에 대한 많은 혜택을 남기고 있다고 확신하지만, 당신이 요점을 얻었기를 바랍니다.


4
아이러니하게도 Java에서는 Vector 대신 ArrayList (또는 LinkedList)를 사용해야합니다. 이것은 일반적으로 불필요한 오버 헤드 인 동기화되는 벡터와 관련이 있습니다.
ashirley

0

배열의 장점을 보는 방법은 배열의 O (1) 액세스 기능이 필요한 위치를 파악하여 대문자로 표시하는 것입니다.

  1. 응용 프로그램의 조회 테이블 (특정 범주 형 응답에 액세스하기위한 정적 배열)

  2. Memoization (복잡한 함수 결과를 이미 계산 했으므로 log x와 같이 함수 값을 다시 계산하지 않음)

  3. 이미지 처리가 필요한 고속 컴퓨터 비전 응용 프로그램 ( https://en.wikipedia.org/wiki/Lookup_table#Lookup_tables_in_image_processing )

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.