모든 데이터 유형이 포인터로 노드로 정리됩니까?


21

배열 또는 벡터는 일련의 값입니다. 그것들은 반드시 연결된리스트로 구현 될 수 있습니다. 이것은 다음 노드에 대한 포인터를 가진 노드들입니다.

스택과 큐는 Intro CS 과정에서 일반적으로 가르치는 두 가지 추상 데이터 유형입니다. 수업 어딘가에서 학생들은 종종 기본 데이터 구조로 연결된 목록을 사용하여 스택과 대기열을 구현해야합니다. 즉, 동일한 "노드 모음"아이디어로 돌아갑니다.

힙을 사용하여 우선 순위 큐를 작성할 수 있습니다. 힙은 루트에 최소값을 가진 트리로 생각할 수 있습니다. BST, AVL, 힙을 포함한 모든 종류의 트리는 에지로 연결된 노드 모음으로 생각할 수 있습니다. 이 노드들은 한 노드가 다른 노드를 가리키는 곳에서 서로 연결되어 있습니다.

모든 데이터 개념은 항상 다른 적절한 노드에 대한 포인터가있는 노드로 요약 될 수 있습니다. 맞습니까? 이것이 간단하다면 교과서에서 왜 데이터가 포인터를 가진 노드의 무리라고 설명하지 않습니까? 노드에서 이진 코드로 어떻게 이동합니까?


5
당신이 암시하는 기본 데이터 구조를 "콘 셀"이라고합니다. 원하는 데이터 구조를 구축 할 수 있습니다. 주어진 교과서 저자가 죄수 세포를 설명하지 않는 이유를 알고 싶다면, 그 저자에게 왜 그 선택을했는지 물어보십시오. 노드 배열의 설명에서 이진 코드로 이동하는 것을 "컴파일"이라고하며 "컴파일러"의 작업입니다.
Eric Lippert

18
모든 데이터 구조를 배열로 정리할 수도 있습니다. 결국, 그것들은 모두 메모리로 끝나는데, 이것은 단지 하나의 매우 큰 배열입니다.
BlueRaja-대니 Pflughoeft

10
인덱싱을 유지하려면 연결된 목록을 사용하여 배열을 구현할 수 없습니다 . 영형(1)
svick

5
죄송합니다. "노드 및 포인터"에 대해 이야기하면 이미 음식을 먹었 음을 의미합니다. " 모든 Real Programmer가 알고 있듯이 유용한 데이터 구조는 배열뿐입니다. 문자열, 목록, 구조, 세트는 모두 특수한 배열의 사례이며 모든 종류의 프로그래밍 언어를 망칠 필요없이 쉽게 처리 할 수 ​​있습니다. . 합병증 "참조 :"진짜 프로그래머에서 "파스칼 사용하지 않는 web.mit.edu/humor/Computers/real.programmers
alephzero

3
...하지만 더 진지하게, 데이터 구조에서 중요한 것은 구현 방법이 아니라 데이터 구조로 수행 할 수있는 작업입니다 . 21 세기에 자신을 구현하는 것은 프로그래밍 연습 일뿐입니다. 게으른 교육자들에게는 그러한 운동이 쉽게 등급을 매기는 것이 무의미하다는 사실보다 중요하며, 학생들이 " 바퀴 재발견 "은 실제 프로그래밍에서 유용한 활동입니다.
alephzero

답변:


14

기본적으로 모든 데이터 구조가 요약됩니다. 연결이있는 데이터 노드는 모두 인공적입니다. 실제로 물리적으로 존재하지는 않습니다. 이것은 바이너리 부분이 들어온 곳입니다. C ++에서 몇 가지 데이터 구조를 생성하고 객체가 메모리에서 어디에 위치하는지 확인해야합니다. 데이터가 메모리에 어떻게 배치되는지 배우는 것은 매우 흥미로울 수 있습니다.

매우 다양한 구조의 주된 이유는 모두 하나 또는 다른 것을 전문화하기 때문입니다. 예를 들어, 메모리에서 페이지를 가져 오는 방법으로 인해 일반적으로 링크 된 목록 대신 벡터를 반복하는 것이 더 빠릅니다. 벡터는 사용되지 않는 슬롯에 추가 공간을 할당해야하므로 링크 된 목록은 더 큰 크기의 유형을 저장하는 것이 좋습니다 (벡터 디자인에 필요함).

참고로 흥미로운 데이터 구조는 해시 테이블입니다. 설명하는 노드 및 포인터 시스템을 따르지 않습니다.

