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.
Uses:4.1 Pointers, new/delete & the Heap4.2 Singly Linked List5.2 Stacks with a Linked List5.5 Circular Queue
01Spec
The task
Build the back-office tool for a small department library. Three data structures, each doing the job it is best at:
| Need | Structure | Why |
|---|---|---|
| The catalogue, kept in id order, with inserts and deletes anywhere | singly linked list | no shifting on insert/delete |
| “Undo my last change” | stack | the most recent change is undone first (LIFO) |
| Students waiting to borrow | circular queue | first come, first served (FIFO), fixed memory |
Commands (one per line)
| Command | Effect |
|---|---|
ADD id title… | add a book (reject duplicate ids); push onto the undo stack |
REMOVE id | remove a book; push onto the undo stack (keep the title!) |
UNDO | reverse the last ADD or REMOVE |
REQUEST name id | enqueue a borrow request (only for books that exist) |
SERVE | dequeue the oldest request: lend the book, or re-queue the request if it is out |
RETURN id | mark a book as returned |
LIST | print the catalogue in id order |
Rules
- Write your own list, stack and queue. No
std::list,std::stackorstd::queue. - Every
newhas a matchingdelete. Check withg++ -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.
// 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
// 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]