Skip to content
dsa/roadmap
← All labs
Phase 2 labWeeks 5–10 5–6 hours

Lab 2: Library Manager

A linked list of books, an undo stack for add/remove, and a circular request queue for borrowing, driven by text commands.

01Spec

The task

Build the back-office tool for a small department library. Three data structures, each doing the job it is best at:

NeedStructureWhy
The catalogue, kept in id order, with inserts and deletes anywheresingly linked listno shifting on insert/delete
“Undo my last change”stackthe most recent change is undone first (LIFO)
Students waiting to borrowcircular queuefirst come, first served (FIFO), fixed memory

Commands (one per line)

CommandEffect
ADD id title…add a book (reject duplicate ids); push onto the undo stack
REMOVE idremove a book; push onto the undo stack (keep the title!)
UNDOreverse the last ADD or REMOVE
REQUEST name idenqueue a borrow request (only for books that exist)
SERVEdequeue the oldest request: lend the book, or re-queue the request if it is out
RETURN idmark a book as returned
LISTprint the catalogue in id order

Rules

  • Write your own list, stack and queue. No std::list, std::stack or std::queue.
  • Every new has a matching delete. Check with g++ -std=c++17 -fsanitize=address.
  • Never crash on bad input: unknown ids, empty undo, empty queue, full queue.

02Starter file

It compiles as-is. Every TODO is a milestone below.

Download library-manager.cpp
library-manager-starter.cpp47 lines · C++17
// Lab 2: Library Manager (starter). Implement the three classes, then make main() pass the sample.
#include <iostream>
#include <string>
using namespace std;

struct Book {
    int id;
    string title;
    bool out = false;
    Book* next = nullptr;
};

class Library {                       // singly linked list kept sorted by id
    Book* head = nullptr;
public:
    Book* find(int id) const {
        // TODO: walk the list
        (void)id;
        return nullptr;
    }
    bool add(int id, const string& title) {
        // TODO: reject duplicates; insert in id order (a dummy node avoids the head special case)
        (void)id; (void)title;
        return false;
    }
    bool remove(int id, string& title) {
        // TODO: unlink and delete; copy the title out so UNDO can restore it
        (void)id; (void)title;
        return false;
    }
    void list() const { cout << "  (no books)\n"; }   // TODO
    // TODO: destructor that frees every node
};

// TODO: class UndoStack   (linked-list stack of {type, id, title})
// TODO: class RequestQueue (circular array queue of {who, id})

int main() {
    Library lib;
    string cmd;
    while (cin >> cmd) {
        // TODO: handle ADD, REMOVE, UNDO, REQUEST, SERVE, RETURN, LIST (see the lab page for the format)
        string rest;
        getline(cin, rest);
        cout << "TODO " << cmd << "\n";
    }
}

03Milestones

0/6 ticked

04Sample input & output

Save the input as input.txt and run ./library-manager < input.txt. Your output should match the solution's output exactly.

input.txt
ADD 30 Data Structures in C++
ADD 10 Introduction to Algorithms
ADD 20 The C++ Programming Language
ADD 10 Duplicate
LIST
REMOVE 20
LIST
UNDO
UNDO
LIST
REQUEST Hana 30
REQUEST Dawit 10
REQUEST Liya 30
REQUEST Sara 99
SERVE
SERVE
SERVE
RETURN 30
SERVE
LIST

05Reference solution

Don't just watch: implement it yourself, then solve 2–3 problems. Try for at least an hour before opening this.

Show the solution
library-manager.cpp137 lines · C++17
// Lab 2: Library Manager (solution)
#include <iostream>
#include <string>
using namespace std;

struct Book {
    int id;
    string title;
    bool out = false;
    Book* next = nullptr;
};

// ---------- linked list of books (sorted by id) ----------
class Library {
    Book* head = nullptr;
public:
    Book* find(int id) const {
        for (Book* b = head; b; b = b->next) if (b->id == id) return b;
        return nullptr;
    }
    bool add(int id, const string& title) {
        if (find(id)) return false;
        Book dummy{0, "", false, head};
        Book* prev = &dummy;
        while (prev->next && prev->next->id < id) prev = prev->next;
        prev->next = new Book{id, title, false, prev->next};
        head = dummy.next;
        return true;
    }
    bool remove(int id, string& title) {     // returns the removed title for undo
        Book dummy{0, "", false, head};
        Book* prev = &dummy;
        while (prev->next && prev->next->id != id) prev = prev->next;
        if (!prev->next) return false;
        Book* gone = prev->next;
        title = gone->title;
        prev->next = gone->next;
        delete gone;
        head = dummy.next;
        return true;
    }
    void list() const {
        if (!head) { cout << "  (no books)\n"; return; }
        for (Book* b = head; b; b = b->next)
            cout << "  #" << b->id << " " << b->title << (b->out ? " [borrowed]" : "") << "\n";
    }
    ~Library() { while (head) { Book* n = head->next; delete head; head = n; } }
};