TL; DR : 컨테이너는 기본적으로 모든 노드와 포인터이지만 매우 특정한 용도로 사용되며 무언가에 더 좋고 다른 것에는 더 나쁩니다.


1
내 테이크 아웃은 대부분의 데이터가 실제로 포인터가있는 여러 노드로 표현 될 수 있다는 것입니다. 그러나 그것들은 (a) 물리적 수준, 즉 일어나는 일이 아니고 (b) 개념적 수준에서 그 값을 연결된 목록으로 생각하는 것이 기본 데이터에 대한 추론에 유용하지 않기 때문은 아닙니다. 어쨌든 우리의 생각을 단순화하는 것은 단지 추상화 일뿐이므로 다른 상황이 가능하더라도 상황에 가장 적합한 추상화를 선택할 수도 있습니다.
derekchen14

13

모든 데이터 개념은 항상 다른 적절한 노드에 대한 포인터가있는 노드로 요약 될 수 있습니다.

아, 아뇨 당신은 날 상처주고 있어요.

고정 데이터 구조의 경우에도 다른 곳에서 설명하려고 시도한 것처럼 ( " 이진 검색 트리와 이진 힙의 차이점은 무엇입니까? ")이를 이해하는 데에는 몇 가지 수준이 있습니다.

언급 한 우선 순위 큐와 같이 사용하려는 경우 추상 데이터 유형입니다. 어떤 종류의 객체를 저장하고 어떤 질문을 할 수 있는지 알고 사용합니다. 그것은 더 많은 데이터 구조입니다. 다음 단계에서 유명한 구현 인 이진 힙 은 이진 트리 로 이해 있지만 마지막 수준은 효율성을 이유로 배열로 구현 된 것입니다. 노드와 포인터가 없습니다.

또한 노드와 포인터 (가장자리)처럼 보이는 그래프의 경우 인접 배열과 인접 목록이라는 두 가지 기본 표현이 있습니다. 내가 생각하는 모든 포인터는 아닙니다.

실제로 데이터 구조를 이해하려고 할 때, 장점과 단점을 연구해야합니다. 때로는 표현이 효율성 (공간 또는 시간)을 위해 배열을 사용하기도합니다 (때로는 유연성을 위해). 이 보유하고 심지어 는 C ++ STL과 같은 구현 "통조림"좋은이있는 경우도 가끔 기본 표현을 선택할 수 있기 때문에. 거기에는 항상 상충 관계가 있습니다.


10

에프:아르 자형아르 자형

아무도 당신이 그 연속 함수를 정의하기 위해 그 모든 것을 말할 것을 기대하지 않습니다. 그렇지 않으면 아무도 어떤 일도 할 수 없습니다. 우리는 누군가 누군가가 우리를 위해 지루한 작업을했다는 것을 "믿습니다".

생각할 수있는 모든 데이터 구조는 기본 계산 모델이 처리하는 기본 개체, 임의 액세스 기계를 사용하는 경우 일부 레지스터의 정수 또는 Turing 기계를 사용하는 경우 일부 테이프의 기호로 요약됩니다.

우리는 추상화를 사용하여 사소한 문제에서 마음을 자유롭게하고보다 복잡한 문제에 대해 이야기 할 수 있습니다. 그 구조가 효과가 있다는 것을 "신뢰"하는 것이 완벽하게 합리적입니다. 모든 세부 사항으로 나선다는 것은 특별한 이유가없는 한 아무 소용이없는 헛된 운동입니다.


10

λ- 미적분에서 모든 데이터 유형은 함수로 요약됩니다. λ- 미적분에는 노드 또는 포인터가 없으며 함수 만 있으면됩니다. 따라서 함수를 사용하여 모든 것을 구현해야합니다.

ECMAScript로 작성된 부울을 함수로 인코딩하는 예입니다.

const T   = (thn, _  ) => thn,
      F   = (_  , els) => els,
      or  = (a  , b  ) => a(a, b),
      and = (a  , b  ) => a(b, a),
      not = a          => a(F, T),
      xor = (a  , b  ) => a(not(b), b),
      iff = (cnd, thn, els) => cnd(thn, els)();

그리고 이것은 단점 목록입니다.

const cons = (hd, tl) => which => which(hd, tl),
      first  = list => list(T),
      rest   = list => list(F);

자연수는 반복 함수로 구현할 수 있습니다.

집합은 특성 기능 (예 : contains방법)과 동일합니다.

