Stack vs. Queue

29
Stack vs. Queue First, we will look at what is stack and what is queue individually, and then we will discuss the differences between stack and queue. What is a Stack? A Stack is a linear data structure. In case of an array, random access is possible, i.e., any element of an array can be accessed at any time, whereas in a stack, the sequential access is only possible. It is a container that follows the insertion and deletion rule. It follows the principle LIFO (Last In First Out) in which the insertion and deletion take place from one side known as a top. In stack, we can insert the elements of a similar data type, i.e., the different data type elements cannot be inserted in the same stack. The two operations are performed in LIFO, i.e., push and pop operation. The following are the operations that can be performed on the stack: o push(x): It is an operation in which the elements are inserted at the top of the stack. In the push function, we need to pass an element which we want to insert in a stack.

Transcript of Stack vs. Queue

Stack vs. Queue

First, we will look at what is stack and what is queue individually, and then we will

discuss the differences between stack and queue.

What is a Stack?

A Stack is a linear data structure. In case of an array, random access is possible, i.e., any

element of an array can be accessed at any time, whereas in a stack, the sequential

access is only possible. It is a container that follows the insertion and deletion rule. It

follows the principle LIFO (Last In First Out) in which the insertion and deletion take

place from one side known as a top. In stack, we can insert the elements of a similar

data type, i.e., the different data type elements cannot be inserted in the same stack. The

two operations are performed in LIFO, i.e., push and pop operation.

The following are the operations that can be performed on the stack:

o push(x): It is an operation in which the elements are inserted at the top of the

stack. In the push function, we need to pass an element which we want to insert

in a stack.

o pop(): It is an operation in which the elements are deleted from the top of the

stack. In the pop() function, we do not have to pass any argument.

o peek()/top(): This function returns the value of the topmost element available in

the stack. Like pop(), it returns the value of the topmost element but does not

remove that element from the stack.

o isEmpty(): If the stack is empty, then this function will return a true value or else

it will return a false value.

o isFull(): If the stack is full, then this function will return a true value or else it will

return a false value.

In stack, the top is a pointer which is used to keep track of the last inserted element. To

implement the stack, we should know the size of the stack. We need to allocate the

memory to get the size of the stack. There are two ways to implement the stack:

o Static: The static implementation of the stack can be done with the help of

arrays.

o Dynamic: The dynamic implementation of the stack can be done with the help of

a linked list.

What is the Queue?

A Queue is a linear data structure. It is an ordered list that follows the principle FIFO

(First In -First Out). A Queue is a structure that follows some restrictions on insertion and

deletion. In the case of Queue, insertion is performed from one end, and that end is

known as a rear end. The deletion is performed from another end, and that end is

known as a front end. In Queue, the technical words for insertion and deletion

are enqueue() and dequeue(), respectively whereas, in the case of the stack, the

technical words for insertion and deletion are push() and pop(), respectively. Its structure

contains two pointers front pointer and rear pointer, where the front pointer is a

pointer that points to the element that was first added in the queue and the rear pointer

that points to the element inserted last in the queue.

Similarities between stack and queue.

There are two similarities between the stack and queue:

o Lineardatastructure

Both the stack and queue are the linear data structure, which means that the

elements are stored sequentially and accessed in a single run.

o Flexibleinsize

Both the stack and queue are flexible in size, which means they can grow and

shrink according to the requirements at the run-time.

Differences between stack and queue

The following are the differences between the stack and queue:

Basis of Comparision Stack Queue

Basis for comparison Stack Queue

Principle It follows the principle LIFO (Last In-

First Out), which implies that the

element which is inserted last would

be the first one to be deleted.

It follows the principle FIFO (First In -First Out),

which implies that the element which is added

first would be the first element to be removed

from the list.

Structure It has only one end from which both

the insertion and deletion take

place, and that end is known as a

top.

It has two ends, i.e., front and rear end. The

front end is used for the deletion while the

rear end is used for the insertion.

Number of

pointers used

It contains only one pointer known

as a top pointer. The top pointer

holds the address of the last

