IT Solution Menu

N O T I C E

Project on Computer topics, Programs for practicals for BCA Students, and Mobile Android app requirements. Please pay less and get more immediate delivery for school and higher level. Thank You. Contact me - at +91-8800304018(w)

Thursday, October 24, 2024

Data Structure Stack & Queue Handling #4 by Md Farrukh Asif

 

Data Structure Stack & Queue Handling #4

by Md Farrukh Asif

Ø In this article we will discuss in deep about the Stacks and Queues.

Ø In this article you will also show the difference between infix, prefix, and postfix.

Ø We will try to solve stack and Queue with array(static) and linked lists(dynamic).

Here we have used our full efforts and attempts to execute the operations for you, hence we request all of you to comment and share with love.

Thank You…..

Stack:

A stack is an Abstract Data Type (ADT), commonly used in most programming languages. It is named stack as it behaves like a real-world stack, for example – a deck of cards or a pile of plates, etc.

A real-world stack


Allows operations at one end only. For example, we can place or remove a card or plate from the top of the stack only. Likewise, Stack ADT allows all data operations at one end only. At any given time, we can only access the top element of a stack.

This feature makes it a LIFO data structure. LIFO stands for Last-In-First-Out. Here, the element which is placed (inserted or added) last, is accessed first. In stock terminology, the insertion operation is called the PUSH operation and the removal operation is called the POP operation.

Stack Representation

The following diagram depicts a stack and its operations −


A stack can be implemented through an Array, Structure, Pointer, and Linked List. Stack can either be a fixed size one or it may have a sense of dynamic resizing. Here, we are going to implement a stack using arrays, which makes it a fixed-size stack implementation.

Basic Operations

Stack operations may involve initializing the stack, using it, and then de-initializing it. Apart from these basic stuffs, a stack is used for the following two primary operations −

·        push() − Pushing (storing) an element on the stack.

·        pop() − Removing (accessing) an element from the stack.

When data is PUSHed onto the stack.

To use a stack efficiently, we need to check the status of stack as well. For the same purpose, the following functionality is added to stacks −

·        peek() − get the top data element of the stack, without removing it.

·        isFull() − check if stack is full.

·        isEmpty() − check if stack is empty.

At all times, we maintain a pointer to the last PUSHed data on the stack. As this pointer always represents the top of the stack, hence named top. The top pointer provides the top value of the stack without actually removing it.

First we should learn about procedures to support stack functions −

peek()

Algorithm of peek() function −

begin procedure peek

   return stack[top]

end procedure

Implementation of peek() function in C programming language −

Example

int peek() {

   return stack[top];

}

isfull()

Algorithm of isfull() function −

begin procedure isfull

   if top equals to MAXSIZE

      return true

   else

      return false

   endif

end procedure

Implementation of isfull() function in C programming language −

Example

bool isfull() {

   if(top == MAXSIZE)

      return true;

   else

      return false;

}

isempty()

Algorithm of isempty() function −

begin procedure isempty

   if top less than 1

      return true

   else

      return false

   endif

end procedure

Implementation of isempty() function in C programming language is slightly different. We initialize top at -1, as the index in array starts from 0. So we check if the top is below zero or -1 to determine if the stack is empty. Here's the code −

Example

bool isempty() {

   if(top == -1)

      return true;

   else

      return false;

}

Push Operation

The process of putting a new data element onto a stack is known as a Push Operation. Push operation involves a series of steps −

·        Step 1 − Check if the stack is full.

·        Step 2 − If the stack is full, produce an error and exit.

·        Step 3 − If the stack is not full, increments top to point next empty space.

·        Step 4 − Adds data element to the stack location, where top is pointing.

·        Step 5 − Returns success.



If the linked list is used to implement the stack, then in step 3, we need to allocate space dynamically.

Algorithm for PUSH Operation

A simple algorithm for Push operation can be derived as follows −

begin procedure push: stack, data

   if stack is full

      return null

   endif

   top top + 1

   stack[top] data

end procedure

Implementation of this algorithm in C, is very easy. See the following code −

Example

void push(int data) {

   if(!isFull()) {

      top = top + 1;  

      stack[top] = data;

   } else {

      printf("Could not insert data, Stack is full.\n");

   }

}

Pop Operation

