스칼라에서 List에 추가하는 데 왜 O (n) 시간이 복잡합니까?


13

방금 List(: +)에 대한 추가 작업의 실행 시간이 의 크기에 따라 선형으로 증가 한다는 것을 읽었습니다 List.

에 추가하는 List것은 꽤 일반적인 작업처럼 보입니다. 이를 수행하는 관용적 방법이 구성 요소 앞에 붙은 다음 목록을 바꾸어야하는 이유는 무엇입니까? 구현은 언제라도 변경 될 수 있으므로 설계 실패 일 수 없습니다.

내 관점에서, 선행과 추가는 모두 O (1)이어야합니다.

이것에 대한 정당한 이유가 있습니까?


2
"legitimate"의 정의에 따라 다릅니다. 스칼라는 불변 데이터 구조, 편재 익명 목록, 기능 구성 등에 크게 의존하고있다. 기본 목록 구현 (목록 꼬리에 대한 추가 가변 포인터가없는)은 해당 스타일에 적합하다. 더 강력한 목록이 필요한 경우 최소한 표준 컨테이너와 거의 구별 할 수없는 자체 컨테이너를 작성하는 것이 매우 쉽습니다.
Kilian Foth

1
교차 사이트 관련 -Scala에서 목록 끝에 요소 추가 -그 특성에 대해서는 약간의 차이가 있습니다. 나타납니다 스칼라의 목록 O (N)인지, 따라서 당신은 그것을 복사해야 불변이다.

사용 가능한 많은 가변 데이터 구조 중 하나 또는 Scala가 제공하는 O (1) 추가 시간 (벡터)이있는 불변 데이터 구조를 사용할 수 있습니다. List[T]순수한 기능 언어에서 사용하는 방식으로 사용한다고 가정합니다. 일반적으로 해체와 접두사로 머리에서 작업합니다.
KChaloux

3
앞에 추가하면 새로운 헤드의 다음 노드 포인터가 기존의 불변 목록에 놓이게되며 변경할 수 없습니다. O (1)입니다.

1
순수한 FP의 데이터 구조 복잡성 측정에 대한 일반적인 주제에 대한 중요한 작업과 분석을 위해 나중에 책으로 출판 된 오카 사키 논문 을 읽었습니다. FP를 배우는 사람이라면 누구나 FP에서 데이터를 구성하는 방법을 이해하는 것이 높이 평가됩니다. 또한 글을 잘 작성하고 읽기 쉽고 품질이 높은 텍스트를 쉽게 따라갈 수 있습니다.
지미 호파

답변:


24

내 의견을 조금 확장하겠습니다. 의 List[T]데이터 구조 scala.collection.immutable는보다 순전히 기능적인 프로그래밍 언어에서 변경 불가능한 목록이 작동하는 방식으로 작동하도록 최적화되었습니다. 접두사 시간 이 매우 빠르며 거의 모든 액세스를 위해 헤드에서 작업한다고 가정합니다.

불변 목록은 일련의 "콘 셀"로 연결된 목록을 모델링하기 때문에 매우 빠른 선행 시간을 갖습니다. 셀은 단일 값과 다음 셀에 대한 포인터를 정의합니다 (클래식 단일 링크 목록 스타일).

Cell [Value| -> Nil]

목록 앞에 추가하면 실제로 기존 목록의 나머지 부분을 가리키는 하나의 새 셀을 만드는 것입니다.

Cell [NewValue| -> [Cell[Value| -> Nil]]

목록은 변경할 수 없으므로 실제로 복사하지 않아도 안전합니다 . 이전 목록이 변경되어 새 목록의 모든 값이 유효하지 않게 될 위험은 없습니다. 그러나 타협으로 목록 을 변경할 수있는 포인터를 가질 수 없습니다.

이것은 목록에서 재귀 적으로 작업하는 데 매우 적합합니다. 자신의 버전을 정의했다고 가정 해 봅시다 filter.

def deleteIf[T](list : List[T])(f : T => Boolean): List[T] = list match {
  case Nil => Nil
  case (x::xs) => f(x) match {
    case true => deleteIf(xs)(f)
    case false => x :: deleteIf(xs)(f)
  }
}

목록의 헤드에서만 독점적으로 작동하고 :: 추출기를 통해 패턴 일치를 이용하는 재귀 함수입니다. 이것은 Haskell과 같은 언어에서 많이 볼 수있는 것입니다.

빠른 추가를 원한다면 Scala는 선택할 수있는 많은 가변 및 불변 데이터 구조를 제공합니다. 변경 가능한 측면에서을 살펴볼 수 있습니다 ListBuffer. 또는 Vectorfrom scala.collection.immutable은 빠른 추가 시간이 있습니다.


지금은 이해! 완벽하게 이해됩니다.
DPM

나는 스칼라를 모르지만 else무한 루프가 아닙니까? 나는 그것이 다음과 같아야한다고 생각합니다 x::deleteIf(xs)(f).
svick

@svick 어 ... 네. 네 그렇습니다. : p (지금 수정해야합니다!)
KChaloux

때문에 @Jubbat headtail빠른 임의 해시 기반 맵 또는 어레이를 사용하는 것보다 - -리스트 이런 종류의 접근이 매우 빠르다는 재귀 함수에 대한 우수한 유형이다. 이것이리스트가 대부분의 기능적 언어 (예 : Haskell 또는 Scheme)에서 핵심 유형 인 이유 중 하나입니다.
itsbruce

훌륭한 답변입니다. TL; DR을 추가하고 싶을 것입니다. "첨부하지 말고 앞에 추가해야하기 때문에"(대부분의 개발자가에 대해 List추가하고 추가 / 접두 하는 기본 가정을 정리하는 데 도움이 될 수 있습니다 ).
Daniel B
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.