inserted or the topmost element of

the stack.

It contains two pointers front and rear pointer.

The front pointer holds the address of the first

element, whereas the rear pointer holds the

address of the last element in a queue.

Operations

performed

It performs two operations, push

and pop. The push operation inserts

the element in a list while the pop

operation removes the element

from the list.

It performs mainly two operations, enqueue

and dequeue. The enqueue operation

performs the insertion of the elements in a

queue while the dequeue operation performs

the deletion of the elements from the queue.

Examination of the

empty condition

If top==-1, which means that the

stack is empty.

If front== -1 or front = rear+1, which means

that the queue is empty.

Examination of full

condition

If top== max-1, this condition

implies that the stack is full.

If rear==max-1, this condition implies that the

stack is full.

Variants It does not have any types. It is of three types like priority queue, circular

queue and double ended queue.

Implementation It has a simpler implementation. It has a comparatively complex

implementation than a stack.

Visualization A Stack is visualized as a vertical

collection.

A Queue is visualized as a horizontal

collection.

Queue

1. A queue can be defined as an ordered list which enables insert operations to be

performed at one end called REAR and delete operations to be performed at another

end called FRONT.

2. Queue is referred to be as First In First Out list.

3. For example, people waiting in line for a rail ticket form a queue.

Applications of Queue

Due to the fact that queue performs actions on first in first out basis which is quite fair

for the ordering of actions. There are various applications of queues discussed as below.

1. Queues are widely used as waiting lists for a single shared resource like printer, disk,

CPU.

2. Queues are used in asynchronous transfer of data (where data is not being transferred at

the same rate between two processes) for eg. pipes, file IO, sockets.

3. Queues are used as buffers in most of the applications like MP3 media player, CD player,

etc.

4. Queue are used to maintain the play list in media players in order to add and remove the

songs from the play-list.

5. Queues are used in operating systems for handling interrupts.

Types of Queues

Before understanding the types of queues, we first look at 'what is Queue'.

What is the Queue?

A queue in the data structure can be considered similar to the queue in the real-world. A

queue is a data structure in which whatever comes first will go out first. It follows the

FIFO (First-In-First-Out) policy. In Queue, the insertion is done from one end known as

the rear end or the tail of the queue, whereas the deletion is done from another end

known as the front end or the head of the queue. In other words, it can be defined as a

list or a collection with a constraint that the insertion can be performed at one end

called as the rear end or tail of the queue and deletion is performed on another end

called as the front end or the head of the queue.

Operations on Queue

There are two fundamental operations performed on a Queue:

o Enqueue: The enqueue operation is used to insert the element at the rear end of

the queue. It returns void.

o Dequeue: The dequeue operation performs the deletion from the front-end of

the queue. It also returns the element which has been removed from the front-

end. It returns an integer value. The dequeue operation can also be designed to

void.

o Peek: This is the third operation that returns the element, which is pointed by the

front pointer in the queue but does not delete it.

o Queue overflow (isfull): When the Queue is completely full, then it shows the

overflow condition.

o Queue underflow (isempty): When the Queue is empty, i.e., no elements are in

the Queue then it throws the underflow condition.

A Queue can be represented as a container opened from both the sides in which the

element can be enqueued from one side and dequeued from another side as shown in

the below figure:

What are the use cases of Queue?

Here, we will see the real-world scenarios where we can use the Queue data structure.

The Queue data structure is mainly used where there is a shared resource that has to

serve the multiple requests but can serve a single request at a time. In such cases, we

need to use the Queue data structure for queuing up the requests. The request that

arrives first in the queue will be served first. The following are the real-world scenarios in

which the Queue concept is used:

o Suppose we have a printer shared between various machines in a network, and

any machine or computer in a network can send a print request to the printer.

But, the printer can serve a single request at a time, i.e., a printer can print a

single document at a time. When any print request comes from the network, and

if the printer is busy, the printer's program will put the print request in a queue.

o . If the requests are available in the Queue, the printer takes a request from the

front of the queue, and serves it.

o The processor in a computer is also used as a shared resource. There are multiple