Accessing the content while removing it from the stack, is known as a Pop Operation. In an array implementation of pop() operation, the data element is not actually removed, instead top is decremented to a lower position in the stack to point to the next value. But in linked-list implementation, pop() actually removes data element and deallocates memory space.

A Pop operation may involve the following steps −

·        Step 1 − Check if the stack is empty.

·        Step 2 − If the stack is empty, produce an error and exit.

·        Step 3 − If the stack is not empty, access the data element at which the top is pointing.

·        Step 4 − Decreases the value of the top by 1.

·        Step 5 − Returns success.


Algorithm for Pop Operation

A simple algorithm for Pop operation can be derived as follows −

begin procedure pop: stack

   if stack is empty

      return null

   endif

   data stack[top]

   top top - 1

   return data

end procedure

Implementation of this algorithm in C, is as follows −

Example

int pop(int data) {

   if(!isempty()) {

      data = stack[top];

      top = top - 1;  

      return data;

   } else {

      printf("Could not retrieve data, Stack is empty.\n");

   }

}

 

The way to write arithmetic expression is known as a notation. An arithmetic expression can be written in three different but equivalent notations, i.e., without changing the essence or output of an expression. These notations are −

  • Infix Notation
  • Prefix (Polish) Notation
  • Postfix (Reverse-Polish) Notation

These notations are named as how they use operator in expression. We shall learn the same here in this chapter.

Infix Notation

We write expressions in infix notation, e.g. a - b + c, where operators are used in-between operands. It is easy for us humans to read, write, and speak in infix notation but the same does not go well with computing devices. An algorithm to process infix notation could be difficult and costly in terms of time and space consumption.

Prefix Notation

In this notation, the operator is prefixed to operands, i.e. operator is written ahead of operands. For example, +ab. This is equivalent to its infix notation a + b. Prefix notation is also known as Polish Notation.

Postfix Notation

This notation style is known as Reversed Polish Notation. In this notation style, the operator is postfixed to the operands i.e., the operator is written after the operands. For example, ab+. This is equivalent to its infix notation a + b.

The following table briefly tries to show the difference in all three notations −

Sr.No.

Infix Notation

Prefix Notation

Postfix Notation

1

a + b

+ a b

a b +

2

(a + b) c

+ a b c

a b + c

3

a (b + c)

a + b c

a b c +

4

a / b + c / d

+ / a b / c d

a b / c d / +

5

(a + b) (c + d)

+ a b + c d

a b + c d +

6

((a + b) c) - d

- + a b c d

a b + c d -

Parsing Expressions

As we have discussed, it is not a very efficient way to design an algorithm or program to parse infix notations. Instead, these infix notations are first converted into either postfix or prefix notations and then computed.

To parse any arithmetic expression, we need to take care of operator precedence and associativity also.

Precedence

When an operand is in between two different operators, which operator will take the operand first, is decided by the precedence of an operator over others. For example −

As multiplication operation precedes addition, b * c will be evaluated first. A table of operator precedence is provided later.

Associativity

Associativity describes the rule where operators with the same precedence appear in an expression. For example, in expression a + b − c, both + and – have the same precedence, then which part of the expression will be evaluated first, is determined by associativity of those operators. Here, both + and − are left associative, so the expression will be evaluated as (a + b) − c.

Precedence and associativity determines the order of evaluation of an expression. Following is an operator precedence and associativity table (highest to lowest) −

Sr.No.

Operator

Precedence

Associativity

1

Exponentiation ^

Highest

Right Associative

2

Multiplication ( ) &

Division ( / )

Second Highest

Left Associative

3

Addition ( + ) &

 Subtraction ( − )

Lowest

Left Associative

The above table shows the default behavior of operators. At any point of time in expression evaluation, the order can be altered by using parenthesis. For example −

In a + b*c, the expression part b*c will be evaluated first, with multiplication as precedence over addition. We here use parenthesis for a + b to be evaluated first, like (a + b)*c.

Postfix Evaluation Algorithm

We shall now look at the algorithm on how to evaluate postfix notation −

Step 1 − scan the expression from left to right

Step 2 − if it is an operand push it to stack

Step 3 − if it is an operator pull operand from the stack and perform the operation

Step 4 − store the output of step 3, back to stack

Step 5 − scan the expression until all operands are consumed

Step 6 − pop the stack and perform operation

 Queue


