주제: 파이썬에서 큐 구현하기
파이썬과 함께하는 자료구조의 이해[개정판] pp.101-104 참고해서 내용 작성하였습니다.
파이썬으로 배우는 자료구조 핵심 원리 pp.74-77 참고해서 내용 작성하였습니다.
정의
양쪽 끝에서 삽입과 삭제를 허용하는 자료구조이다.
단순연결리스트보다 이중연결리스트에 적합한 이유는?
: rear가 가리키는 노드의 이전 노드의 레퍼런스를 알아야 삭제가 가능하기 때문에 이중연결리스트가 적합하다.
데이터: 전단과 후단을 통한 접근을 허용하는 항목들의 모음
1) 큐와 알고리즘이 동일한 연산
2) 큐와 데이터가 동일하고 연산도 유사함
3) 원형 큐에서 추가된 연산
front <- (front -1 + MAX_QSIZE) % MAX_QSIZE
rear <- (rear -1 + MAX_QSIZE) % MAX_QSIZE
1) 원형 큐를 상속하여 원형 덱 클래스를 구현
class CircularDeque(CircularQueue) #CircularQueue에서 상속
2) 덱의 생성자 (상속되지 않는다)
def __init___(self): # CircularDeque의 생성자
super().__init__() # 부모 클래스의 생성자를 호출
3) 재 사용: isEmpty, isFull, size, clear
4) 인터페이스 변경
def addRear(self, item):self.enqueue(item) # enqueue호출
def deleteFront(self): return self.dequeue() # 반환에 주의
def getFront(self): return self.peek()
🤜 입력
from collections import deque
dq = deque('data') # 새 데크 객체를 생성
for elem in dq:
print(elem.upper(), end='')
print()
dq.append('r') # 맨 뒤와
dq.appendleft('k') # 맨 앞에 항목 삽입
print(dq)
dq.pop() # 맨 뒤와
dq.popleft() # 맨 앞의 항목 삭제
print(dq[-1]) # 맨 뒤의 항목 출력
print('x' in dq)
dq.extend('structure') # 맨 뒤와
dq.extendleft(reversed('python')) # 맨 앞에 여러 항목 삽입
print(dq)
💻 출력
DATA
deque(['k', 'd', 'a', 't', 'a', 'r'])
a
False
deque(['p', 'y', 't', 'h', 'o', 'n', 'd', 'a', 't', 'a', 's', 't', 'r', 'u', 'c', 't', 'u', 'r', 'e'])
1
🤜 입력
def addFront(self, item): # 새로운 기능: 전단 삽입
if not self.isFull():
self.items[self.front] = item # 항목 저장
self.front = self.front -1 # 반시계 방향으로 회전
if self.front <0: self.front = MAX_QSIZE -1
def deleteRear(self): # 새로운 기능: 후단 삭제
if not self.isEmpty():
item = self.items[self.rear]; # 항목 복사
self.rear = self.rear -1 # 반시계 방향으로 회전
if self.rear <0: self.rear = MAX_QSIZE -1
return item # 항목 반환
def getRear(self): # 새로운 기능: 후단 peek
return self.items[self.rear]
🎲 주의 : 연산 수행 도중 원소가 모두 삭제되어 데크가 비는 경우에도, 초기화 상태가 되어야 한다
🎯 위의 그림과 같이 입력을 하면, 출력의 결과가 위와 같아야 한다.
💻 입출력 형식:
1) 첫 번째 라인 : 연산의 개수 n
2) 두 번째 이후 라인: n개의 연산이 한 줄에 하나씩 차례로 입력됨.
- 하나의 줄에는 연산의 종류, 추가인 경우 원소가 주어짐 (원소는 양의 정수로 표기).
- 연산의 종류: 다음의 연산 이름이 대문자로 주어짐.
* AF (add_front),
* AR (add_rear),
* DF (delete_front),
* DR (delete_rear),
* P (print_depue)
* underflow 발생 시, 화면에 underflow를 출력하고 프로그램 종료.
class DNode: # 이중연결리스트를 위한 노드
def __init__(self, elem, prev = None, next = None):
self.data = elem
self.prev = prev
self.next = next
class DoublyLinkedDeque<:
def __init__(self):
self.front = None
self.rear = None
def isEmpty(self): return self.front == None
def clear(self): self.front = self.front = None
def add_front(self, item):
node = self.DNode(item, None, self.front) # Step1
if (self.isEmpty()): # 공백이면
self.front = self.rear = node # front와 rear 모두 node
else: # 공백이 아니면
self.front.prev = node # Step2
self.front = node # Step3
def add_rear(self, item):
node = self.DNode(item, self.rear, None) # Step1
if (self.isEmpty()): # 공백이면
self.front = self.rear = node # front와 rear 모두 node
else: # 공백이 아니면
self.rear.next = node # Step2
self.rear = node # Step3
def delete_front(self):
if not self.isEmpty():
data = self.front.data
self.front = self.front.next
if self.front == None:
self.rear = None
else:
self.front.prev = None # Step3
return data # Step4
def delete_rear(self):
if not self.isEmpty():
data = self.rear.data # Step1 # 데이터 복사
self.rear = self.rear.prev # Step2
if self.rear == None: # 노드가 하나 뿐이면
self.front=None # front도 None으로 설정
else:
self.rear.next = None # Step3
return data # Step4
🤜 입력
class DoublyLinkedDeque:
class DNode:
def __init__(self, elem, prev = None, next = None):
self.data = elem
self.prev = prev
self.next = next
def __init__(self):
self.front = None
self.rear = None
def isEmpty(self): return self.front == None
def clear(self): self.front = self.front = None
def size(self):
node = self.front
count = 0
while not node == None:
node = node.next
count += 1
return count
def print_deque(self):
print(end='')
node = self.front
while node.next != None:
print(f"{node.data}", end=' ')
node = node.next
print(f"{node.data}")
def add_front(self, item):
node = self.DNode(item, None, self.front)
if (self.isEmpty()):
self.front = self.rear = node
else:
self.front.prev = node
self.front = node
def add_rear(self, item):
node = self.DNode(item, self.rear, None)
if (self.isEmpty()):
self.front = self.rear = node
else:
self.rear.next = node
self.rear = node
def delete_front(self):
if not self.isEmpty():
data = self.front.data
self.front = self.front.next
if self.front == None:
self.rear = None
else:
self.front.prev = None
return data
def delete_rear(self):
if not self.isEmpty():
data = self.rear.data
self.rear = self.rear.prev
if self.rear == None:
self.front=None
else:
self.rear.next = None
return data
if __name__ == "__main__":
num = int(input())
Deque = DoublyLinkedDeque()
for i in range(num):
inputs = input().split(" ")
operation = inputs[0]
if operation == "AF":
number = inputs[-1]
Deque.add_front(number)
elif operation == "AR":
number = inputs[-1]
Deque.add_rear(number)
elif operation == "DF":
data = Deque.delete_front()
if data is None:
print('underflow')
break
elif operation == 'DR':
data = Deque.delete_rear()
if data is None:
print('underflow')
break
elif operation == 'P':
Deque.print_deque()
💻 출력
15
AF 10
AF 20
AF 30
AR 40
AR 50
P
30 20 10 40 50
DF
DF
DR
P
10 40
DF
DR
DR
underflow
파이썬 공식 문서를 확인하면 deque 파트에 다음 문장이 있다.
"Indexed access is 0(1) at both ends b니t slows to O(n) in the middle."
즉, "인덱스로 양 끝에 접근할 때는 빅오가 O(1)이지만 중간에 있는 데이터에 접근하려면 조금 느려서 빅오가 O(n)이다."
이중연결리스트를 활용하여 덱을 구현해보았다. 그림을 보면서 이해를 하니 이해하기 수월했다. 위의 이중연결리스트 문제를 구현 할 때, underflow가 출력이 안돼서 고생을 했다. 사실 for문 안에 data라는 변수를 추가해서 이 데이터가 없더라면 underflow를 출력하고, break를 걸었으면 되는 간단한 문제였다. 아직 파이썬에 대해서 많이 많이 부족하다.. 철저히 복습과 예습을 병행해야겠다.