requests that the processor must execute, but the processor can serve a single

request or execute a single process at a time. Therefore, the processes are kept in

a Queue for execution.

Types of Queue

There are four types of Queues:

o Linear Queue

In Linear Queue, an insertion takes place from one end while the deletion occurs from

another end. The end at which the insertion takes place is known as the rear end, and

the end at which the deletion takes place is known as front end. It strictly follows the

FIFO rule. The linear Queue can be represented, as shown in the below figure:

The above figure shows that the elements are inserted from the rear end, and if we

insert more elements in a Queue, then the rear value gets incremented on every

insertion. If we want to show the deletion, then it can be represented as:

In the above figure, we can observe that the front pointer points to the next element,

and the element which was previously pointed by the front pointer was deleted.

The major drawback of using a linear Queue is that insertion is done only from the rear

end. If the first three elements are deleted from the Queue, we cannot insert more

elements even though the space is available in a Linear Queue. In this case, the linear

Queue shows the overflow condition as the rear is pointing to the last element of the

Queue.

o Circular Queue

In Circular Queue, all the nodes are represented as circular. It is similar to the linear

Queue except that the last element of the queue is connected to the first element. It is

also known as Ring Buffer as all the ends are connected to another end. The circular

queue can be represented as:

The drawback that occurs in a linear queue is overcome by using the circular queue. If

the empty space is available in a circular queue, the new element can be added in an

empty space by simply incrementing the value of rear.

o Priority Queue

A priority queue is another special type of Queue data structure in which each element

has some priority associated with it. Based on the priority of the element, the elements

are arranged in a priority queue. If the elements occur with the same priority, then they

are served according to the FIFO principle.

In priority Queue, the insertion takes place based on the arrival while the deletion occurs

based on the priority. The priority Queue can be shown as:

The above figure shows that the highest priority element comes first and the elements

of the same priority are arranged based on FIFO structure.

o Deque

Both the Linear Queue and Deque are different as the linear queue follows the FIFO

principle whereas, deque does not follow the FIFO principle. In Deque, the insertion and

deletion can occur from both ends.

Linear vs Circular Queue

What is a Linear Queue?

A linear queue is a linear data structure that serves the request first, which has been

arrived first. It consists of data elements which are connected in a linear fashion. It has

two pointers, i.e., front and rear, where the insertion takes place from the front end, and

deletion occurs from the front end.

Operations on Linear Queue

There are two operations that can be performed on a linear queue:

o Enqueue: The enqueue operation inserts the new element from the rear end.

o Dequeue: The dequeue operation is used to delete the existing element from the

front end of the queue.

What is a Circular Queue?

As we know that in a queue, the front pointer points to the first element while the rear

pointer points to the last element of the queue. The problem that arises with the linear

queue is that if some empty cells occur at the beginning of the queue then we cannot

insert new element at the empty space as the rear cannot be further incremented.

A circular queue is also a linear data structure like a normal queue that follows the FIFO

principle but it does not end the queue; it connects the last position of the queue to the

first position of the queue. If we want to insert new elements at the beginning of the

queue, we can insert it using the circular queue data structure.

In the circular queue, when the rear reaches the end of the queue, then rear is reset to

zero. It helps in refilling all the free spaces. The problem of managing the circular queue

is overcome if the first position of the queue comes after the last position of the queue.

Conditions for the queue to be a circular queue

o Front ==0 and rear=n-1

o Front=rear+1

If either of the above conditions is satisfied means that the queue is a circular queue.

Operations on Circular Queue

The following are the two operations that can be performed on a circular queue are:

o Enqueue: It inserts an element in a queue. The given below are the scenarios that

can be considered while inserting an element:

1. If the queue is empty, then the front and rear are set to 0 to insert a new

element.

2. If queue is not empty, then the value of the rear gets incremented.

3. If queue is not empty and rear is equal to n-1, then rear is set to 0.

o Dequeue: It performs a deletion operation in the Queue. The following are the

points or cases that can be considered while deleting an element:

1. If there is only one element in a queue, after the dequeue operation is

