Syllabus   Labs   Homework   Quizzes  
Notes   Scores   Blank


Lecture Notes
Dr. Tong Yu, Jan. 2013
    1. Basic Concepts
    2. The Standard Library Container Class
    3. Vectors and Component Reuse
    4. Lists: A Dynamic Data Structure
    5. Stacks and Queues
     
    6. Sets and Multisets
    7. Trees
    8. Tree Applications
    9. Hashing
    
    The history of free man is never written by chance
    but by choice -- their choice.
    					Dwight D. Eisenhower
    
    

    Stacks and Queues

    1. Stacks

      a finite list with zero or more elements

      LIFO

      insert, delete all take place at head
      push, pop

      S = ( a0, a1, .... ,an-1 )

      a0 -- bottom element
      ai is on top of ai-1
      an-1 -- top element

      e.g. ( A, B, C, D, E )

      Adaptors
          do not directly hold data values but built on top of other containers.

        stack <vector<int> > s1;     //need space between > >
        stack <list<double> > s2;
        stack <string> s3;

        default is deque

        stack <char> s4;
        is same as
        stack <deque <char> > s4;
        stack s5;     //invalid

    2. An application example -- postfix arithmetic

      infix arithmetic

      postfix arithmetic

      Evaluate a postfix expression

    3. need two stacks
      • postfix stack stores all tokens ( numbers and operators )
      • evaluation stack stores numbers for evaluation

    4. retrieve and delete top element from postfix stack

    5. if obtain a number, save it in evaluation stack

    6. if obtain an operator, get two numbers from evaluation stack:
      • perform operation
      • save result in evaluation stack

    7. repeat until postfix stack is empty
    8. e.g. 13, 4, -, 2, 3, *, +
      postfix
      stack
      evaluation
      stack
      13 
      4 
      - 
      2 
      3 
      * 
      +
      ___
       
      ___
      post
       
      eval
       
        
      4 
      - 
      2 
      3 
      * 
      +
      ___
      13
      ___
      post
       
      eval
       
        
        
      - 
      2 
      3 
      *4
      +
      ___
      13
      ___
      post
       
      eval
       
        
        
        
      2 
      3 
      * 
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
      2 
      3 
      * 
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
        
      3 
      *2
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
        
       3
      *2
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
        
        
       6
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
        
        
       6
      +
      ___
      9
      ___
      post
       
      eval
       
        
        
        
        
        
        
       
      ___
      15
      ___

    9. Queues

      FIFO ( first in first out )

      insertions at one end -- rear ( tail )

      deletions at another end -- front ( head )

      Q = ( a0, a1, .... ,an-1 )

      a0 -- front element
      ai+1 is behind ai
      an-1 -- rear element

      Array

      use two cursors to locate front and rear positions
      wrap-around

      Circular Queue

      maxLength = Nmax, currently N items

      head ( front ) at N - 1, tail ( rear ) at 0

      Linked List

    10. Infix to Postfix conversion

      How to convert from infix to postfix?

      Observations:

      A - B * C + D * E - A / C
    11. Fully parenthesize the expression

      ( ( ( A - ( B * C ) ) + ( D * E ) ) - ( A / C ) )

    12. Observe that each operator associates with one right parenthesis:

      ( ( ( A- ( B * C ) ) + ( D * E ) ) - ( A / C ) )

    13. But

      ( B * C ) is translated to B C *

    14. So we move all operators so that they replace their corresponding right parenthesis

      ( ( ( A   B   C * - ( D   E * + ( A   C / -

    15. Then delete all parenthesis

      A   B   C   *   -   D   E   *   +   A   C   /   -

      which is the required postfix expression

    16. Observe: order of operands ( numbers ) are the same as in prefix and postfix expressions
    17. Since orders of operands of infix and postfix are the same, we can scan the infix expression and form postfix expression by parsing the operands and use a queue to hold them
      on the other hand we can use an stack to hold the operators

      e.g. A + B * C

      infix
      stack
      operator
      stack
      postfix
      queue
      A
      +
      B
      *
      C
      __
       
       
       
       
       
      __
       
       
       
       
       
      infix
       
      oper
       
      postfix
       
       
      +
      B
      *
      C
      __
       
       
       
       
       
      __
       
       
       
       
      A
       
      infix
       
      oper
       
      postfix
       
       
       
      B
      *
      C
      __
       
       
       
       
      +
      __
       
       
       
       
      A
       
      infix
       
      oper
       
      postfix
       
       
       
       
      *
      C
      __
       
       
       
       
      +
      __
       
       
       
      B
      A
       
      infix
       
      oper
       
      postfix
       
       
       
       
       
      C
      __
       
       
       
      *
      +
      __
       
       
       
      B
      A
       
      * has higher precedence
      thus at higher position
      in stack
      infix
       
      oper
       
      postfix
       
       
       
       
       
       
      __
       
       
       
      *
      +
      __
       
       
      C
      B
      A
       
       
      infix
       
      oper
       
      postfix
       
       
       
       
       
       
      __
       
       
       
       
       
      __
      +
      *
      C
      B
      A
       
       
      So the postfix expression is :
      A B C * +

      e.g. A /( B + C ) * D

      A
      /
      (
      B
      +
      C
      )
      *
      D
      __
       
       
       
       
       
       
       
       
       
      __
       
       
       
       
       
       
       
       
       
       
       
       
      (
      B
      +
      C
      )
      *
      D
      __
       
       
       
       
       
       
       
       
      /
      __
       
       
       
       
       
       
       
       
      A
       
       
       
       
       
      B
      +
      C
      )
      *
      D
      __
       
       
       
       
       
       
       
      (
      /
      __
       
       
       
       
       
       
       
       
      A
       
       
       
       
       
       
      +
      C
      )
      *
      D
      __
       
       
       
       
       
       
       
      (
      /
      __
       
       
       
       
       
       
       
      B
      A
       
       
       
       
       
       
       
      C
      )
      *
      D
      __
       
       
       
       
       
       
      +
      (
      /
      __
       
       
       
       
       
       
       
      B
      A
       
       
       
       
       
       
       
      )
      *
      D
      __
       
       
       
       
       
       
      +
      (
      /
      __
       
       
       
       
       
       
      C
      B
      A
       
       
       
       
       
       
       
       
      )
      *
      D
      __
       
       
       
       
       
       
      +
      (
      /
      __
       
       
       
       
       
       
      C
      B
      A
       
      the ) associated with '+'
      has higher precedence,
      thus transfer
       
       
       
       
       
       
       
       
      *
      D
      __
       
       
       
       
       
       
       
       
      /
      __
       
       
       
       
       
      +
      C
      B
      A
       
       
       
       
       
       
       
       
       
      *
      D
      __
       
       
       
       
       
       
       
       
      /
      __
       
       
       
       
       
      +
      C
      B
      A
       
      '*' and '/' have
      same precedence,
      but '/' occurs first.
      thus transfer '/'
       
       
       
       
       
       
       
       
       
      D
      __
       
       
       
       
       
       
       
       
      *
      __
       
       
       
       
      /
      +
      C
      B
      A
       
       
       
       
       
       
       
       
       
       
      __
       
       
       
       
       
       
       
       
      *
      __
       
       
       
      D
      /
      +
      C
      B
      A
       
       
       
       
       
       
       
       
       
       
       
      __
       
       
       
       
       
       
       
       
       
      __
       
       
      *
      D
      /
      +
      C
      B
      A
       
      Infix stack empty.
      So done!
      postfix: A B C + / D *

      Implementations

      //token.h
      #ifndef TOKEN_H
      #define TOKEN_H
      
      #include <string>
      #include <stack>
      #include <queue>
      #include <ctype.h>
      #include <stdio.h>
      
      typedef double dataType;	//could be int or double
      
      enum whatKind { operate, number };
      
      class TOKEN {
      public:
        whatKind  kind;
        dataType  num;
        char	    op;
      };
      
      void in2post ( stack<TOKEN> &infix, queue<TOKEN> &postq );
      dataType evaluate ( queue<TOKEN> &post );
      
      #endif
      

      //util.h
      #ifndef UTIL_H
      #define UTIL_H
      void q2stack( queue<TOKEN> &q, stack<TOKEN> &s );
      void reverse_stack( stack<TOKEN> &s, stack<TOKEN> &sr );
      bool c_is_an_operator( char c );
      void print_queue( queue<TOKEN> q ); 
      void print_stack( stack<TOKEN> s ); 
      
      #endif
      

      //in2postd.cpp
      
      #include "token.h"
      
      int precedence ( char c )
      {
        switch ( c ) {
          case '+':
          case '-':
      	return 1;
          case '*':
          case '/':
      	return 2;
        }
        return 0;	//lowest
      }
      
      
      void transfer ( stack<TOKEN> &s, queue<TOKEN> &q )
      {
        q.push( s.top() );
        s.pop();			//remove top element of stack
      }
      
      void in2post ( stack<TOKEN> &infix, queue<TOKEN> &postq )
      {
        stack <TOKEN> opstack;	//operator stack
        TOKEN a;
      
        while ( !infix.empty() ) {
          a = infix.top();		//get a Token from infix stack
          infix.pop();		//remove token at top
          if ( a.kind == number ) 
      	postq.push( a );	//enter a to postfix queue
          else if ( opstack.empty() )	
      	opstack.push( a );	//put token in operator stack
          else if ( a.op == '(' )	//'(' has lowest precedence, always put it in op stack
      	opstack.push( a );    
          else if ( a.op == ')' ){	//encounters right parenthesis, so transfer everything enclosed by  ( .. )
      	while ( opstack.top().op != '(' )
      	  transfer ( opstack, postq );
      	opstack.pop();		//remove '(' after the transfer
          } else {
      	while ( !opstack.empty() &&
      	  	precedence( a.op ) <= precedence( opstack.top().op ) )
          	  transfer ( opstack, postq );
      
      	opstack.push( a );
          }
        } //while
        while ( !opstack.empty() )
          transfer ( opstack, postq );
      }
      

      //evaluate.cpp
      #include "token.h"
      
      //evaluates postfix expression represented as a queue of tokens
      //assumes that postfix expression is correctly formed
      dataType evaluate ( queue<TOKEN> &post )
      {
        stack<TOKEN> eval;
        dataType first, second, ans;
        char bin_op;
        TOKEN tp, te;
      
        while ( !post.empty() ) {
          tp = post.front();
          post.pop();
          if ( tp.kind == number )	//number
      	eval.push( tp );	//save the number on evaluation stack
          else {			//its an operator
      	te = eval.top();	//get a 'number' from eval stack
      	first = te.num;
      	eval.pop();
      	te = eval.top();	//get a second 'number'
      	second = te.num;
      	eval.pop();
      	bin_op = tp.op;
      	switch ( bin_op ) {
      	  case '+' : 
      	    ans = second + first;
      	    break;
      	  case '-' : 
      	    ans = second - first;
      	    break;
      	  case '*' : 
      	    ans = second * first;
      	    break;
      	  case '/' : 
      	    ans = second / first;
      	    break;
      	} //switch
      	te.num = ans;		//save the result
      	eval.push ( te );
          } //else
        } //while
        te = eval.top();
        return te.num;
      }
      

    18. Back tracking

      Solving a problem by trial and error in a well organized manner:
    19. never try the same thing twice,
    20. if the problem is finite we will eventually try all possibilities
    21. if the steps tried do not lead to a solution, some or all of them may
      have to be retraced and the relevant details discarded
    22. e.g. nonattacking queens

      Try this.
        Q        
          Q      
             Q   
               Q 
         Q       
       Q         
              Q  
            Q    

      Brute force

      64
      8
      = 4,426,165,368

      Only one queen in each row

      88 = 16,777,216

      Rejects position whose column or diagonals are guarded

      8! = 40, 320

      To solve the problem, when we place a queen on a square, we check if is under attack; if no, proceed; if yes, remove the queen and back track

       

      r1
      row
      r2
         0     1     2     3  
      0               
      1               
      2               
      3               
      4               

      //queen8.cpp
      #include <iostream.h>
      
      class queen8
      {
      public:
      	queen8();
      	void clearBoard();
      	void displayBoard();
      	void placeQueen( int col, bool &done );
      private:
      	enum square{QUEEN, EMPTY};
      	square board[8][8];
      	void setQueen( int row, int col );  //set to square( row, col) to QUEEN
      	void removeQueen( int row, int col ); //set to EMPTY
      	bool isUnderAttack( int row, int col );
      	//determines whether ( row, col ) is under attack by any
      	// queens 1 through col-1
      //	int index( int n );
      };
      
      queen8::queen8()
      {
      	int i, j;
      	for ( i = 0; i < 8; ++i )
      		for( j = 0; j < 8; ++j )
      			board[i][j] = EMPTY;
      }	
      
      void queen8::placeQueen( int col, bool & done )
      {
      	if ( col > 7 )
      		done = true;
      	else {
      		done = false;
      		int row = 0;
      		while ( !done && ( row < 8 ) ) {
      			if ( isUnderAttack( row, col ) )
      				++row;
      			else {	//place queen and consider next column
      				setQueen( row, col );
      				placeQueen( col+1, done );
      				if ( !done ) {	//if no queen is possible in next col
      								//backtrack
      					removeQueen( row, col );
      					++row;
      				}
      			} //else
      		}//while
      	} //else
      } //queen8
      
      void queen8::setQueen( int row, int col )
      {
      	board[row][col] = QUEEN;
      }
      
      void queen8::removeQueen( int row, int col )
      {
      	board[row][col] = EMPTY;
      }	
      
      bool queen8::isUnderAttack( int row, int col )
      //precondition : each column between 0 and col-1 
      //has a queen placed in it
      {
      	int r1, r2, c;
      
      	r1 = row-1;
      	r2 = row+1;
      	for ( c = col-1; c >= 0 ; --c ) {
      		if ( board[row][c] == QUEEN )
      			return true;
      		if ( r1 >= 0 ) {
      			if ( board[r1][c] == QUEEN )
      				return true;
      			--r1;
      		}
      		if ( r2 < 8 ) {
      			if ( board[r2][c] == QUEEN )
      				return true;
      			++r2;
      		}
      	} //for
      	return false;
      }
      
      void queen8::displayBoard()
      {
      	int row, col;
       
      	cout << endl;
      	for ( row = 0; row < 8; ++row ) {
      		for ( col = 0; col < 8; ++col ) {
      			if ( board[row][col] == QUEEN )
      				cout << "Q    ";
      			else
      				cout << "X    ";
      		}
      		cout << endl;
      	}
      }
      
      void main()
      {
      	queen8 q8;
      	bool done = false;
      
      	q8.placeQueen( 0, done );
      	q8.displayBoard();
      }
      

    23. Deques: Double-Ended Queues

      An interesting and versatile data structure in STL:
    24. a combination of stack and queue
    25. allow elements to be inserted and removed at both ends (fast)
    26. like vector, insertion at middle allowed but not efficient ( O(n) )
    27. STL deque operations are a combination of vector and list
    28. Application: Depth- and Breadth- Search

      Consider a maze:
    29. can be divided into cells (squares)
    30. find a path using a deque to save neigbors
    31. Depth-first search: push_front()
              front         back
                    20
                    19 15
                    22 18 15
                    16 17 18 15      
      	
    32. Breadth-first search: push_back()
              front                back
                    20
                    15 19
                    15 10 14
                    17 21  2  8 8 12      
      	
    33. each cell is characterized by its 'walls', specified by a 4-bit
      binary number denoting W, N, E, S.

         Decimal       W N E S       Walls  
      0 0 0 0 0
      1 0 0 0 1    
      2 0 0 1 0   |
      3 0 0 1 1    |
      . . .
      7 0 1 1 1
      . . .
      . . .
      10 1 0 1 0 |     |
      . ..
      . ..
      15 1 1 1 1
    34. A class cell should have
      1. An id number identifying the cell
      2. A list maintaining neigbouring cells
      3. A boolean value indicating whether the cell has been visited
          class cell {
          protected:
            int id;
            bool visited;
            list <cell *> neighbors;
          public:
            //constructor
            cell ( int n )
            {
              id = n;
              visited = false;
            }
          
            void addNeighbor ( cell *a )
            {
              neighbors.push_back ( a );
            }
            bool visit ( deque <cell *> & );
          };
          
    35. Class maze maintains the entire maze structure; should have
      1. a pointer to the starting cell
      2. a boolean flag indicating the destination has been reached
      3. a deque to remember the traversed paths
          class maze {
          protected:
            cell *start;
            bool finished;
            deque <cell *> path;
          public:
            maze ( istream & );
            void solveMaze();
          };