위의 부울의 교회 인코딩은 실제로 언어 구성으로 부울, 조건부 또는 루프가없고 순수하게 라이브러리 기능으로 구현하는 스몰 토크와 같은 OO 언어에서 조건부가 구현되는 방식입니다. 스칼라의 예 :

sealed abstract trait Boolean {
  def apply[T, U <: T, V <: T](thn: => U)(els: => V): T
  def(other: => Boolean): Boolean
  def(other: => Boolean): Boolean
  val ¬ : Boolean

  final val unary_! = ¬
  final def &(other: => Boolean) =(other)
  final def |(other: => Boolean) =(other)
}

case object True extends Boolean {
  override def apply[T, U <: T, V <: T](thn: => U)(els: => V): U = thn
  override def(other: => Boolean) = other
  override def(other: => Boolean): this.type = this
  override final val ¬ = False
}

case object False extends Boolean {
  override def apply[T, U <: T, V <: T](thn: => U)(els: => V): V = els
  override def(other: => Boolean): this.type = this
  override def(other: => Boolean) = other
  override final val ¬ = True
}

object BooleanExtension {
  import scala.language.implicitConversions
  implicit def boolean2Boolean(b: => scala.Boolean) = if (b) True else False
}

import BooleanExtension._

(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3

2
@Hamsteriffic : 이것을 시도하십시오 : 이것이 실제로 스몰 토크와 같은 OO 언어에서 조건이 구현되는 방법입니다. 스몰 토크에는 언어 구조로 부울, 조건부 또는 루프가 없습니다. 이들 모두는 순수하게 라이브러리로 구현됩니다. 마음이 여전히 날리지 않습니까? 윌리엄 쿡 (William Cook)은 오래 전에 분명 했어야하지만 실제로 눈치 채지 못했던 것을 지적합니다. λ- 미적분학은 OO가 필요합니다. Ergo, λ-calculus는 가장 오래되고…
Jörg W Mittag

… 가장 순수한 OO 언어!
Jörg W Mittag

1
스몰 토크와 나쁜 하루 C ++ :-)와 함께 좋은 하루를 이길
밥 자비스-복원 모니카

@ JörgWMittag 나는 당신의 결론이 당신의 가정에 따른다고 생각하지 않습니다, 나는 당신의 가정이 사실이라고 생각하지 않으며, 당신의 결론이 진실이라고 생각하지 않습니다.
Miles Rout

4

많은 (대부분의) 데이터 구조는 노드와 포인터로 구성됩니다. 배열은 일부 데이터 구조의 또 다른 중요한 요소입니다.

궁극적으로, 모든 데이터 구조는 메모리에있는 단어들 또는 비트 들일뿐입니다. 그것은 그들이 어떻게 구성되고 우리가 중요한 것을 해석하고 사용하는 방법입니다.


2
궁극적으로, 비트는 와이어상의 많은 전기 신호, 또는 광섬유 케이블의 광 신호, 또는 플래터상의 구체적으로 자화 된 입자, 또는 특정 파장의 전파, 또는, 또는, 또는이다. 문제는 얼마나 깊이 가고 싶습니까? :)
와일드 카드

2

데이터 구조의 구현 은 항상 노드와 포인터로 귀결됩니다.

그런데 왜 거기서 멈춰? 노드와 포인터의 구현은 비트로 요약됩니다 .

비트의 구현은 전기 신호, 자기 저장 장치, 아마도 광섬유 케이블 등으로 요약됩니다 (물리적으로 물리학).

이것은 "모든 데이터 구조가 노드와 포인터로 귀결된다 "는 진술의 부당한 결론 이다. 사실이지만 구현 에만 관련됩니다 .


Chris Date 는 에세이가 특히 데이터베이스를 목표로하지만 구현모델을 매우 차별화 할 수 있습니다.

모델과 구현 사이에 하나의 구분선 이 없다는 것을 알면 조금 더 나아갈 수 있습니다 . 이것은 "추상화 계층"개념과 유사하다 (동일하지는 않다).

주어진 추상화 계층에서, 당신 아래에있는 계층들 (당신이 구축하고있는 계층들)은 단지 당신이 다루고 있는 추상화 나 모델에 대한 "구현 세부 사항"일뿐 입니다.

그러나, 추상화의 하위 계층 자체 에는 구현 세부 사항이 있습니다.