performed on the queue, the queue will become empty. In this case, the

front and rear values are set to -1.

2. If the value of the front is equal to n-1, after the dequeue operation is

performed, the value of the front variable is set to 0.

3. If either of the above conditions is not fulfilled, then the front value is

incremented.

Differences between linear Queue and Circular Queue

Basis of

comparison

Linear Queue Circular Queue

Meaning The linear queue is a type of linear

data structure that contains the

elements in a sequential manner.

The circular queue is also a linear data structure

in which the last element of the Queue is

connected to the first element, thus creating a

circle.

Insertion and

Deletion

In linear queue, insertion is done

from the rear end, and deletion is

done from the front end.

In circular queue, the insertion and deletion can

take place from any end.

Memory space The memory space occupied by the

linear queue is more than the circular

queue.

It requires less memory as compared to linear

queue.

Memory

utilization

The usage of memory is inefficient. The memory can be more efficiently utilized.

Order of It follows the FIFO principle in order It has no specific order for execution.

execution to perform the tasks.

Array representation of Queue

We can easily represent queue by using linear arrays. There are two variables i.e. front

and rear, that are implemented in the case of every queue. Front and rear variables point

to the position from where insertions and deletions are performed in a queue. Initially,

the value of front and queue is -1 which represents an empty queue. Array

representation of a queue containing 5 elements along with the respective values of

front and rear, is shown in the following figure.

The above figure shows the queue of characters forming the English word "HELLO".

Since, No deletion is performed in the queue till now, therefore the value of front

remains -1 . However, the value of rear increases by one every time an insertion is

performed in the queue. After inserting an element into the queue shown in the above

figure, the queue will look something like following. The value of rear will become 5

while the value of front remains same.

After deleting an element, the value of front will increase from -1 to 0. however, the

queue will look something like following.

Algorithm to insert any element in a queue

Check if the queue is already full by comparing rear to max - 1. if so, then return an

overflow error.

If the item is to be inserted as the first element in the list, in that case set the value of

front and rear to 0 and insert the element at the rear end.

Otherwise keep increasing the value of rear and insert each element one by one having

rear as the index.

Algorithm

o Step 1: IF REAR = MAX - 1

Write OVERFLOW

Go to step

[END OF IF]

o Step 2: IF FRONT = -1 and REAR = -1

SET FRONT = REAR = 0

ELSE

SET REAR = REAR + 1

[END OF IF]

o Step 3: Set QUEUE[REAR] = NUM

o Step 4: EXIT

C Function

1. void insert (int queue[], int max, int front, int rear, int item)

2. {

3. if (rear + 1 == max)

4. {

5. printf("overflow");

6. }

7. else

8. {

9. if(front == -1 && rear == -1)

10. {

11. front = 0;

12. rear = 0;

13. }

14. else

15. {

16. rear = rear + 1;

17. }

18. queue[rear]=item;

19. }

20. }

Algorithm to delete an element from the queue

If, the value of front is -1 or value of front is greater than rear , write an underflow

message and exit.

Otherwise, keep increasing the value of front and return the item stored at the front end

of the queue at each time.

Algorithm

o Step 1: IF FRONT = -1 or FRONT > REAR

Write UNDERFLOW

ELSE

SET VAL = QUEUE[FRONT]

SET FRONT = FRONT + 1

[END OF IF]

o Step 2: EXIT

C Function

1. int delete (int queue[], int max, int front, int rear)

2. {

3. int y;

4. if (front == -1 || front > rear)

5.

6. {

7. printf("underflow");

8. }

9. else

10. {

11. y = queue[front];

12. if(front == rear)

13. {

14. front = rear = -1;

15. else

16. front = front + 1;

17.

18. }

19. return y;

20. }

21. }

Menu driven program to implement queue using array 1. #include<stdio.h>

2. #include<stdlib.h>

3. #define maxsize 5

4. void insert();

5. void delete();

6. void display();

7. int front = -1, rear = -1;

8. int queue[maxsize];

9. void main ()