// ---------- undo stack (linked list) ----------
struct Action { char type; int id; string title; Action* next; };   // type: 'A'dded or 'R'emoved
class UndoStack {
    Action* top = nullptr;
public:
    bool empty() const { return top == nullptr; }
    void push(char t, int id, const string& title) { top = new Action{t, id, title, top}; }
    Action pop() {
        Action a = *top;
        Action* old = top;
        top = top->next;
        delete old;
        return a;
    }
    ~UndoStack() { while (!empty()) pop(); }
};

// ---------- request queue (circular array) ----------
struct Request { string who; int id; };
class RequestQueue {
    static const int N = 8;
    Request q[N];
    int front = 0, rear = -1, count = 0;
public:
    bool empty() const { return count == 0; }
    bool enqueue(const Request& r) {
        if (count == N) return false;
        rear = (rear + 1) % N;
        q[rear] = r;
        count++;
        return true;
    }
    Request dequeue() {
        Request r = q[front];
        front = (front + 1) % N;
        count--;
        return r;
    }
    int size() const { return count; }
};

int main() {
    Library lib;
    UndoStack undo;
    RequestQueue requests;
    string cmd;
    while (cin >> cmd) {
        if (cmd == "ADD") {
            int id; string title;
            cin >> id; getline(cin >> ws, title);
            if (lib.add(id, title)) { undo.push('A', id, title); cout << "added #" << id << "\n"; }
            else cout << "#" << id << " already exists\n";
        } else if (cmd == "REMOVE") {
            int id; cin >> id;
            string title;
            if (lib.remove(id, title)) { undo.push('R', id, title); cout << "removed #" << id << "\n"; }
            else cout << "#" << id << " not found\n";
        } else if (cmd == "UNDO") {
            if (undo.empty()) { cout << "nothing to undo\n"; continue; }
            Action a = undo.pop();
            string t;
            if (a.type == 'A') { lib.remove(a.id, t); cout << "undo: removed #" << a.id << " again\n"; }
            else { lib.add(a.id, a.title); cout << "undo: restored #" << a.id << "\n"; }
        } else if (cmd == "REQUEST") {
            Request r; cin >> r.who >> r.id;
            if (!lib.find(r.id)) cout << "#" << r.id << " not in library\n";
            else if (requests.enqueue(r)) cout << r.who << " queued for #" << r.id << " (" << requests.size() << " waiting)\n";
            else cout << "request queue full\n";
        } else if (cmd == "SERVE") {
            if (requests.empty()) { cout << "no requests\n"; continue; }
            Request r = requests.dequeue();
            Book* b = lib.find(r.id);
            if (!b) cout << r.who << ": #" << r.id << " was removed\n";
            else if (b->out) { requests.enqueue(r); cout << r.who << ": #" << r.id << " is out, back to the end of the line\n"; }
            else { b->out = true; cout << r.who << " borrows " << b->title << "\n"; }
        } else if (cmd == "RETURN") {
            int id; cin >> id;
            Book* b = lib.find(id);
            if (b && b->out) { b->out = false; cout << "returned #" << id << "\n"; }
            else cout << "#" << id << " was not borrowed\n";
        } else if (cmd == "LIST") {
            lib.list();
        } else {
            cout << "unknown command " << cmd << "\n";
            string rest; getline(cin, rest);
        }
    }
}
Expected output (with sample input)
added #30
added #10
added #20
#10 already exists
  #10 Introduction to Algorithms
  #20 The C++ Programming Language
  #30 Data Structures in C++
removed #20
  #10 Introduction to Algorithms
  #30 Data Structures in C++
undo: restored #20
undo: removed #20 again
  #10 Introduction to Algorithms
  #30 Data Structures in C++
Hana queued for #30 (1 waiting)
Dawit queued for #10 (2 waiting)
Liya queued for #30 (3 waiting)
#99 not in library
Hana borrows Data Structures in C++
Dawit borrows Introduction to Algorithms
Liya: #30 is out, back to the end of the line
returned #30
Liya borrows Data Structures in C++
  #10 Introduction to Algorithms [borrowed]
  #30 Data Structures in C++ [borrowed]

Keyboard shortcuts

⌘K /
Search topics, glossary, cheat sheets
space
Play / pause the visualizer
← →
Step back / forward
r
Reset the visualizer
?
Show this help