최근에 코드를보다 효율적으로 만들기 위해 다양한 데이터 구조가 Python에서 구현되는 방식을 조사했습니다. 목록과 deques의 작동 방식을 조사한 결과, 목록의 O (n)에서 deques의 O (1)로 시간을 단축하거나 이동을 해제하고 싶을 때 이점을 얻을 수 있음을 발견했습니다 (목록은 다음과 같은 고정 길이 배열로 구현 됨). 전면에 무언가를 삽입 할 때마다 완전히 복사됩니다. 내가 찾을 수없는 것은 deque가 어떻게 구현되는지에 대한 세부 사항과 그 단점과 목록의 세부 사항입니다. 누군가이 두 가지 질문에 대해 가르쳐 줄 수 있습니까?
답변
https://github.com/python/cpython/blob/v3.8.1/Modules/_collectionsmodule.c
A
dequeobject
는 이중으로 연결된block
노드 목록으로 구성 됩니다.
그래서 예, deque
다른 답변에서 알 수 있듯이 a 는 (의심스러운) 연결된 목록입니다.
정교함 : 이것이 의미하는 것은 Python 목록이 슬라이싱을 포함한 임의 액세스 및 고정 길이 작업에 훨씬 더 나은 반면, 데크는 인덱싱 (하지만 슬라이싱은 아님)을 사용하여 끝 부분을 밀어 내고 터뜨리는 데 훨씬 더 유용하다는 것입니다. 가능하지만 목록보다 느립니다.
답변
확인하십시오 collections.deque
. 문서에서 :
Deques는 어느 방향 으로든 거의 동일한 O (1) 성능으로 deque의 양쪽에서 스레드로부터 안전한 메모리 효율적인 추가 및 팝을 지원합니다.
목록 객체는 유사한 작업을 지원하지만 빠른 고정 길이 작업에 최적화되어 있으며 기본 데이터 표현의 크기와 위치를 모두 변경하는 pop (0) 및 insert (0, v) 작업에 대해 O (n) 메모리 이동 비용이 발생합니다. .
말했듯이 pop (0) 또는 insert (0, v)를 사용하면 목록 객체에 큰 불이익이 발생합니다. 에서 슬라이스 / 인덱스 작업을 deque
사용할 수 없지만 작업 이 최적화 된 popleft
/ 를 사용할 수 있습니다 . 다음은이를 입증하는 간단한 벤치 마크입니다.appendleft
deque
import time
from collections import deque
num = 100000
def append(c):
for i in range(num):
c.append(i)
def appendleft(c):
if isinstance(c, deque):
for i in range(num):
c.appendleft(i)
else:
for i in range(num):
c.insert(0, i)
def pop(c):
for i in range(num):
c.pop()
def popleft(c):
if isinstance(c, deque):
for i in range(num):
c.popleft()
else:
for i in range(num):
c.pop(0)
for container in [deque, list]:
for operation in [append, appendleft, pop, popleft]:
c = container(range(num))
start = time.time()
operation(c)
elapsed = time.time() - start
print "Completed %s/%s in %.2f seconds: %.1f ops/sec" % (container.__name__, operation.__name__, elapsed, num / elapsed)
내 컴퓨터의 결과 :
Completed deque/append in 0.02 seconds: 5582877.2 ops/sec
Completed deque/appendleft in 0.02 seconds: 6406549.7 ops/sec
Completed deque/pop in 0.01 seconds: 7146417.7 ops/sec
Completed deque/popleft in 0.01 seconds: 7271174.0 ops/sec
Completed list/append in 0.01 seconds: 6761407.6 ops/sec
Completed list/appendleft in 16.55 seconds: 6042.7 ops/sec
Completed list/pop in 0.02 seconds: 4394057.9 ops/sec
Completed list/popleft in 3.23 seconds: 30983.3 ops/sec
답변
deque
객체에 대한 문서 항목은 알아야 할 대부분의 내용을 설명합니다. 주목할만한 인용구 :
Deques는 어느 방향 으로든 거의 동일한 O (1) 성능으로 deque의 양쪽에서 스레드로부터 안전한 메모리 효율적인 추가 및 팝을 지원합니다.
그러나…
인덱스 액세스는 양쪽 끝에서 O (1)이지만 중간에서 O (n)으로 느려집니다. 빠른 임의 액세스를 위해 대신 목록을 사용하십시오.
구현이 연결 목록인지 아니면 다른 것인지 확인하기 위해 소스를 살펴보아야하지만, 마치 deque
이중 연결 목록과 거의 동일한 특성이있는 것처럼 들립니다 .
답변
다른 모든 도움이 답변 외에도, 여기 파이썬 목록, deques, 세트 및 사전에 다양한 작업의 시간 복잡도 (빅 – 오)를 비교 좀 더 정보입니다. 이는 특정 문제에 적합한 데이터 구조를 선택하는 데 도움이됩니다.
답변
파이썬이 어떻게 구현했는지 정확히 모르겠지만 여기에서는 배열 만 사용하여 큐 구현을 작성했습니다. Python의 큐와 복잡성이 동일합니다.
class ArrayQueue:
""" Implements a queue data structure """
def __init__(self, capacity):
""" Initialize the queue """
self.data = [None] * capacity
self.size = 0
self.front = 0
def __len__(self):
""" return the length of the queue """
return self.size
def isEmpty(self):
""" return True if the queue is Empty """
return self.data == 0
def printQueue(self):
""" Prints the queue """
print self.data
def first(self):
""" Return the first element of the queue """
if self.isEmpty():
raise Empty("Queue is empty")
else:
return self.data[0]
def enqueue(self, e):
""" Enqueues the element e in the queue """
if self.size == len(self.data):
self.resize(2 * len(self.data))
avail = (self.front + self.size) % len(self.data)
self.data[avail] = e
self.size += 1
def resize(self, num):
""" Resize the queue """
old = self.data
self.data = [None] * num
walk = self.front
for k in range(self.size):
self.data[k] = old[walk]
walk = (1+walk)%len(old)
self.front = 0
def dequeue(self):
""" Removes and returns an element from the queue """
if self.isEmpty():
raise Empty("Queue is empty")
answer = self.data[self.front]
self.data[self.front] = None
self.front = (self.front + 1) % len(self.data)
self.size -= 1
return answer
class Empty(Exception):
""" Implements a new exception to be used when stacks are empty """
pass
그리고 여기에서 몇 가지 코드로 테스트 할 수 있습니다.
def main():
""" Tests the queue """
Q = ArrayQueue(5)
for i in range(10):
Q.enqueue(i)
Q.printQueue()
for i in range(10):
Q.dequeue()
Q.printQueue()
if __name__ == '__main__':
main()
C 구현만큼 빠르게 작동하지는 않지만 동일한 논리를 사용합니다.