UMBC CS 201, Fall 05
UMBC CMSC 201 Fall '05
CSEE | 201 | 201 F'05 | lectures | news | help

The Stack

Implementations using arrays

Initial Implementation

Similar to the queue, we can implement a stack as an array. Again, let us create an array of some size to hold all of the data that think we will ever need to put into it. Let us also maintain the top of the stack in terms of the corresponding indicies.

   int myStack[5];
   int top;


To Push, all we need to do is insert at the end of the used portion of the array. In other words insert immediately past the top and then increment top to reflect the new top of the stack.

Initially we have top = -1, as it does not really point to anything in the array, and thus should not have a valid array index.

0 1 2 3 4

So lets push...

   Push ( myStack, &top, 5 );
0 1 2 3 4

Now we have top = 0, as the top of the stack is at myStack[0].

Then after a couple more inserts...

   Push ( myStack, &top, 1 );
   Push ( myStack, &top, 6 );
5 1 6    
0 1 2 3 4

Now we have top = 2

Again, what happens is we want to do even more inserts...

   Push ( myStack, &top, 1 );
   Push ( myStack, &top, 9 );
   Push ( myStack, &top, 7 );
   Push ( myStack, &top, 8 );
5 1 6 1 9
0 1 2 3 4

Again, we run into issues with filling up the array.


Unlike queues, stacks do not have a problem when removing an element from the array. All we are ever doing popping off what is at the end of the array. Thus we never have any problems with creating gaps in the array.

For example, given the following stack where top = 3, let us see what happens when popping...

5 1 6 1  
0 1 2 3 4

Okay, lets pop...

   tmp = Pop ( myStack, &top );
5 1 6    
0 1 2 3 4

All we had to do was to return the value stored at myStack[top], and decrement top to reflect the new top of the stack, so top = 2. No gaps were ever created.

Problems with this Initial Array Implementation

Array Implementation - as good as it gets

Just like with the queue, we can take it a step further and manually allocate a contiguous chunk of memory ourselves. This will allow us to potentially allocate more memory as needed, thus in essence allowing us to be able to resize the stack. This will require us to keep track of the current size of the array allocated for stack use.

However, unlike the queue, we will never have the case where data wrapped around to the beginning of the array. The top is always to the right and the bottom is always to the left, thus we can copy over directly and simple insert it at the end.

Problems with this "as good as it gets" Implementation

Implementation using a linked list

Since we already know about linked lists, we should consider using a linked list implementation for the stack. The stack can be implemented without wasting any space, and by doing pointer manipulation rather than a series of value moves, we can make our stack work much faster than an array implementation could.

Benefits of using a linked list

The user interface for the linked list implementation

/************************************************\ * Filename: stack.h * * Author: Sue Bogar * * Date Written: 4/25/98 * * Section: 101 * * EMail: * * * * Description: This file contains the function * * prototypes to work with stack.c. * * This set of functions provide the operations * * needed including adding an item to the stack, * * pushing; deleting an item from the stack, * * popping; determining if the stack is empty and * * the printing the items in the stack. * * Since the stack is being implemented as a * * linked list, some functions needed for a list * * have been added to this file. Those functions * * are CreateNode and SetData. * \************************************************/ #ifndef _stack_h #define _stack_h /****************** * This typedef allows us to call the type * of a pointer to a node a NODEPTR ******************/ typedef struct node *NODEPTR; /****************** * The queue is being implemented as a linked list * and a linked list requires nodes. Each node is * a structure that has two members, the first to * hold data and the second, of type nodePtr is * a pointer to the next node in the list. ******************/ typedef struct node { int data; NODEPTR next; }NODE; /****************** * Push takes a pointer to NODEPTR as its first * argument, which holds the address of top. * The second argument is a pointer to the node * that's to be inserted. Push will insert the * item at the top of the stack. The address of * top is passed into this function, because the * function may need to change the address held * in top. ******************/ void Push (NODEPTR *topPtr, NODEPTR temp); /****************** * Pop takes a pointer to a NODEPTR as its * only argument. It will hold the address * of top. This function removes an item from * the stack and returns the data value stored * there. This function may alter the value * of top. ******************/ int Pop (NODEPTR *topPtr); /****************** * IsEmpty takes a NODEPTR as its first * argument, which is top. It determines * if the stack is empty and returns 1 (true) * if the stack is empty and 0 (false) if it * is not empty. ******************/ int IsEmpty (NODEPTR head); /****************** * PrintStack takes a NODEPTR as an argument * which is initially the top. The stack is * traversed and the value of the data member * of each item is printed. ******************/ void PrintStack (NODEPTR curr); /****************** * CreateNode mallocs the space needed to * hold a struct of type node, initializes * the members, and returns a pointer to * the new node. ******************/ NODEPTR CreateNode (void); /****************** * SetData sets the value passed in into the * data member of the node pointed to by the * NODEPTR it receives as an argument. ******************/ void SetData (NODEPTR temp, int value); #endif

CSEE | 201 | 201 F'05 | lectures | news | help

Thursday, 29-Sep-2005 00:00:56 EDT