Lecture 14:  Abstract Data Types –Stack

 

Objectives of this lecture

q       Learn how ADTs are implemented generally

q       Implement Stack using Array

q       Learn how to apply stack in problem solving

 

Implementation of ADTs

q       As explained earlier, an ADT is a data model designed to solve general problems that have some common properties.

q       The model usually consists of a data structure and a set of operations that are allowed on the structure

q       ADTs are usually implemented following the principle of information hiding.

q       This involves breaking the implementation into two – user interface and implementation details.

q       The user interface defines the structure and a set of function prototypes that implement the allowed operations.  These are made available to the user so that he can see how the ADT works.

q       The implementation details implements the operations of the ADT and is hidden from the user .  This has the advantage that:

Ø      The implementation of the operations can be changed (if need be) without changing the applications that use them.

Ø      It also provides a protection to the structure since it can only be manipulated using the prescribed set of operations.

q       In C, Information hiding is achieved using Header and Library files – The user interface is stored in a header file and the implementation details is stored as a library.

 

What is Stack?

q       Stack is a linear structure that holds homogeneous data and has the property that data is entered (pushed) and removed (popped) at only one end called top.

q       Thus, the last element to be pushed onto the stack is always the last to be popped –hence it is sometimes called a last-in-first-out (LIFO) structure.

q       Stack has many applications especially in system software and operating system.

q       The common operations that are defined on stack are:

Create_stack – make stack logically accessible

Destroy_stack – make stack logically inaccessible

Empty_stack – checks if a stack is empty

Full_stack – checks if a stack is full

Push  - add item ot the top of the stack

Pop – remove item from the top of the stack.

 

Implementation of Stack using Array

q       One way of implementing a stack is to use a structure consisting of an array (to hold the items of the stack and an int to hold the positions of top.

 

stack

 

 

 

 

 

 

 

 

 

 

top

 

 

 

 

 

 

 

 

 

 

 

The user_inteface:

q       This consists of the definition of constants and types for the structure as well as function prototypes for the operations that are allowed.  This should be stored in a header file stack.h

 

#define MAX_STACK 100

 

typedef char ITEM_TYPE

typedef struct {  ITEM_TYPE item[MAX_STACK];

                    int top;

                          } STACK_TYPE;

typedef enum {FALSE, TRUE} BOOLEAN;

 

void create_stack(STACK_TYPE *stack);

void destroy_stack(STACK_TYPE *stack);

BOOLEAN empy_stack(STACK_TYPE *stack);

BOOLEAN full_stack(STACK_TYPE *stack);

void push(STACK_TYPE *stack, ITEM_TYPE newitem);

void pop(STACK_TYPE *stack, ITEM_TYPE *old_item);

 

q       Note:  ITEM_TYPE can be changed by the user.  This makes the implementation to work for any type.

q       The user can also change the MAX_STACK to suit the problem he is solving.

 

Implementation details

q       These can be stored in a separate library file (stack.c) to hide it from the user.

#include <stdio.h>

#include "stack.h"

 

/* initializes stack by setting top to 0 */

void create_stack(STACK_TYPE *stack)

{ stack->top=0;

}

 

/* resets stack by resetting top to 0 */

void destroy_stack(STACK_TYPE *stack)

{  stack->top=0;

}

 

q       Notice that although the create_stack and  destroy_stack are the same in this implementation, it is better to keep them separate since in another implementation, they could be different.

/* checks if stack is empty (if top is 0) */

BOOLEAN empty_stack(STACK_TYPE *stack)

{ if (stack->top==0)

     return TRUE;

  else

     return FALSE;

}

        

/* checks if stack is full (if top is MAX_STACK) */

BOOLEAN full_stack(STACK_TYPE *stack)

{ if (stack->top == MAX_STACK)

     return TRUE;

  else

     return FALSE;

}

 

/* put item at position top and then increment top */

void push(STACK_TYPE *stack, ITEM_TYPE newitem)

{  stack->item[stack->top]=newitem;

   stack->top++;

}

 /* decrement top by 1 and then copy the item at top */

void pop(STACK_TYPE *stack, ITEM_TYPE *old_item)

{   stack->top--;

    *old_item=stack->item[stack->top];

}

 

q       Note that top always points to the next free location.  Thus, in push, we add an item in the current position of top, then increment top by one.  In pop, we decrement top by one, then return the element at that position (logically making the position free).

q       Note also that before the function push is called, full_stack must be called to make sure that the stack is not full.

q       Similarly, before pop is called, empty_stack must be called to make sure that stack is not empty.

 

Example:

q       The following program shows how ADT stack may be used in a program:

 

#include <stdio.h>

#include "stack.h"

 

void input_stack(STACK_TYPE *stack);

void output_stack (STACK_TYPE *stack);

 

main()

{  STACK_TYPE stack;

   create_stack(&stack);

   input_stack(&stack);

   output_stack(&stack);

   destroy_stack(&stack);

   return 0;

}

 

void input_stack(STACK_TYPE *stack)

{  ITEM_TYPE in_value;

   printf("\nEnter a string of caharacters: ");

   while (((in_value=getchar()) != '\n') &&

             (full_stack(stack)==FALSE))

               push(stack,in_value);

}

 

void output_stack (STACK_TYPE *stack)

{  ITEM_TYPE out_value;

   printf("\nThe string reversed is: ");

   while ( empty_stack(stack)== FALSE)

   {    pop(stack,&out_value);

          putchar(out_value);

   }

}

 

Application of Stack –Infix to Postfix conversion

q       In conventional arithmetic, binary operators are between the two operands that it operates on.  This is called infix notation.

q       A problem with infix notation is the ambiguity involved in evaluating expressions with that notation. E.g   2+3*4 (is it equal to 20 or 14?)

q       The table of precedence rule must then be applied to resolve the problem.

q       Another way of representing expressions is to use prefix (operator before operands) or postfix (operator after operands).

q       In either of these approaches, the precedence rule is embedded in the expression, making it more efficient to evaluate the expression.

q       Because of this efficiency, most compilers convert infix expressions to postfix before generating the code.

 

q       The following table shows sample expressions in all three format:

Prefix

Infix

Postfix

* 3 4

3 * 4

3 4 *

+ 3 * 4 5

3 + 4 * 5

3 4 5 * +

* + 3 4 5

(3 + 4) * 5

3 4 + 5 *

 

 

q       A postfix expression is evaluated by scanning through the expression from left to right.  Each time an operator is encountered, it is applied on the last two operands.

 

Example:  3 4 5 * + à3 20 + à 23.

 

 

q       The following program uses stack to evaluate a postfix expressions.

 

#include <stdio.h>

#include "stack.h"

 

main()

{ char in_char;

  ITEM_TYPE op1, op2, result;

  STACK_TYPE op_stack;

 

  create_stack(&op_stack);

 

  while ((in_char=getchar()) != '\n'  &&

           full_stack(&op_stack)==FALSE)

  { if (in_char != ' ')

    if (in_char >= '0' && in_char <= '9')

       push(&op_stack, in_char - '0');

    else

    {  pop(&op_stack, &op2);

       pop(&op_stack, &op1);

       switch (in_char)

       {  case '+' : push(&op_stack, op1+op2); break;

            case '-' : push(&op_stack, op1-op2); break;

           case '*' : push(&op_stack, op1*op2); break;

           case '/' : push(&op_stack, op1/op2); break;

            default : printf("nError in input");

       }

    }

  }

 

  pop(&op_stack, &result);

  printf("\nResult = %d\n\n", result);

  destroy_stack(&op_stack);

 

  return 0;

}