Queue is an abstract data structure, somewhat similar to Stacks. Unlike stacks, a queue is open at both its ends. One end is always used to insert data (enqueue) and

the other is used to remove data (dequeue). Queue follows First-In-First-Out methodology, i.e., the data item stored first will be accessed first.

A real-world example of queue can be a single-lane one-way road, where the vehicle enters first, exits first. More real-world examples can be seen as queues at the ticket windows and bus-stops.

Queue Representation


As we now understand that in queue, we access both ends for different reasons. The following diagram given below tries to explain queue representation as data structure −

As in stacks, a queue can also be implemented using Arrays, Linked-lists, Pointers and Structures. For the sake of simplicity, we shall implement queues using one-dimensional array.

Basic Operations

Queue operations may involve initializing or defining the queue, utilizing it, and then completely erasing it from the memory. Here we shall try to understand the basic operations associated with queues −

·        enqueue() − add (store) an item to the queue.

·        dequeue() − remove (access) an item from the queue.

Few more functions are required to make the above-mentioned queue operation efficient. These are −

·        peek() − Gets the element at the front of the queue without removing it.

·        isfull() − Checks if the queue is full.

·        isempty() − Checks if the queue is empty.

In the queue, we always dequeue (or access) data, pointed by front pointer and while enqueing (or storing) data in the queue we take help of rear pointer.

Let's first learn about supportive functions of a queue −

peek()

This function helps to see the data at the front of the queue. The algorithm of peek() function is as follows −

Algorithm

begin procedure peek

   return queue[front]

end procedure

Implementation of peek() function in C programming language −

Example

int peek() {

   return queue[front];

}

isfull()

As we are using a single dimension array to implement queue, we just check for the rear pointer to reach at MAXSIZE to determine that the queue is full. In case we maintain the queue in a circular linked list, the algorithm will differ. Algorithm of isfull() function −

Algorithm

begin procedure isfull

 

   if rear equals to MAXSIZE

      return true

   else

      return false

   endif

end procedure

Implementation of isfull() function in C programming language −

Example

bool isfull() {

   if(rear == MAXSIZE - 1)

      return true;

   else

      return false;

}

isempty()

Algorithm of isempty() function −

Algorithm

begin procedure isempty

   if front is less than MIN  OR front is greater than rear

      return true

   else

      return false

   endif

end procedure

If the value of the front is less than MIN or 0, it tells that the queue is not yet initialized, hence empty.

Here's the C programming code −

Example

bool isempty() {

   if(front < 0 || front > rear)

      return true;

   else

      return false;

}

Enqueue Operation

Queues maintain two data pointers, front and rear. Therefore, its operations are comparatively difficult to implement than that of stacks.

The following steps should be taken to enqueue (insert) data into a queue −

·        Step 1 − Check if the queue is full.

·        Step 2 − If the queue is full, produce overflow error and exit.

·        Step 3 − If the queue is not full, increment rear pointer to point the next empty space.

·        Step 4 − Add data element to the queue location, where the rear is pointing.

·        Step 5 − return success.

Sometimes, we also check to see if a queue is initialized or not, to handle any unforeseen situations.

Algorithm for enqueue operation

procedure enqueue(data)     

   if queue is full

      return overflow

   endif

   rear rear + 1

   queue[rear] data

   return true

end procedure

Implementation of enqueue() in C programming language −

Example

int enqueue(int data)     

   if(isfull())

      return 0;

   rear = rear + 1;

   queue[rear] = data;

   return 1;

end procedure

Dequeue Operation



Accessing data from the queue is a process of two tasks − access the data where front is pointing and remove the data after access. The following steps are taken to perform dequeue operation −

·        Step 1 − Check if the queue is empty.

·        Step 2 − If the queue is empty, produce underflow error and exit.

·        Step 3 − If the queue is not empty, access the data where front is pointing.

·        Step 4 − Increment front pointer to point to the next available data element.

·        Step 5 − Return success.

Algorithm for dequeue operation

procedure dequeue

   if queue is empty

      return underflow

   end if

   data = queue[front]

   front front + 1

   return true

end procedure

Implementation of dequeue() in C programming language −

Example

int dequeue() {

   if(isempty())

      return 0;

   int data = queue[front];

   front = front + 1;

   return data;

}

We will discuss more in the next post in detail with another way……

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

 Share Like and Commnt Please

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

No comments:

Post a Comment

Top Popular Post