10. {

11. int choice;

12. while(choice != 4)

13. {

14. printf("\n*************************Main Menu*****************************\n");

15. printf("\n=====================================================

============\n");

16. printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");

17. printf("\nEnter your choice ?");

18. scanf("%d",&choice);

19. switch(choice)

20. {

21. case 1:

22. insert();

23. break;

24. case 2:

25. delete();

26. break;

27. case 3:

28. display();

29. break;

30. case 4:

31. exit(0);

32. break;

33. default:

34. printf("\nEnter valid choice??\n");

35. }

36. }

37. }

38. void insert()

39. {

40. int item;

41. printf("\nEnter the element\n");

42. scanf("\n%d",&item);

43. if(rear == maxsize-1)

44. {

45. printf("\nOVERFLOW\n");

46. return;

47. }

48. if(front == -1 && rear == -1)

49. {

50. front = 0;

51. rear = 0;

52. }

53. else

54. {

55. rear = rear+1;

56. }

57. queue[rear] = item;

58. printf("\nValue inserted ");

59.

60. }

61. void delete()

62. {

63. int item;

64. if (front == -1 || front > rear)

65. {

66. printf("\nUNDERFLOW\n");

67. return;

68.

69. }

70. else

71. {

72. item = queue[front];

73. if(front == rear)

74. {

75. front = -1;

76. rear = -1 ;

77. }

78. else

79. {

80. front = front + 1;

81. }

82. printf("\nvalue deleted ");

83. }

84.

85.

86. }

87.

88. void display()

89. {

90. int i;

91. if(rear == -1)

92. {

93. printf("\nEmpty queue\n");

94. }

95. else

96. { printf("\nprinting values .....\n");

97. for(i=front;i<=rear;i++)

98. {

99. printf("\n%d\n",queue[i]);

100. }

101. }

102. }

Output:

*************Main Menu**************

==============================================

1.insert an element

2.Delete an element

3.Display the queue

4.Exit

Enter your choice ?1

Enter the element

123

Value inserted

*************Main Menu**************

==============================================

1.insert an element

2.Delete an element

3.Display the queue

4.Exit

Enter your choice ?1

Enter the element

90

Value inserted

*************Main Menu**************

===================================

1.insert an element

2.Delete an element

3.Display the queue

4.Exit

Enter your choice ?2

value deleted

*************Main Menu**************

==============================================

1.insert an element

2.Delete an element

3.Display the queue

4.Exit

Enter your choice ?3

printing values .....

90

*************Main Menu**************

==============================================

1.insert an element

2.Delete an element

3.Display the queue

4.Exit

Enter your choice ?4

Drawback of array implementation

Although, the technique of creating a queue is easy, but there are some drawbacks of

using this technique to implement a queue.

o Memory wastage : The space of the array, which is used to store queue elements, can

never be reused to store the elements of that queue because the elements can only be

inserted at front end and the value of front might be so high so that, all the space before

that, can never be filled.

The above figure shows how the memory space is wasted in the array representation of

queue. In the above figure, a queue of size 10 having 3 elements, is shown. The value of

the front variable is 5, therefore, we can not reinsert the values in the place of already

deleted element before the position of front. That much space of the array is wasted

and can not be used in the future (for this queue).

o Deciding the array size

On of the most common problem with array implementation is the size of the array

which requires to be declared in advance. Due to the fact that, the queue can be

extended at runtime depending upon the problem, the extension in the array size is a

time taking process and almost impossible to be performed at runtime since a lot of

reallocations take place. Due to this reason, we can declare the array large enough so

that we can store queue elements as enough as possible but the main problem with this

declaration is that, most of the array slots (nearly half) can never be reused. It will again

lead to memory wastage.

Stacks And Stack Operations

A stack is a simple last-in, first-out (LIFO) data structure. That is, the last data

element stored in a stack is the first data element retrieved from the stack. The

common analogy is a stack of plates in a cafeteria: when you go through the line,

you pop the top plate off the stack; the dish washer (stepping away from reality a bit),

pushes a single clean plate on top of the stack. So a stack supports two basic

operations: push and pop. Some stacks also provide additional operations: size (the

number of data elements currently on the stack) and peek (look at the top element

without removing it).

Fig-1 The primary stack operations. A new data element is stored by pushing it on the top of the

stack. A data element is retrieved by popping the top element off the top of the stack and returning it.

Stacks are an important data structure in their own right and they may be implemented

in several ways. We will use one of the ways of building a stack as an example of how

to create and use arrays. The bottom of the stack is the first element of the array (i.e.,

the element at index location 0). But how do we know the location of the element at

the top of the stack? Keeping track of the top element requires a second bit of

information, an integer called the stack pointer that denotes the current top of the

stack. The stack pointer is just an index into the array that represents the stack.

Fig2 Implementing a stack as an array. To better match the typical depiction of a stack, the array is

drawn vertically rather than horizontally with the beginning of the array at the bottom and the other

end left open. The array index numbers are shown on the left side of the array. Initially, the stack is

empty, which is denoted by setting sp to 0.

We encounter two problems when we implement a stack as an array. Both the size of

the stack (i.e., the maximum number of elements that the stack can hold) and the type

of data that can be stored in the stack are limited by the requirement that the size and

type of the array must be specified when the array is defined (i.e., when we write the

code). We will return to and refine this initial implementation of a stack in subsequent

chapters. Each refinement will get us closer to removing these (and other) problems

but we won't see the final, clean result until we cover templates near the end of the

text.

The following discussion focuses on how stacks work or behave and how that

behavior can be achieved with arrays. So for now, we "solve" the two problems

presented above by simply creating a stack that can only store characters implemented

as a char array whose size is left ambiguous (specified as a macro, enum, or const).

char st[SIZE];

int sp = 0;

Stack Behavior

The various stack operations are easy to implement, but notice that push and pop

use post-increment and pre-decrement respectively (this is crucial for the algorithm to

work).

push

st[sp++] = data (sp must be < SIZE)

pop

return st[--sp] (sp must be > 0)

size

return sp;

peek

return st[sp-1]

Based on these operations, the snapshots shown in Figure 3 illustrate the appearance

of a stack as data (characters) are stored in or pushed on to it.

Operation Picture Execution

Stack is empty

push('A');

st[0] = 'A';

sp = 0 + 1;

push('B');

st[1] = 'B';

sp = 1 + 1;

push('C');

st[2] = 'C';

sp = 2 + 1;

Fig:3 The push operation illustrated. Each call to the push function (left column) pushes a data element

on to the stack. The main instruction in the push function is st[sp++] = data, where "data" is the

function argument. The middle column abstractly illustrates how the stack (the array and the stack

pointer) appear after each call to the push function. The right column breaks the behavior of the push

function in to two steps.

Similarly, the data (characters) stored in a stack can be retrieved from or popped off of

it.

Operation Picture Execution

data = pop();

sp = 3 - 1;

return sp[2];

data = pop();

sp = 2 - 1;

return sp[2];

data = pop();

sp = 1 - 1;

return sp[2];

Fig:4 The pop operation illustrated. Each call to the pop function (middle column) removes a data

element from the top of the stack and returns it. The main instruction in the pop function is return

st[--sp]. The middle column abstractly illustrates how the stack (the array and the stack pointer)

appear after each call to the pop function. The right column breaks the behavior of the pop function in

to two steps. Notice that the pop operations doesn't actually remove a character from the stack array,

but the slots at and above the stack pointer are treated as empty. The next push operation will

overwrite the data currently stored at the stack pointer. The stack appearing at the bottom of the table

is logically empty.

Maintaining a stack as two discrete variables (an array and a stack pointer) is

cumbersome, error-prone, and makes it difficult to support multiple stacks in a

program. Fortunately, we can solve these problems (if somewhat inelegantly) if we

implement a stack as a struct. But even after settling on a structure-based solution,

there are still two possible paths that we can take: the first implementation is based on

automatic variables while the second is based on dynamic variables. More elegant

solutions based on classes and templates will follow in subsequent chapters.