Ex) Queue at ticket office
insertion과 deletion이 같은 location에서 일어나는 stack과 다름
an ordered group consisting of n elements
dequeue와 peek는 맨앞의 원소를 제거하는지 아닌지에만 차이점을 가지고 있음
implements a queue using arrays linearly
implements a queue using arrays in a circular form
Two variables to manage the front and back of the queue
1. front : index before the first element
2. rear : index of the last element
Empty : front == rear
Full : front % M == (rear + 1) % M
Note) One space is always left empty to distinguish between 'blank' and 'full' state
순서대로(circular, counterwise) element가 저장되기 때문에 Full의 공식이 성립할 수 있음
int is_empty(QueueType * q){
return (q->front == q->rear);
}
int is_full(QueueType * q){
return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}
//don't have to use remain operator at q->front
void enqueue(QueueType *q, element item){
if (is_full(q)) error("Queue is full\n"); //종료
q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
//circular queue이기 때문에 나머지 연산
q->queue[q->rear] = item;
}
circular queue이기 때문에 index 최고값에 rear가 있을 때 원소를 삽입하면, rear는 0으로 변경됨.
따라서 나머지 연산을 통해 이를 수행하도록 함
3.2 Delete function
element dequeue(QueueType *q){
if (is_empty(q)) error("Queue is empty\n"); //종료
q->front = (q->front + 1) % MAX_QUEUE_SIZE;
//circular queue이기 때문에 나머지 연산
return q->queue[q->front];
}
Example
#define MAX_QUEUE_SIZE
typedef int element;
typedef struct QueueType{
element queue[MAX_QUEUE_SIZE];
int front, rear;
}QueueType;
void error(char *message) {
fprintf(stderr, "%s\n", message);
exit(1);
}
void init(QueueType *q) {q->front = q->rear = 0;}
int is_empty(QueueType *q) {return (q->front == q->rear);}
int is_full(QueType *q) {
return (q->rear + 1) % MAX_SIZE_QUEUE == q->front ;
}
void enqueue(QueueType *q, element item){
if (is_full(q)) error("Queue is full\n"); //종료
q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
//circular queue이기 때문에 나머지 연산
q->queue[q->rear] = item;
}
element dequeue(QueueType *q){
if (is_empty(q)) error("Queue is empty\n"); //종료
q->front = (q->front + 1) % MAX_QUEUE_SIZE;
//circular queue이기 때문에 나머지 연산
return q->queue[q->front];
}
element peek(QueType *q){
if (is_empty(q)) error ("Queue is empty\n");
return q->queue[(q->front + 1) % MAX_QUEUE_SIZE];
//don't update front ← (front + 1) % MAX_QUEUE_SIZE
}
void main(){
QueueType q;
init(&q);
printf("front = %d, rear = %d\n", q.front, q.rear);
enqueue(&q, 1);
enqueue(&q, 2);
enqueue(&q, 3);
printf("dequeue() = %d\n", dequeue(&q));
printf("dequeue() = %d\n", dequeue(&q));
printf("dequeue() = %d\n", dequeue(&q));
printf("front = %d, rear = %d\n", q.front, q.rear);
}
A queue that is implemented as a linked list
related to deletion and points to the element at the front of the linked list
related to insertion and points to the element at the last of the linked list
If there are no elements in the queue, front and rear are NULL
void enqueue(QueueType *q, element item){
QueueNode *temp = (QueueNode *)malloc(sizeof(QueueNode));
if (temp == NULL)
printf("Memory cannot be allocated.\n")'
else{
temp->item = item;
temp->link = NULL;
if(is_empty(q)){
q->front = temp;
q->rear = temp;
}
else{
q->rear->link = temp;
q->rear = temp; //linked list와 똑같음!
}
}
}
element dequeue(QueueType *q){
QueueNode *temp = q->front;
element item;
if(is_empty(q))
error("Queue is empty");
else {
item = temp->item;
q->front = q->front->link;
if(q->front == NULL) //삭제하니 empty queue가 된 경우
q->rear = NULL;
}
free temp;
return item;
}
typedef int element;
typedef struct QueueNode{
element item;
struct QueueNode *link;
}QueueNode;
typedef struct QueueType{
QueueNode* front;
QueueNode* rear;
}QueueType;
void error(char *message) {
fprintf(stderr, "%s\n", message);
exit(1);
}
int is_empty(QueueType *q){
return (q->front == NULL);
}
void init(QueueType *q) {
q->front = NULL;
q->rear = NULL;
}
void enqueue(QueueType *q, element item){
QueueNode *temp = (QueueNode *)malloc(sizeof(QueueNode));
if (temp == NULL)
printf("Memory cannot be allocated.\n")'
else{
temp->item = item;
temp->link = NULL;
if(is_empty(q)){
q->front = temp;
q->rear = temp;
}
else{
q->rear->link = temp;
q->rear = temp;
}
}
}
element dequeue(QueueType *q){
QueueNode *temp = q->front;
element item;
if(is_empty(q))
error("Queue is empty");
else {
item = temp->item;
q->front = q->front->link;
if(q->front == NULL)
q->rear = NULL;
}
free temp;
return item;
}
element peek(QueueType *q){ //don't need to update front
if(is_empty(q))
error("Queue is empty");
else {
return (q->front->item);
}
}
void main(){
QueueType q;
init(&q);
enqueue(&q, 1);
enqueue(&q, 1);
enqueue(&q, 1);
printf("dequeue() = %d\n", dequeue(&q));
printf("dequeue() = %d\n", dequeue(&q));
printf("dequeue() = %d\n", dequeue(&q));
}
It can be inserted and deleted at the front and rear of the queue
an ordered group of n elements
Deque는 front와 rear에서 모두 삽입, 삭제가 가능해야 하므로 doubly linked list를 사용
simple linked list를 사용해도 되지만 그래도 doubly linked list가 선호됨
typedef int element;
typedef struct DlistNode{
element data;
struct DlistNode *llink;
struct DlistNode *rlink;
}DlistNode;
typedef struct DequeType{
DlistNode* head; //front
DlistNode* tail; //rear
}DequeType;
Doubly linked list can be implemented in two way
1. head node : node with no actual data
→ if the list is blank, we only have head node
circular connection
2. head, tail : just addresses
→ if list is blank, head and tail are NULL
circular connection is not needed
DlistNode *create_node(DlistNode *llink, element item, DlistNode *rlink){
DlistNode *node = (DlistNode *)malloc(sizeof(DlistNode));
if (node == NULL) error("Memory allocation error");
node->llink = llink;
node->data = item;
node->rlink = rlink;
return node;
}
void add_rear(DequeType *dq, element item){
DlistNode *new_node = create_node(dp->tail, item, NULL);
if(is_empty(dq)) //case1. the deque is empty
dq->head = new_node;
else //case2. the deque is not empty
dq->tail->rlink = new_node;
dq->tail = new_node;
}
void ad_front(DequeType *dq, element item){
DlistNode *new_node = create_node(NULL, item, dq->head);
if (is_empty(dq)) //case1. the deque is empty
dq->tail = new_node;
else //case2. the deque is not empty
dq->head->llink = new_node;
dq->head = new_node;
}
element delete_rear(DequeType *dq){
element item;
DlistNode *removed_node;
if(is_empty(dq)) printf("Deque is empty\n");
else{
removed_node = dq->tail;
item = removed_node->data;
dq->tail = dq->tail->llink;
free(removed_node);
if(dq->tail == NULL) //삭제 후 deque에 더이상 노드가 없을 때
dq->head = NULL;
else
dq->tail->rlink = NULL;
}
return item;
}
element delete_front(DequeType *dq){
element item;
DlistNode *removed_node;
if(is_empty(dq)) {
printf("Deque is empty\n");
}
else {
removed_node = dq->head;
item = removed_node->data;
dq->head = dq->head->rlink;
free(removed_node);
if (dq->head == NULL) //삭제 후 deque에 더이상 노드가 없을 때
dq->tail = NULL;
else
dq->head->llink = NULL;
}
return item;
}
Queues can work as buffers that coordinate an interaction between two processes running at different speeds
Buffer links between a producer process that produces data and a consumer process that consumes data
Ex) Computer → Data(Queue) → Printer
QueueType buffer;
/*Producer Process*/
producer(){
while(1){
Produce data;
while(lock(buffer) != SUCCESS);
/*lock(): function to avoid producer and consumer
access the buffer simultaneously*/
/*while lock(buffer) does not succeed,
no 'enqueue' operation is performed*/
if(!is_full(buffer)){
enqueue(buffer, data);
//if lock(buffer) succeeds, then it is executed
}
unlock(buffer);
}
}
/*Consumer Process*/
consumer(){
while(1){
while(lock(buffer) != SUCCESS);
if(!is_empty(buffer)){
data = dequeue(buffer);
Consume data;
}
unlock(buffer);
}
}
이 예시의 자세한 부분은 OS(Operating system) lecture에서 다룰 것임
구조체 정의 및 필요한 변수 선언
typedef struct element{ //customer structure
int id;
int arrival_time;
int service_time;
}element;
typedef struct QueueType{
element queue[MAX_QUEUE_SIZE];
int front, rear;
}QueueType;
Queue queue;
double random(){
return rand() / (double)RAND_MAX;
}
/*various state variables needed for simulation*/
int duration = 10;
//simulation time
double arrival_prob = 0.7;
//average number of customers arriving in one time unit
int max_serv_time = 5;
//maximum service time for one customer
int clock;
/*results of the simulation*/
int customers;
//total number of customers
int served_customers;
//number of customers served
int waited_time;
//time the customers waited
관련 함수들 정의
int is_customer_arrived(){
if (random() < arrival_prob)
return TRUE;
else return FALSE;
}
void insert_customer(int arrival_time){
element customer;
customer.id = customers++;
customer.arrival_time = arrival_time;
customer.service_time = (int)(max_serv_time * random()) + 1;
enqueue(&queue, customer);
printf("Customer %d comes in %d minutes. Service time is %d minutes", customer.id, customer.arrival_time, customer.service_time);
}
int remove_customer(){
element customer;
int service_time = 0;
if(is_empty(&queue)) return 0;
customer = dequeue(&queue);
service_time = customer.service_time - 1;
served_customers++;
waited_time += clock - customer.arrival_time;
printf("Customer %d starts service in %d minutes.
Wait time was %d minutes",
customer.id, clock, clock - customer.arrival_time);
return service_time;
}
void print_stat(){
printf("Number of customers served = %d", served_customers);
printf("Total wait time = %d minutes", wated_time);
printf("Average wait time per person = %f minutes", (double)waited_time/served_customers);
printf("Number of customers sill waithing = %d", customers - served_customers);
}
simulation program
void main(){
int service_time = 0;
clock = 0;
while(clock < duration){
clock++;
printf("Current time = %d\n", clock);
if(is_customer_arrived()){
insert_customer(clock);
}
if(service_time > 0)
//서비스 받고 있는 고객의 서비스가 끝나지 않았을 때
service_time--;
else { //서비스를 받고 있는 손님이 없을 때
service_time = remove_customer();
}
}
print_stat();
}