소프트웨어에 대한 매뉴얼을 읽는 경우, 해당 소프트웨어에 "발표 된"추상화 계층에 대해 배우고 있으며, 여기에서 자체 추상화를 작성하거나 메시지 보내기와 같은 조치를 수행 할 수 있습니다.

소프트웨어 의 구현 세부 사항 을 배우면 제작자가 자신이 구축 한 추상화를 어떻게 강화했는지 배울 수 있습니다. "구현 세부 사항"은 무엇보다도 데이터 구조 및 알고리즘을 포함 할 수있다.

그러나 실제 소프트웨어에서 "비트"및 "바이트"및 "스토리지"가 실제로 작동하는 방식에 기초하더라도 전압 측정을 특정 소프트웨어에 대한 "구현 세부 사항"의 일부로 간주 하지는 않습니다 .

요약하면, 데이터 구조는 알고리즘 및 소프트웨어에 대한 추론 및 구현을위한 추상화 계층입니다. 이 추상화 계층이 노드 및 포인터와 같은 낮은 수준의 구현 세부 사항을 기반으로한다는 사실은 사실이지만 추상화 계층 내에서는 관련이 없습니다 .


시스템을 실제로 이해하는 데있어 가장 큰 부분은 추상화 레이어가 어떻게 맞는지 파악 하는 것입니다. 따라서 데이터 구조가 어떻게 구현 되는지 이해하는 것이 중요합니다 . 그러나 그것들 구현되었다고해서 데이터 구조의 추상화가 존재 하지 않는다는 의미는 아닙니다 .


2

배열 또는 벡터는 일련의 값입니다. 그것들은 반드시 연결된리스트로 구현 될 수 있습니다. 이것은 다음 노드에 대한 포인터를 가진 노드들입니다.

배열 또는 벡터는 연결된 목록으로 구현할 수 있지만 거의 없어야합니다.

Θ()Θ(로그)Θ(1)(즉, 랜덤 액세스 메모리의 순차 블록). 또한 CPU에서 실제 어레이에 액세스하는 것은 구현하기가 훨씬 간단하고 실행 속도가 빠르며 별도의 노드 사이의 포인터에서 공간을 낭비 할 필요가 없으므로 메모리를 적게 사용합니다.

Θ()Θ(1)Θ(1)평균적으로, 배열의 실제 할당 된 크기를 예를 들어 2의 가장 가까운 거듭 제곱으로 반올림하는 것만으로도 추가 메모리의 일정한 상수를 희생하면서 평균적으로 많은 삽입 및 / 또는 제거가 필요한 경우 목록의 중간에있는 요소의 경우 실제 배열이 데이터 구조에 가장 적합한 구현이 아닐 수 있습니다. 그러나 삽입 및 제거를 스왑으로 대체 할 수있는 저렴한 경우가 종종 있습니다.

툴박스에 물리적 연속 배열을 포함하기 위해 스코프를 약간 확장하면 거의 모든 실제 데이터 구조를 실제로 노드 및 포인터와 함께 구현할 수 있습니다.

Θ(1)반전 작업). 그러나 실제로 이러한 기능은 추가적인 구현 복잡성과 표준 가비지 수집 체계 와의 비 호환성을 포함하여 단점을 극복하기에 충분히 유용하지 않습니다 .


1

이것이 간단하다면 교과서에서 왜 데이터가 포인터를 가진 노드의 무리라고 설명하지 않습니까?

그것이 "데이터"의 의미가 아니기 때문입니다. 추상적 인 아이디어를 구현과 혼동시키고 있습니다. "데이터"는 매우 추상적 인 아이디어입니다. "정보"의 또 다른 이름 일뿐입니다. 포인터 (일명 "링크 된 데이터 구조")가있는 여러 개의 연결된 노드는 훨씬 구체적인 아이디어입니다. 정보를 표현하고 구성하는 특정 방법입니다.

일부 데이터 추상화는 "연결된"구현에 매우 적합합니다. 명시적인 노드와 포인터 (또는 노드와 포인터의 일부 동형)를 사용하지 않고 완전히 일반적인 트리의 분기 특성을 구현하는 좋은 방법은 많지 않습니다. 그러나 노드와 포인터를 사용하여 구현하지 않는 다른 추상화가 있습니다. 부동 소수점 숫자가 떠 오릅니다.

스택과 큐는 중간에 있습니다. 스택의 링크 된 구현을 수행하는 것이 합리적 일 때가 있습니다. 배열과 단일 "스택 포인터"를 사용하는 것이 훨씬 더 합리적인 경우가 있습니다.

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