자료구조 스택과 큐 (2)- 큐

"큐(Queue)"
“큐”(Queue)는 삽입-삭제 연산이 양 끝에서 각각 수행되는 자료구조이다.

큐(Queue)는,
앞서 다룬 스택(Stack)과 다르게
삽입-삭제 연산이 동일한 자리(Top)에서 일어나지 않는다.

각각 front 자리와 rear 자리가 존재하며,
삽입 연산은 rear에서,
삭제 연산은 front 자리에서 일어난다.

SLL로 구현하였을때
삽입 연산은 단순히 끝(rear)자리에서의 레퍼런스 연결-
삭제 연산은 앞(front)자리에서의 레퍼런스 갱신으로 연산이 일어난다.
(python list는 append(),pop(0)이용.)

따라서 큐 자료구조는
FIFO(First In First Out)구조에 따라 연산이 발생한다.
=가장 먼저 삽입한 항목이 가장 먼저 삭제된다.
간단하게 이해하려면,빨대 구조를 생각하면 된다.(스택은 주머니..?)

python에서 코드를 작성하였으며,
앞서 스택과 같이 python의 리스트나 SLL로 구현이 가능하다.

동일하게 python의 리스트를 사용하여 구현하였을땐,
add/remove 연산은 각각 O(1)시간이 소요된다.
그러나,
동적 크기 변화 때문에 시간복잡도가 O(N)으로 증가한다(리스트 전체복사)
반면,SLL을 이용하면 삽입 삭제 연산의 시간복잡도가 O(1)이다.
(순차적 방문이 필요없기에,front-rear 자리에서의 연산만 일어나면 된다.)

CODE:

우선,SLL로 구현한 큐이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#소스코드

class Node:
    def __init__(self,item,n): #노드 생성자
        self.item = item
        self.next = n

def add(item): #삽입 연산
    global size #전역변수 size,front,rear(Queue는 rear 삽입,front 삭제 연산)
    global front 
    global rear 
    new_node = Node(item,None) # 노드 객체 생성(item을 가지고 있는)
    if size == 0: #  일떄)
        front = new_node # 노드 객체가 front(rear 삽입)
    else: # 큐가 아닐떄)
        rear.next = new_node # 마지막 rear 자리에 삽입
    rear = new_node #rear = 새로 추가한 노드(FIFO,First In First Out), "rear 재지정"
    size += 1

def remove(): #삭제 연산
    global size #전역변수 size,front,rear(Queue는 rear 삽입,front 삭제 연산)
    global front
    global rear
    if size != 0: # 큐가 아닐때)
        fitem = front.item #front 자리,  앞에 있는 항목의 객체를 지정
        front = front.next #SLL 삭제처럼 레퍼런스 갱신 = 삭제
        size -= 1
        if size == 0: #삭제 연산   큐가 되었을 )
            rear = None #rear = None
        return fitem #삭제한 항목 리턴

def print_q(): # 출력
    p = front 
    print('front: ', end='')
    while p: # 끝까지 출력
        if p.next != None: #front의 다음 객체가 None이 아닐때)
            print(p.item,'->  ', end='')
        else: #다음 객체가 rear  
            print(p.item, end = '')
        p = p.next #다음 객체 이동
    print(' : rear')

#실행코드

front = None #초기화 => front, rear   초기화 필요
rear = None 
size = 0

add('apple')
add('orange')
add('cherry')
add('pear')
print('사과, 오렌지, 체리, 배 삽입 후: \t', end='') # rear => pear > cherry > orange > apple <= front
print_q()
remove()
print('remove한 후:\t\t', end='') #remove, front 부터 삭제(FIFO) / rear => pear > cherry > orange <= front
print_q()
remove()
print('remove한 후:\t\t', end='') # rear => pear > cherry <= front
print_q()
add('grape')
print('포도 삽입 후:\t\t', end='') #add, rear 에서 삽입(FIFO),스택과 다르게 삭제 삽입 연산하는 곳이 다름.
# rear => grape > pear > cherry <= front
print_q()

#실행결과

#사과, 오렌지, 체리,  삽입 :         front: apple ->  orange ->  cherry ->  pear : rear
#remove한 :            front: orange ->  cherry ->  pear : rear
#remove한 :            front: cherry ->  pear : rear
#포도 삽입 :           front: cherry ->  pear ->  grape : rear

다음으로,
python 리스트를 활용한 큐 자료구조이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#소스코드

def add(item):
    q.append (item)

def remove():
    if len(q) != 0:
        item = q.pop(0)
        return item

def print_q():
    print('front =>  ', end='')
    for i in range(len(q)):
        print('{!s:<8}'.format(q[i]), end='') # 앞부터 항목들을 차례로 출력
        #특이한건 .format의 이용인데,
        # {!s = String으로 형변환:conversion, :<8 8칸을 사용하면서 왼쪽으로 정렬한다는 .}
        #형식을 지정해주는 출력문...알아두자
    print(' <- rear')

#실행코드

q = []

add('apple')
add('orange')
add('cherry')
add('pear')
print('사과, 오렌지, 체리, 배 삽입 후: \t', end='') # rear => pear > cherry > orange > apple <= front
print_q()
remove()
print('remove한 후:\t\t', end='') #remove, front 부터 삭제(FIFO) / rear => pear > cherry > orange <= front
print_q()
remove()
print('remove한 후:\t\t', end='') # rear => pear > cherry <= front
print_q()
add('grape')
print('포도 삽입 후:\t\t', end='') #add, rear 에서 삽입(FIFO),스택과 다르게 삭제 삽입 연산하는 곳이 다름.
# rear => grape > pear > cherry <= front
print_q()

#실행결과는 SLL과 같다.

큐 자료구조는,
CPU의 Task Scheduling,
네트워크 프린터
실시간 시스템의 interrupt 처리,
다양한 이벤트 구동방식 컴퓨터 시뮬레이션,
콜 센터의 전화 서비스 처리에 사용되며,
BFS(Breath-First-Search),
이진트리의 레벨순회에 사용된다.
(다양하게 사용된다!)

스택 자료구조의 구조 차이점과,
시간복잡도의 동일성을 기억하자.