Homework Solution

Stacks and Queues

Problems to be Submitted (20 points)

  1. (5 points)

    Operation Return Value S
    (bottom, ..., top)
    S.push(7) - (7)
    S.push(10) - (7, 10)
    S.pop() - (7)
    S.top() 7 (7)
    S.push(3) - (7, 3)
    S.push(5) - (7, 3, 5)
    S.pop() - (7, 3)
    S.empty() false (7, 3)
    S.size() 2 (7, 3)
    S.top() 3 (7, 3)
    S.push(8) - (7, 3, 8)
    S.pop() - (7, 3)
    S.pop() - (7)

  2. (5 points)

    Operation
     
    Return Value
     
    Q
    (front, ..., back)
    Q.push(10) - (10)
    Q.push(4) - (10, 4)
    Q.size() 2 (10, 4)
    Q.front() 10 (10, 4)
    Q.push(6) - (10, 4, 6)
    Q.pop() - (4, 6)
    Q.push(3) - (4, 6, 3)
    Q.pop() - (6, 3)
    Q.front() 6 (6, 3)
    Q.pop() - (3)
    Q.push(7) - (3, 7)
    Q.front() 3 (3, 7)

  3. (10 points)

    There are several possible ways to accomplish this task. One general theme is to use Stack S1 as the permanent storage, and to only use S2 as temporary storage at key times. The issue is whether to associate the top of S1 with the front of the queue or the back of the queue. If that top represents the back of the queue, it makes it easier to push items but tougher to implement pop or front, since that requires access to the bottom of the stack. If the top of S1 represents the front of the queue it becomes easier to implement pop and front, but tougher to implement push since this requires placing the new item at the bottom of S1.

    We first present this approach with the top of the stack as the front of the queue. This implemenation appears as follows:

    #include <stack>      
    template <typename Object>
    class queue {
    public:
    
      bool empty() const {
        return S1.empty();
       }
    
      int size() const {
        return S1.size();
       }
    
      void push(const Object& item) {
        while (!S1.empty()) {  // move all to S2
          S2.push(S1.top());
          S1.pop();
        }
        S1.push(item);
        while (!S2.empty()) {  // move all back to S1
          S1.push(S2.top());
          S2.pop();
        }
      }
    
      void pop() {
        S1.pop();
      }
          
      Object& front() {
        return S1.top();
      }
    
    
    private:
      // Do not use any data members other than the following two stacks
      std::stack<Object> S1;
      std::stack<Object> S2;
    
    }
    

    A similar approach can be used with the top of the stack as the back of the queue. In this case, push is trivial, and we have to do more work for both front and pop, in order to "uncover" the desired element. An additional technical challenge is that front must return a reference to the front object (not a copy of the front object), and it must be a reference to the location of that object after the method completes.

    #include <stack>      
    template <typename Object>
    class queue {
    public:
    
      bool empty() const {
        return S1.empty();
       }
    
      int size() const {
        return S1.size();
       }
    
      void push(const Object& item) {
        S1.push();
      }
    
      void pop() {
        while (S1.size() > 1) {  // move all but one to S2
          S2.push(S1.top());
          S1.pop();
        }
        S1.pop();
        while (!S2.empty()) {  // move all remaining back to S1
          S1.push(S2.top());
          S2.pop();
        }
      }
          
      Object& front() {
        while (S1.size() > 1) {         // move all but one to S2
          S2.push(S1.top());
          S1.pop();
        }
        Object& answer(S1.top());       // use reference varaible to denote location within S1
        while (!S2.empty()) {           // move all remaining back to S1
          S1.push(S2.top());
          S2.pop();
        }
        return answer;
      }
    
    
    private:
      // Do not use any data members other than the following two stacks
      std::stack<Object> S1;
      std::stack<Object> S2;
    
    }
    

    In the next homework, we will revisit this problem with an even more elegant solution.


Extra Credit

  1. (2 points)

    We use a similar technique here as in the previous problem. We keep all items on Queue Q1 in general, intentionally ordered so that the front of the queue represents the top of our stack. This means that a newly pushed item must make its way immediately to the front of the queue rather than the back. We do this by using a loop to rotate the queue's contents.

    #include <queue>      
    template <typename Object>
    class stack {
    public:
          
      bool empty() const {
        return Q1.empty();
       }
    
      int size() const {
        return Q1.size();
       }
    
      void push(const Object& item) {
        int original = Q1.size();
        Q1.push(item);
        for (int i = 0; i < original; i++) {
          Q1.push(Q1.front());      // push a copy to the back
          Q1.pop();                 // and remove the one at the front
        }
      }
    
      void pop() {
        Q1.pop();
      }
    
      Object& top() {
        return Q1.front();
      }
    
    private:
      std::queue<Object> Q1;
    }
    

Last modified: Thursday, 20 February 2014