PRG622 · Programming 622

Programming 622 Study Hub

A Programming 622 open-book reference with a guided index for test Topics 1–4, concise quick points, detailed explanations, and task-organised C++ examples.

CA Test

Use this index with a mouse or touchscreen to jump to a topic, subtopic, or fuller explanation. The topic boundaries below follow the Programming 622 Study Guide.

Confirmed scope source: Programming 622 Study Guide, open the source PDF. Topics 1–4 span printed pages 9–284. Topic headings, subtopics, and review-question ranges below are study-guide coverage. Explanations and examples in the general content are study aids; entries marked Useful supporting detail add context and are not confirmed test content by the study guide.

Review-question ranges are also included in the source guide: Topic 1 pages 58–64, Topic 2 pages 111–117, Topic 3 pages 224–232, and Topic 4 pages 281–284.

Exam Focus Areas

Confirmed exam focus areas will be added here once the assessment coverage is available.

Coming soon

Exam focus areas are coming soon

The current exam-focus content has been cleared and will be updated when the confirmed focus areas are provided.

General Module Content

Follow all ten topics in the study-guide order. Each chapter explains the key idea, shows a worked C++ example, and links to the related names and operations in the reference below. Topics 1–4 also have a separate test index above.

1. Software Engineering Principles And C++ Classes

The guide starts by connecting software engineering practice to C++ program design: understand the problem, design algorithms and objects, implement with documented functions and classes, then test before the program enters long-term maintenance.

Software life cycle Analysis and design Big-O Classes ADT Preconditions

1.1 Software Life Cycle

A program has a life cycle from the moment it is first conceived until it is retired. The guide names three fundamental stages: development, use and maintenance.

  • Development: the new program is created to solve a customer's problem.
  • Use: the completed program is released and users begin applying it to real work.
  • Maintenance: problems, corrections and improvement ideas are sent back to the developer.
Exam angle: Maintenance is not an afterthought. A well-developed program is easier and less expensive to maintain.

1.2 Development Phase

Software engineers break development into four main phases: analysis, design, implementation, and testing/debugging. Each phase answers a different question.

Analysis
Design
Implementation
Testing and debugging
  • Analysis: understand the problem, required user interaction, data, output format and any complex subproblems.
  • Design: create algorithms for the full problem and for each subproblem.
  • Implementation: write and compile the C++ code for the classes and functions discovered in design.
  • Testing and debugging: run test cases, compare actual output to expected output, then find and fix faults.

1.3 Algorithm Analysis: Big-O

After designing an algorithm, the guide says it should also be analysed. The focus is normally the number of basic operations as a function of input size n, not the raw time on one computer.

Big-O describes the dominant growth term when n becomes large. Constants and smaller terms become less important. For example, n² + n is O(n²) because the quadratic term dominates.

Growth Meaning Typical Guide Connection
O(1) Fixed amount of work, independent of input size. Simple statements and direct vector access.
O(n) Work grows in direct proportion to the number of items. Sequential search and traversal.
O(log n) The problem is repeatedly divided into smaller parts. Binary search.
O(n2) Repeated work over the same growing data set. Nested loops and simple comparison sorting patterns.
How to analyse: identify the dominant operation first. In searching, count comparisons; in matrix multiplication, multiplication may be the expensive operation.

1.4 Classes

A C++ class is the mechanism used to combine data and operations in one unit. It defines a type; memory is allocated only when objects of that type are declared.

  • Class components are called members.
  • Members can be data members or member functions.
  • Member functions can directly access other members of the same class.
  • Members are usually grouped under private, protected or public access specifiers.
  • The semicolon after the closing brace is part of the class syntax.
class Rectangle {
private:
	double length;
	double width;

public:
	void set(double l, double w);
	double area() const;
};

1.5 Function Contracts

The guide uses preconditions and postconditions to document how a function should be called and what will be true after it finishes.

  • Precondition: what must be true before the call, such as "the value must be nonnegative".
  • Postcondition: what the function guarantees after the call, such as "returns centimetres" or "returns -1.0 for invalid input".
  • assert can enforce a condition, but a failed assertion terminates the program.
Design decision: Returning an error value lets the caller recover; using assert is stricter and is better when continuing would make the program invalid.

1.6 Data Abstraction, Classes And ADTs

Abstraction separates logical properties from implementation details. The guide's car example makes the idea simple: the driver needs to know how to start and drive the car, not how the engine is built.

Data abstraction applies the same idea to data types. An abstract data type has a type name, a domain of possible values, and a set of operations. A C++ class is a convenient way to implement an ADT because it can expose operations while hiding storage details.

ADT Part Meaning Example From The Guide
Type name The name of the abstract data type. clockType or listType
Domain The valid values belonging to the type. A time of day, or a list of up to 1000 integers.
Operations The actions users can perform on the type. Set, return, print, increment, compare, insert, delete, search.

1.7 Identifying Classes, Objects And Operations

The guide gives a practical OOD technique: start with the problem description, list nouns and verbs, then choose candidate classes and operations.

  • Nouns often suggest possible classes or data values.
  • Verbs often suggest operations.
  • After choosing a class, identify operations objects can perform, operations performed on the object, and information the object must maintain.
Guide example: In a cylinder problem, cylinderType is a natural class; radius and height are data; volume, surface area, input and print are operations.

1.8 UML Class Notation

The study guide lists Unified Modeling Language (UML) notation as a Topic 1 learning outcome. A class box commonly shows the class name, attributes, and operations in separate compartments. Visibility symbols are + public, - private, and # protected.

------------------------
| Cylinder             |
------------------------
- radius: double       |
- height: double       |
------------------------
+ volume(): double     |
+ setHeight(h: double) |
------------------------

This box describes the interface and structure; it does not prescribe the internal code.

Topic 1 Extended Code Example: ADT-Style Class With Contracts

This example combines the guide's software-design ideas: analyse the problem, model the correct object, protect its data, document preconditions/postconditions, and expose clear operations.

#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
using namespace std;

class cylinderType {
private:
	double radius;
	double height;

public:
	// Default constructor.
	// Postcondition: the object exists with safe starting dimensions.
	cylinderType() {
		radius = 1.0;
		height = 1.0;
	}

	// Constructor with parameters.
	// Precondition: r and h must be positive measurements.
	// Postcondition: radius = r and height = h.
	cylinderType(double r, double h) {
		setDimensions(r, h);
	}

	// Member function used to change the object's data safely.
	// Encapsulation means outside code cannot assign radius/height directly.
	void setDimensions(double r, double h) {
		assert(r > 0 && h > 0);
		radius = r;
		height = h;
	}

	// Logical operation of the ADT.
	// The caller needs the result, not the internal formula details.
	double volume() const {
		const double pi = 3.141592653589793;
		return pi * radius * radius * height;
	}

	// Another logical operation of the ADT.
	double surfaceArea() const {
		const double pi = 3.141592653589793;
		return 2 * pi * radius * (radius + height);
	}

	void print() const {
		cout << fixed << setprecision(2);
		cout << "Radius: " << radius << endl;
		cout << "Height: " << height << endl;
		cout << "Volume: " << volume() << endl;
		cout << "Surface area: " << surfaceArea() << endl;
	}
};

int main() {
	// Analysis: the problem needs radius and height as input data.
	// Design: cylinderType stores dimensions and provides operations.
	cylinderType waterTank(3.5, 8.0);

	// Implementation: use the public interface instead of touching private data.
	waterTank.print();

	return 0;
}

Topic 1 Extended Code Example: Reading Code For Big-O

This small program shows how to connect code structure to operation growth, which is exactly what the Big-O section expects you to practise.

#include <iostream>
#include <vector>
using namespace std;

bool sequentialSearch(const vector<int>& values, int target) {
	// In the worst case, the loop checks every element once.
	// If values has n elements, the dominant operation is n comparisons.
	// Worst-case complexity: O(n).
	for (int item : values) {
		if (item == target) {
			return true;
		}
	}
	return false;
}

void printPairs(const vector<int>& values) {
	// This nested loop compares every element with every other element.
	// For n elements, the inner statement can run n * n times.
	// Dominant term: n2, so complexity is O(n2).
	for (int i = 0; i < static_cast<int>(values.size()); i++) {
		for (int j = 0; j < static_cast<int>(values.size()); j++) {
			cout << values[i] << "," << values[j] << " ";
		}
		cout << endl;
	}
}

int main() {
	vector<int> marks = {72, 85, 91, 60};

	cout << boolalpha;
	cout << "Found 91? " << sequentialSearch(marks, 91) << endl;
	printPairs(marks);

	return 0;
}
Topic 1 Glossary: Key Words And Code Terms
analysis
The phase where the problem, inputs, outputs, user needs and constraints are understood before coding begins.
design
The phase where algorithms, classes and operations are planned.
implementation
The phase where the planned solution is translated into C++ code.
testing
Running test cases to compare expected output with actual output.
debugging
Finding and correcting faults discovered during testing.
O(n)
Linear growth; the work grows in proportion to the number of items.
O(log n)
Logarithmic growth; the problem is repeatedly divided into smaller parts.
O(n2)
Quadratic growth; commonly caused by nested loops over the same data set.
class
A programmer-defined C++ type that groups data members and member functions.
object
A variable or instance created from a class type.
private
An access specifier that hides members from outside code.
public
An access specifier for the interface clients are allowed to call.
const
Used on member functions to promise that the object will not be changed by that function.
assert
A debugging check that terminates the program if a required condition is false.
precondition
What must be true before a function is called.
postcondition
What a function guarantees after it completes.
ADT
Abstract data type; a logical type described by values and operations, separate from implementation details.
constructor
A member function that initializes an object when it is created.
Back to Topics 1–4 index

2. Standard Template Library (STL)

The STL provides professionally written class templates and generic algorithms for storing, accessing and manipulating data. This topic introduces containers, iterators and algorithms, then uses vector and deque to show the common patterns.

Containers Sequence containers Iterators Algorithms Vector Deque copy

2.1 Components Of The STL

The guide frames the STL around the central goal of a program: store data, access data and manipulate data to produce results.

Component Role Typical Examples
Containers Class templates that manage objects of a given type. vector, deque, list, stacks and queues.
Iterators Pointer-like objects used to step through container elements. begin(), end(), ++it, *it.
Algorithms Reusable functions that manipulate ranges of data. copy, searching, sorting and transformation-style operations.

2.2 Sequence Containers

STL containers are classified into sequence containers, associative containers and container adapters. In a sequence container, every object has a specific position.

  • vector: dynamic array, random access, fast insertion at the end.
  • deque: double-ended queue, can expand at the front and back.
  • list: linked sequence container, covered later in the linked-list topic.
  • Container adapters are revisited with stacks and queues.

Container Performance Comparison

Use these typical complexity costs to compare containers. n is the number of elements. “Middle insertion” assumes the insertion position is already known; finding a position in a linked list still takes linear time.

ContainerRandom accessMiddle insertionEnd insertion / insertion by keySearchMemory per element
vectorO(1)O(n)Amortized O(1) at the back; O(n) for an individual reallocationO(n)Low
listO(n)O(1) with the position iteratorO(1) at either endO(n)High
dequeO(1)O(n)O(1) at either endO(n)Medium
Ordered map / setO(n) to advance to an arbitrary position; no index accessNot position-based; insertion by key is O(log n)Insertion by key is O(log n)O(log n)High
unordered_map / unordered_setO(n) to advance; no index accessNot position-based; insertion by key is average O(1)Insertion by key is average O(1)Average O(1), worst O(n)Medium

Memory labels are approximate: linked and associative containers store per-element links or tree/hash bookkeeping, while sequence-container storage can be contiguous or segmented. Unordered-container operations are usually constant time but can become linear during heavy collisions or rehashing.

2.3 Sequence Container: vector

A vector stores and manages its objects in a dynamic array. Because arrays are random-access structures, vector elements can be accessed with indexes just like array elements.

  • Use #include <vector>.
  • Declare the component type, for example vector<int> intList;.
  • vector<int> intList(10); creates ten integer elements initialized to zero.
  • push_back adds a new element at the end; repeated end insertion is amortized constant time, although an individual reallocation can take linear time.
  • operator[] does not check bounds. The index must be less than size(); use at() when bounds checking is wanted.
#include <vector>
#include <iostream>
using namespace std;

vector<int> marks;
marks.push_back(76);
marks.push_back(84);

cout << marks[0] << endl;

2.4 begin, end, insert And erase

Every container provides begin() and end(). The guide uses them to initialise iterator loops and to mark the range processed by algorithms.

  • begin() returns an iterator positioned at the first element.
  • end() returns the stopping position just past the final element.
  • insert and erase often need an iterator to specify the exact position.
  • Inserting in the middle of a vector is expensive because later elements must shift.
vector<int>::iterator it = marks.begin();
++it;                 // Move to the second element.
marks.insert(it, 90); // Insert 90 before that position.

2.5 The copy Algorithm

The STL copy algorithm copies a range of elements from a source to a destination. The source range is written as first...last - 1, which means last is the stopping position, not the final copied element.

  • Include <algorithm> for copy.
  • The first two parameters describe the source range.
  • The third parameter describes the destination start.
  • The destination must have enough space unless an insert/output iterator is used.
int intArray[] = {5, 6, 8, 3, 40, 36, 98, 29, 75};
vector<int> vecList(9);

copy(intArray, intArray + 9, vecList.begin());

2.6 ostream Iterator And copy

An ostream_iterator lets copy send each value to an output stream such as cout. The delimiter argument controls what is printed after each value.

#include <algorithm>
#include <iterator>

ostream_iterator<int> screen(cout, " ");
copy(vecList.begin(), vecList.end(), screen);
Shortcut: You can create the output iterator directly inside the copy call: copy(vecList.begin(), vecList.end(), ostream_iterator<int>(cout, ", "));

2.7 Iterators

Iterators work like pointers into containers. The two core operations are incrementing the iterator with ++ and dereferencing it with * to access the current value.

Iterator Type Capability Where It Matters
Input Read values while moving forward. Reading from streams.
Output Write values while moving forward. Writing to streams.
Forward Read/write while moving forward and revisiting elements. General one-way traversal.
Bidirectional Move forward and backward. list, set, map.
Random access Jump directly using arithmetic and indexing-like movement. vector, deque, arrays and strings.
Exam trap: Do not assume every container behaves like an array. Some containers are better traversed through iterators than direct indexes.

2.8 Common STL Algorithms And Compatible Containers

These are useful supporting functions beyond the study guide's copy example. Most algorithms accept iterator ranges, so compatibility depends on iterator category and whether elements can be modified. Container adapters such as stack, queue, and priority_queue do not expose iterators.

FunctionTypical compatible containersImportant notes
std::sort, std::stable_sort, std::partial_sortMutable vector, deque, array, string, and C-style arrays.Require random-access iterators. They do not work with list or forward_list; use list::sort() or forward_list::sort(). Ordered and unordered map/set keys are not writable through their iterators.
std::find, std::find_if, std::count, std::count_ifReadable ranges in sequence containers (vector, deque, array, list, forward_list, string) and associative containers (set, map, and unordered variants).Read-only; the range does not have to be sorted. No iterators means no direct use with container adapters.
std::for_each, std::min_element, std::max_elementAny iterable container, including sequence and associative containers.These inspect an iterator range. A callable passed to for_each can modify values only when the iterators refer to writable elements.
std::reverseMutable vector, deque, array, string, and list.Needs bidirectional, writable iterators. It cannot be used with forward_list or to reorder map/set keys; use std::list::reverse() or std::forward_list::reverse() for those list containers.
std::copy, std::transformSource: any iterable container. Destination: writable sequence containers or an output/insert iterator.A pre-sized destination such as vector or array must have enough elements. Use std::back_inserter to append to containers such as vector, deque, list, or string; do not write through set/map key iterators.
std::lower_bound, std::upper_bound, std::binary_searchSorted ranges in vector, deque, array, string, list, or forward_list. Ordered set/map containers provide member lookup functions.The range must already be sorted using the same ordering. Prefer set/map member lower_bound() or find(); unordered containers are not sorted.
std::remove, std::remove_ifMutable sequence containers: vector, deque, list, forward_list, and string.These move retained values and return a new logical end; they do not shrink the container. Use erase-remove for vector/deque/string, or the remove()/remove_if() members of list and forward_list.
std::accumulateAny readable iterable container.Defined in <numeric>, not <algorithm>. It processes values in iteration order.

Compatibility follows iterator requirements in the C++ algorithm requirements and the sort specification. These are supporting notes, not additional study-guide test scope.

Topic 2 Extended Code Example: vector, Iterators, insert And copy

This example follows the guide's vector sequence: declare a vector, add elements, process by index, process by iterator, insert at an iterator position, and output with copy.

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
using namespace std;

int main() {
	vector<int> marks;

	// push_back is used when the vector starts empty or the final size is unknown.
	marks.push_back(13);
	marks.push_back(75);
	marks.push_back(28);
	marks.push_back(35);

	cout << "Original values: ";
	for (int i = 0; i < static_cast<int>(marks.size()); i++) {
		// vector supports random access, so index-based processing is valid.
		cout << marks[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < static_cast<int>(marks.size()); i++) {
		marks[i] *= 2; // Same position, updated value.
	}

	cout << "Doubled values: ";
	vector<int>::iterator it;
	for (it = marks.begin(); it != marks.end(); ++it) {
		// *it dereferences the iterator and returns the element it points to.
		cout << *it << " ";
	}
	cout << endl;

	it = marks.begin();
	++it;
	++it; // Move to the third element.
	marks.insert(it, 88); // Insert before the current iterator position.

	cout << "After inserting 88: ";
	copy(marks.begin(), marks.end(), ostream_iterator<int>(cout, " "));
	cout << endl;

	return 0;
}

Topic 2 Extended Code Example: deque And Double-Ended Access

The guide introduces deque after vector. Use it when insertions/removals at both the front and the back are part of the problem.

#include <algorithm>
#include <deque>
#include <iostream>
#include <iterator>
using namespace std;

int main() {
	deque<int> queueLikeData;
	ostream_iterator<int> screen(cout, " ");

	// push_back adds at the rear, similar to the vector examples.
	queueLikeData.push_back(13);
	queueLikeData.push_back(75);
	queueLikeData.push_back(28);
	queueLikeData.push_back(35);

	cout << "Initial deque: ";
	copy(queueLikeData.begin(), queueLikeData.end(), screen);
	cout << endl;

	// deque can grow efficiently at both ends.
	queueLikeData.push_front(0);
	queueLikeData.push_back(100);

	cout << "After front and back insertion: ";
	copy(queueLikeData.begin(), queueLikeData.end(), screen);
	cout << endl;

	// Remove from both ends.
	queueLikeData.pop_front();
	queueLikeData.pop_back();

	// Insert in the middle using an iterator position.
	deque<int>::iterator middle = queueLikeData.begin();
	++middle;
	queueLikeData.insert(middle, 444);

	cout << "After removals and middle insert: ";
	copy(queueLikeData.begin(), queueLikeData.end(), screen);
	cout << endl;

	return 0;
}
Topic 2 Glossary: Key Words And Code Terms
STL
The Standard Template Library; reusable C++ containers, iterators and algorithms.
container
An object that stores and manages a collection of values.
sequence container
A container where elements are kept in a specific linear order.
vector
A dynamic array with fast indexed access and efficient insertion at the end.
deque
A double-ended sequence container that supports insertion and removal at both ends.
iterator
A pointer-like object used to move through a container.
begin()
Returns an iterator positioned at the first element.
end()
Returns the stopping iterator just past the final element.
push_back()
Adds a new element to the end of a vector, deque or list.
push_front()
Adds a new element to the front of a deque or list.
insert()
Adds an element at a position identified by an iterator.
erase()
Removes an element or range from a container.
copy()
An STL algorithm that copies a range from one place to another.
ostream_iterator
An output iterator that writes each copied value to a stream such as cout.
*it
Dereferences an iterator to access the element it currently points to.
++it
Moves an iterator to the next element.
Back to Topics 1–4 index

3. Linked Lists

Linked lists use dynamically allocated nodes connected by pointer links. They solve some array limitations because nodes do not need contiguous memory and insert/delete operations can often be done by changing links instead of shifting data.

Nodes Pointers Traversal Insertion Deletion Ordered lists Doubly linked lists

3.1 Linked Lists

A linked list is a collection of nodes. Each node stores the data and the address of the next node. The order of the list is therefore determined by links, not by neighbouring memory positions.

  • The first node is reached through a separate pointer such as head or first.
  • The last node's link is NULL or nullptr.
  • Nodes can be anywhere in memory; they do not need to sit next to one another.
  • Because links move in one direction, losing the first pointer means losing access to the list.
struct nodeType {
	int info;          // Data stored in the node.
	nodeType *link;   // Address of the next node.
};

nodeType *head = nullptr;

3.1.2 Traversal Discipline

To traverse a linked list, the guide stresses that you should not move head. Instead, copy head into another pointer such as current and advance that pointer.

Common failure: If you write head = head->link; just to move through the list, the original first node becomes unreachable unless you saved it somewhere else.
nodeType *current = head;

while (current != nullptr) {
	cout << current->info << " ";
	current = current->link;
}

3.4 Linked-List Iterators

The study guide develops a linked-list iterator that keeps a pointer to the current node and overloads iterator operations. Dereference reads the current item; increment follows the link; comparison detects the end position.

#include <iostream>
#include <list>

int main() {
    std::list<int> values{10, 20, 30};
    for (auto it = values.begin(); it != values.end(); ++it) {
        std::cout << *it << ' ';
    }
    return 0;
}

A linked-list iterator moves node by node. It does not support jumping to item i in constant time like a vector iterator.

3.2 Item Insertion And Deletion

Insertion and deletion are link-management operations. The guide's key rule is to preserve the rest of the list before rerouting pointers.

  • To insert after p, first make the new node point to p->link.
  • Only after that should p->link point to the new node.
  • To delete a node after p, save it in q, bypass it, then delete q.
  • Bypassing without deleting leaves inaccessible memory behind.
Pointer order matters: newNode->link = p->link; must happen before p->link = newNode; when only one pointer marks the insertion position.

3.3 Linked List As An ADT

The guide moves from individual pointer statements to a reusable linked-list ADT. The user of the ADT should ask for operations, not manually manipulate every node.

ADT Operation Purpose
initializeListSet up an empty list.
isEmptyListCheck whether the list has no nodes.
print / lengthTraverse nodes without exposing pointer details.
front / backRetrieve first or last information.
search, insert, deleteNodeCore list manipulation operations.
destroyList / copyManage dynamic memory safely.

3.5-3.6 Unordered And Ordered Lists

The guide separates linked lists into unordered lists and ordered lists because search, insert and delete behave differently.

  • Unordered list: elements have no particular order; insertion can happen at the front or end.
  • Ordered list: elements are kept according to a comparison rule, usually ascending order.
  • After inserting or deleting from an ordered list, the resulting list must still be ordered.
  • For efficiency, the generic list keeps both first and last pointers plus a node count.

3.7-3.9 List Variations

A doubly linked list stores links in both directions. This supports backward movement, but every insert or delete must maintain both the forward and backward links.

  • std::list is the STL doubly linked sequence container; it supports bidirectional iteration and insert/erase at a known iterator position.
  • Header and trailer nodes can simplify edge cases at the beginning and end of a list.
  • Circular linked lists connect the final node back into the list rather than ending with NULL.
  • Finding a matching item still requires traversal, usually O(n); changing links is O(1) only after the position is known.

Topic 3 Extended Code Example: Build, Traverse, Insert And Delete A Linked List

This example demonstrates the guide's essential linked-list pointer rules: keep head stable during traversal, connect the new node before changing the previous node, and delete removed nodes.

#include <iostream>
using namespace std;

struct nodeType {
	int info;
	nodeType *link;
};

void printList(nodeType *head) {
	// Use current for traversal so head still points to the first node.
	nodeType *current = head;
	while (current != nullptr) {
		cout << current->info << " ";
		current = current->link;
	}
	cout << endl;
}

void insertAfter(nodeType *p, int value) {
	if (p == nullptr) return;

	nodeType *newNode = new nodeType;
	newNode->info = value;

	// Correct order: first preserve the rest of the list.
	newNode->link = p->link;
	p->link = newNode;
}

void deleteAfter(nodeType *p) {
	if (p == nullptr || p->link == nullptr) return;

	// q saves the node being removed so its memory can be released.
	nodeType *q = p->link;
	p->link = q->link;
	delete q;
}

int main() {
	nodeType *head = new nodeType{10, nullptr};
	head->link = new nodeType{20, nullptr};
	head->link->link = new nodeType{40, nullptr};

	printList(head);          // 10 20 40
	insertAfter(head->link, 30);
	printList(head);          // 10 20 30 40
	deleteAfter(head);        // Delete node 20.
	printList(head);          // 10 30 40

	while (head != nullptr) {
		nodeType *old = head;
		head = head->link;
		delete old;
	}

	return 0;
}
Topic 3 Glossary: Key Words And Code Terms
linked list
A collection of nodes connected by pointer links instead of contiguous array positions.
node
A record that stores one data item and one or more links to other nodes.
info
The data field inside a node in the guide's linked-list examples.
link
The pointer field that stores the address of the next node.
head
The pointer that gives access to the first node in the list.
current
A traversal pointer used to move through the list without losing the first node.
nullptr
The modern C++ null pointer value used when a pointer does not address a node.
->
The pointer member access operator; used for fields such as current->info.
new
Allocates dynamic memory for a node or object.
delete
Releases dynamic memory that was allocated with new.
insert
An ADT operation that adds a new item by adjusting links.
deleteNode
An ADT operation that removes a node and releases its memory.
ordered list
A list where items are stored according to key order.
unordered list
A list where insertion does not maintain sorted order.
doubly linked list
A linked list where each node stores links to both the next and previous nodes.
Back to Topics 1–4 index

4. Recursion

Recursion solves a problem by reducing it to smaller versions of itself. The guide uses factorial, largest-in-array, reverse linked-list printing, Fibonacci-style thinking and backtracking to show when recursive thinking is natural.

Base case General case Recursive function Call stack Backtracking Iteration tradeoffs

4.1 Recursive Definitions

A recursive definition contains a direct answer for the simplest case and a rule that expresses a larger case in terms of a smaller case.

  • The base case gives the answer directly and stops recursion.
  • The general case reduces the problem toward the base case.
  • A recursive function is a function that calls itself.
  • Direct recursion happens when a function calls itself; indirect recursion happens when functions call one another in a cycle.
Exam trap: A recursive solution without a reachable base case is not a solution; it keeps calling until the program fails.

4.2 Problem Solving Using Recursion

The guide's pattern is to identify how the problem shrinks. For factorial, n! becomes n * (n - 1)!. For largest-in-array, the largest of a list is the maximum of the first item and the largest item in the rest of the list.

Problem Base Case General Case
Factorial0! = 1n * fact(n - 1)
Largest in arraySublist length is 1.Compare first value with largest of the rest.
Reverse print listPointer is NULL.Print tail first, then current node.

4.3 Recursion Or Iteration?

Iteration repeats with loops; recursion repeats through function calls. The guide notes that recursion uses extra memory and execution time because every recursive call has its own parameters and local variables.

  • Choose iteration when it is at least as obvious and easier to construct.
  • Choose recursion when the recursive solution maps more naturally to the problem.
  • Any recursive program can be written iteratively, but the iterative version may be much harder to design.
  • When execution time or memory is critical, compare the recursive and iterative costs carefully.

4.3.1 Backtracking

Backtracking builds partial solutions and abandons a path when it cannot lead to a valid solution. The guide uses the n-queens puzzle: place queens row by row, and back up when a row has no legal column.

Guide pattern: test a candidate, record it, recurse to the next decision, then try another candidate if that branch does not complete a solution.

  • Build a partial solution.
  • Reject it as soon as it violates a rule.
  • Try the next choice at the most recent decision point.
  • Continue until a complete valid solution is found or all choices fail.

Topic 4 Extended Code Example: Recursive Largest And Reverse Print

This example shows two guide patterns: a recursive array problem that reduces the index range, and a linked-list problem where the action happens after the recursive call.

#include <iostream>
using namespace std;

struct nodeType {
	int info;
	nodeType *link;
};

int largest(const int list[], int lowerIndex, int upperIndex) {
	// Base case: one element remains, so it is the largest in this sublist.
	if (lowerIndex == upperIndex) {
		return list[lowerIndex];
	}

	// General case: find the largest in the smaller sublist first.
	int maxOfRest = largest(list, lowerIndex + 1, upperIndex);

	if (list[lowerIndex] >= maxOfRest) {
		return list[lowerIndex];
	}
	return maxOfRest;
}

void reversePrint(nodeType *current) {
	// Hidden base case: if current is nullptr, the if body does not run.
	if (current != nullptr) {
		// First print the tail of the list.
		reversePrint(current->link);

		// Then print the current node while the recursive calls unwind.
		cout << current->info << " ";
	}
}

int main() {
	int values[] = {23, 43, 35, 38, 67, 12, 76, 10};
	cout << "Largest: " << largest(values, 0, 7) << endl;

	nodeType *head = new nodeType{5, new nodeType{10, new nodeType{15, nullptr}}};
	cout << "Reverse list: ";
	reversePrint(head); // Prints 15 10 5
	cout << endl;

	while (head != nullptr) {
		nodeType *old = head;
		head = head->link;
		delete old;
	}
	return 0;
}

Variations With Standard C++ Containers

The first example uses a built-in C-style array. These standalone alternatives use std::array, std::vector, and std::list. Each keeps a base case and makes one step toward it; the list version uses iterators because lists do not support indexing.

std::array: recursively find the largest value in an index range.

#include <array>
#include <cstddef>

int largest(const std::array<int, 5>& values,
            std::size_t first, std::size_t last) {
	if (first == last) { // Base case: one value remains.
		return values[first];
	}
	int largestRest = largest(values, first + 1, last);
	return values[first] > largestRest ? values[first] : largestRest;
}

std::array<int, 5> values{23, 43, 35, 38, 67};
int answer = largest(values, 0, values.size() - 1); // 67

std::vector: recursively sum values from the current index onward.

#include <cstddef>
#include <vector>

int recursiveSum(const std::vector<int>& values, std::size_t index = 0) {
	if (index == values.size()) { // Base case: no values remain.
		return 0;
	}
	return values[index] + recursiveSum(values, index + 1);
}

std::vector<int> values{4, 7, 2};
int answer = recursiveSum(values); // 13

std::list: print values in reverse using recursion and iterator advancement.

#include <iostream>
#include <list>

void reversePrint(std::list<int>::const_iterator current,
                  std::list<int>::const_iterator last) {
	if (current == last) { // Base case: reached past the final element.
		return;
	}
	auto next = current;
	++next;
	reversePrint(next, last); // Visit the rest of the list first.
	std::cout << *current << " "; // Print while calls unwind.
}

std::list<int> values{4, 7, 2};
reversePrint(values.cbegin(), values.cend()); // Prints: 2 7 4
Container note: The std::array and std::vector examples use indexes; the std::list example advances an iterator one position per call. All three use O(n) recursive calls for n elements, so very large inputs can exhaust the call stack.
Note: The recursive call must move toward the base case. In largest, lowerIndex increases; in reversePrint, current moves along the link chain.
Topic 4 Glossary: Key Words And Code Terms
recursion
A technique where a function solves a problem by calling itself on a smaller version of the problem.
recursive function
A function that directly or indirectly calls itself.
base case
The stopping case that gives an answer without another recursive call.
general case
The part of the definition that reduces the problem toward the base case.
call stack
The runtime structure that stores active function calls and their local data.
direct recursion
A function calls itself inside its own body.
indirect recursion
Two or more functions call one another in a cycle.
iteration
Repetition using loops such as for, while or do...while.
backtracking
A recursive search method that tries a choice, backs up if it fails, and tries another choice.
factorial
The product n * (n - 1) * ... * 1, often used to introduce recursive definitions.
return
Ends a function call and sends a value back to the caller.
largest()
A common guide example that finds the largest array value by comparing one value with the largest of the rest.
reversePrint()
A linked-list recursion example that prints the tail before the current node.
Back to Topics 1–4 index

5. Stacks

A stack is a Last In First Out structure. The guide connects stacks to function calls, recursive calls, array and linked implementations, postfix expression evaluation, and replacing some recursive algorithms with nonrecursive stack-based algorithms.

LIFO push top pop Array stack Linked stack Postfix

5.1 Stack ADT

A stack adds and removes elements only at the top. The last element pushed is the first one popped, which is why a stack is a LIFO data structure.

top: C
B
bottom: A

push(D) places D above C. top() reads the current top. pop() removes the current top item only.

5.1 Array Implementation

In an array stack, elements are stored in an array and stackTop tracks how many elements are in the stack. The top item is at stackTop - 1.

  • initializeStack sets the stack to empty.
  • isEmptyStack checks whether there are no elements.
  • isFullStack checks whether the fixed array has space.
  • push stores at the top and advances stackTop.
  • pop decreases stackTop after checking the stack is not empty.

5.2 Linked Implementation Of Stacks

A linked stack avoids the fixed-size limit of an array stack by allocating nodes dynamically. In the linked representation, stackTop stores the address of the top node, not an array index.

  • The stack is empty when stackTop == NULL.
  • A linked stack is full only if the program runs out of memory.
  • push creates a new node and links it before the old top.
  • pop saves the top node, moves stackTop down, and deletes the old top.

5.4 Application: Postfix Expressions

In postfix notation, operators appear after operands, so the operators appear in the order required for computation. Stacks fit this perfectly.

  1. Scan the expression left to right.
  2. Push each operand.
  3. When an operator appears, pop the needed operands.
  4. Apply the operator and push the result.
  5. At =, exactly one value should remain: the answer.
Error cases: Too few operands, illegal operators and too many final operands all indicate invalid postfix expressions.

5.6 Removing Recursion And STL stack

The guide uses stacks to replace some recursive processes because a stack can remember items that still need to be processed. C++ also provides the STL stack class adapter for stack-style work.

  • Recursive calls are naturally managed by the computer's call stack.
  • A manual stack can store nodes, states or pending operations.
  • Use this when you need a nonrecursive version of a naturally recursive algorithm.

Topic 5 Extended Code Example: Postfix Calculator With stack

This program implements the guide's postfix rule: operands are pushed, operators pop two operands, and the final stack must contain exactly one answer. Trace 6 3 + 2 * =: push 6 and 3, replace them with 9, push 2, then replace 9 and 2 with 18. Subtraction and division use the first popped value as the right operand.

#include <iostream>
#include <exception>
#include <sstream>
#include <stack>
#include <string>
using namespace std;

bool applyOperator(stack<double>& values, char op) {
	if (values.size() < 2) {
		return false; // Not enough operands for a binary operator.
	}

	double right = values.top();
	values.pop();
	double left = values.top();
	values.pop();

	switch (op) {
		case '+': values.push(left + right); break;
		case '-': values.push(left - right); break;
		case '*': values.push(left * right); break;
		case '/':
			if (right == 0) return false;
			values.push(left / right);
			break;
		default: return false; // Illegal operator.
	}

	return true;
}

bool evaluatePostfix(const string& expression, double& answer) {
	stack<double> values;
	istringstream input(expression);
	string token;
	bool sawEnd = false;

	while (input >> token) {
		if (token == "=") {
			sawEnd = true;
			if (input >> token) return false;
			break;
		}

		if (token == "+" || token == "-" || token == "*" || token == "/") {
			if (!applyOperator(values, token[0])) {
				return false;
			}
		} else {
			try {
				size_t used = 0;
				double value = stod(token, &used);
				if (used != token.size()) return false;
				values.push(value);
			} catch (const exception&) {
				return false;
			}
		}
	}

	if (!sawEnd || values.size() != 1) {
		return false; // Too many operands remain, or no answer exists.
	}

	answer = values.top();
	return true;
}

int main() {
	double result = 0;

	if (evaluatePostfix("6 3 + 2 * =", result)) {
		cout << result << endl; // (6 + 3) * 2 = 18
	} else {
		cout << "Invalid postfix expression" << endl;
	}

	return 0;
}
Topic 5 Glossary: Key Words And Code Terms
stack
A data structure where insertion and deletion happen at one end called the top.
LIFO
Last In First Out; the most recently pushed item is removed first.
top
The end of the stack where all stack operations occur.
push()
Adds a new item to the top of the stack.
pop()
Removes the top item from the stack.
top()
Returns the current top item without removing it.
isEmptyStack()
Checks whether the stack contains no elements.
isFullStack()
Checks whether a fixed array stack has reached capacity.
stackTop
The variable that tracks the current top position or top node.
postfix
An expression format where operators appear after operands, such as 6 3 +.
operand
A value used by an operator.
operator
A symbol such as +, -, * or / that performs an operation.
std::stack
The STL container adapter that provides stack operations.
empty()
An STL stack function that returns true when the stack has no elements.
size()
Returns how many elements are currently stored.
Back to Exam Focus Areas

6. Queues

A queue is a First In First Out structure. Items are added at the rear and deleted from the front, making queues useful for real systems where arrival order matters, such as bank lines, printers and simulations.

FIFO front rear Circular array Linked queue Priority queue Simulation

6.1 Queue Operations

A queue adds at the rear and deletes from the front. Like stacks, queues need guard operations so you do not delete from an empty queue or add to a full queue.

front: A
B
C
rear: D
  • initializeQueue resets the queue.
  • front returns the first element; back returns the last element.
  • addQueue adds at the rear.
  • deleteQueue removes at the front.

6.2 Implementation Of Queues As Arrays

An array queue needs queueFront, queueRear, storage for the elements and a maximum size. A simple one-way movement wastes freed array space, so the guide uses a circular array.

  • Advance an index with (index + 1) % maxQueueSize.
  • A circular array wraps from the last slot back to slot 0.
  • A count variable can distinguish a full queue from an empty queue.
  • queueFront changes after deletion; queueRear changes after insertion.

6.3 Linked Implementation Of Queues

A linked queue stores items in nodes and keeps pointers to the first and last nodes. This supports clean deletion from the front and insertion at the rear without shifting array elements.

  • queueFront points to the node that will be removed next.
  • queueRear points to the node where the next item will be linked.
  • When the final node is deleted, both pointers must represent an empty queue.

6.4 Priority Queues

A priority queue relaxes pure FIFO order. Higher-priority customers or jobs move ahead of lower-priority ones, such as emergency patients or urgent print jobs.

  • A linked list can keep items ordered by priority.
  • A tree-like structure is often more efficient.
  • The STL priority_queue uses a priority rule, often based on <.
  • Priority queues connect directly to heapsort and heap-based priority queues later in the guide.

6.5 Queue Applications: Simulation

The guide uses queues to model systems such as banks, theatres, grocery stores and printers. A server provides service, customers wait in a queue, and the simulation measures behaviour such as average waiting time.

  • A time-driven simulation uses a clock counter.
  • Customer objects store arrival time, waiting time and transaction time.
  • Server objects track whether they are free and how much service time remains.
  • Queues are the natural structure for customers waiting to be served in arrival order.

Topic 6 Extended Code Example: Circular Array Queue

This example uses the guide's circular-array idea: the front and rear wrap around with modulo arithmetic, and count tells whether the queue is empty or full.

#include <iostream>
#include <stdexcept>
using namespace std;

class intQueue {
private:
	static const int maxQueueSize = 5;
	int list[maxQueueSize];
	int queueFront;
	int queueRear;
	int count;

public:
	intQueue() {
		queueFront = 0;
		queueRear = maxQueueSize - 1;
		count = 0;
	}

	bool isEmptyQueue() const {
		return count == 0;
	}

	bool isFullQueue() const {
		return count == maxQueueSize;
	}

	void addQueue(int item) {
		if (isFullQueue()) {
			throw runtime_error("Queue is full");
		}

		queueRear = (queueRear + 1) % maxQueueSize;
		list[queueRear] = item;
		count++;
	}

	int front() const {
		if (isEmptyQueue()) {
			throw runtime_error("Queue is empty");
		}
		return list[queueFront];
	}

	void deleteQueue() {
		if (isEmptyQueue()) {
			throw runtime_error("Queue is empty");
		}

		queueFront = (queueFront + 1) % maxQueueSize;
		count--;
	}
};

int main() {
	intQueue tickets;

	tickets.addQueue(101);
	tickets.addQueue(102);
	tickets.addQueue(103);

	cout << "Serving customer " << tickets.front() << endl;
	tickets.deleteQueue();

	tickets.addQueue(104);
	cout << "Next customer " << tickets.front() << endl;

	return 0;
}
Topic 6 Glossary: Key Words And Code Terms
queue
A data structure where items are added at the rear and removed from the front.
FIFO
First In First Out; the earliest item inserted is removed first.
front
The end of the queue where deletion occurs.
rear
The end of the queue where insertion occurs.
addQueue()
Adds a new item at the rear of the queue.
deleteQueue()
Removes the item at the front of the queue.
front()
Returns the first item, the next item that would be deleted.
back()
Returns the last item, the most recently added item.
queueFront
The index or pointer that identifies the front item.
queueRear
The index or pointer that identifies the rear item or next rear position.
circular array
An array representation where indexes wrap around using modulo arithmetic.
%
The modulo operator used to wrap queue indexes back to the beginning.
priority_queue
An STL adapter where deletion is based on priority rather than pure arrival order.
simulation
A model of a real system, often using queues to represent waiting customers or jobs.
Back to Exam Focus Areas

7. Searching And Hashing Algorithms

Searching determines whether an item exists, where it is located, or where it should be inserted or deleted. The guide compares comparison-based searching with hashing, where an address is computed rather than found by repeated key comparison.

Keys Sequential search Ordered lists Binary search Hashing Collisions Chaining

7.1 Search Algorithms And Keys

A key is the part of a record that uniquely identifies the item, such as a student ID in a student record. Searching, sorting, insertion and deletion often compare or use these keys.

  • A search can simply answer whether an item exists.
  • It can return the item's position.
  • In ordered data, it can also help find where a new item should be inserted.
  • Algorithm analysis counts key comparisons for comparison-based searches.

7.6 Hashing

Hashing uses a hash function to compute the likely address of an item in a hash table. This is not comparison based, which is why hashing can average order 1 search when the table is well designed.

  • The hash table is usually stored in an array.
  • The hash function maps a key to an index: 0 <= h(key) < tableSize.
  • Good hash functions are easy to compute and reduce collisions.
  • The division method uses key % tableSize.
Exam trap: Hashing is not comparison based. You must explain the role of the hash function and how collisions are handled.

7.7-7.8 Collision Resolution

A collision occurs when different keys hash to the same location. Because collisions are unavoidable in realistic tables, the guide covers open addressing and chaining.

Technique Idea Tradeoff
Linear probingSearch the next slots circularly until a free one appears.No pointers, but clustering can occur.
RehashingUse a different probe function after collision.Can spread probes better.
ChainingEach table slot points to a linked list of colliding keys.Handles many collisions, but uses linked-list overhead.

Topic 7 Extended Code Example: Binary Search And Hashing

This example contrasts comparison-based binary search with hash-table addressing and linear probing for collisions.

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int binarySearch(const vector<int>& list, int item) {
	int first = 0;
	int last = static_cast<int>(list.size()) - 1;

	while (first <= last) {
		int mid = (first + last) / 2;

		if (list[mid] == item) {
			return mid;
		} else if (list[mid] > item) {
			last = mid - 1;
		} else {
			first = mid + 1;
		}
	}

	return -1;
}

int hashFunction(int key, int tableSize) {
	// Division method from the guide.
	return key % tableSize;
}

bool insertLinearProbe(vector<int>& table, int key, int emptyKey) {
	int tableSize = static_cast<int>(table.size());
	int index = hashFunction(key, tableSize);
	int probes = 0;

	while (table[index] != emptyKey && probes < tableSize) {
		if (table[index] == key) {
			return true; // Key is already present.
		}
		index = (index + 1) % tableSize; // Circular probe sequence.
		probes++;
	}

	if (probes == tableSize) {
		return false; // No empty slot.
	}

	table[index] = key;
	return true;
}

int main() {
	vector<int> ordered = {12, 18, 22, 34, 39, 58, 75, 89};
	cout << "75 found at index " << binarySearch(ordered, 75) << endl;

	const int emptyKey = -1;
	vector<int> hashTable(9, emptyKey);

	for (int key : {5, 28, 19, 15, 20, 33, 12, 17, 10}) {
		insertLinearProbe(hashTable, key, emptyKey);
	}

	cout << "Hash table: ";
	for (int value : hashTable) {
		cout << value << " ";
	}
	cout << endl;

	return 0;
}
Topic 7 Glossary: Key Words And Code Terms
search key
The value used to identify and compare records during searching.
sequential search
A search that checks items one by one from the start of the list.
binary search
A search that repeatedly halves an ordered array-based list.
ordered list
A list where items are arranged by key order.
first
The lower boundary of the current binary search range.
last
The upper boundary of the current binary search range.
mid
The middle index tested during binary search.
binarySearch()
A function that returns the target position or -1 when the target is absent.
hashing
Computing a storage address from a key instead of searching through comparisons.
hash function
The rule that maps a key to a hash table index.
hash table
The array-like structure that stores items at computed positions.
collision
Occurs when two keys map to the same hash table index.
linear probing
A collision strategy that checks the next table slot until an empty one is found.
chaining
A collision strategy that stores multiple keys at one hash location using a linked list.
load factor
A measure of how full the hash table is.
Back to Exam Focus Areas

8. Sorting Algorithms

Sorting places data in order so later operations, especially binary search, can be more efficient. The guide compares selection sort, insertion sort, Shellsort, quicksort, mergesort, heapsort and priority queues.

Selection sort Insertion sort Shellsort Quicksort Mergesort Heapsort

8.1 Sorting Algorithms

The guide studies sorting algorithms as public operations of list classes. This gives the sorting functions direct access to the stored elements and lets the class decide the most appropriate algorithm for its representation.

  • Array-based lists support direct indexing.
  • Linked-list sorts must work through links.
  • Comparison-based sorts are limited by a lower bound of about O(n log2 n) in the worst case.
  • Selection and insertion sort are simpler but usually O(n2).

8.1 Selection Sort

Selection sort repeatedly finds the smallest element in the unsorted portion and swaps it into the first unsorted position.

  1. Initially, the entire list is unsorted.
  2. Find the smallest element in list[index]...list[length - 1].
  3. Swap it with list[index].
  4. Move index forward and repeat.
Performance idea: Selection sort makes many comparisons but relatively few item movements because each pass mainly swaps once.

8.2 Insertion Sort

Insertion sort divides the array into a sorted upper portion and an unsorted lower portion. The first unsorted element is moved into its correct place in the sorted portion.

  • firstOutOfOrder points to the first item in the unsorted part.
  • If it is already greater than the previous item, it stays where it is.
  • If it is smaller, it is saved in temp while larger sorted items shift down.
  • Finally, temp is inserted into the gap.

8.3 Shellsort

Shellsort is a modified insertion sort that reduces item movement by first sorting elements far apart, then reducing the gap until the final gap is 1.

  • Also called diminishing-increment sort.
  • Moves far-away elements closer to their final positions earlier.
  • The guide uses Knuth-style increments such as 1, 4, 13, 40 and so on.
  • Avoid weak increment patterns that delay comparing important positions until the final pass.

8.4-8.5 Quicksort, Mergesort And Heapsort

The later sorting sections cover quicksort as a comparison, mergesort on linked lists, and heapsort on array-based lists. Sorting changes order; it does not create or remove items.

Algorithm Core Idea Guide Emphasis
QuicksortPartition around a pivot, then recursively sort sublists.Average O(n log2 n), worst case O(n2).
MergesortDivide and merge sorted parts.Useful with linked list-based lists.
HeapsortBuild a heap, repeatedly move the largest item into place.Worst-case O(n log2 n).

Trace: A merge step combines sorted chains [2, 7] and [3, 5] into [2, 3, 5, 7]. A max-heap repeatedly moves its root, the largest remaining key, to the end of the unsorted region.

8.6 Priority Queues Revisited

Heaps connect sorting to priority queues. A heap keeps the highest-priority item available at the root, making it a natural structure for repeated priority removal.

Topic 8 Extended Code Example: Selection Sort And Insertion Sort

This example shows the two early array-based sorts from the guide: selection sort chooses the smallest remaining item, while insertion sort shifts a value into the sorted part.

#include <iostream>
#include <vector>
using namespace std;

void selectionSort(vector<int>& list) {
	for (int index = 0; index < static_cast<int>(list.size()) - 1; index++) {
		int smallestIndex = index;

		// Find the smallest item in the unsorted portion.
		for (int loc = index + 1; loc < static_cast<int>(list.size()); loc++) {
			if (list[loc] < list[smallestIndex]) {
				smallestIndex = loc;
			}
		}

		// Move the smallest item to the start of the unsorted portion.
		int temp = list[index];
		list[index] = list[smallestIndex];
		list[smallestIndex] = temp;
	}
}

void insertionSort(vector<int>& list) {
	for (int firstOutOfOrder = 1;
	     firstOutOfOrder < static_cast<int>(list.size());
	     firstOutOfOrder++) {
		if (list[firstOutOfOrder] < list[firstOutOfOrder - 1]) {
			int temp = list[firstOutOfOrder];
			int location = firstOutOfOrder;

			// Shift larger sorted items one position down.
			do {
				list[location] = list[location - 1];
				location--;
			} while (location > 0 && list[location - 1] > temp);

			// Insert the saved value into its correct location.
			list[location] = temp;
		}
	}
}

void print(const vector<int>& list) {
	for (int value : list) {
		cout << value << " ";
	}
	cout << endl;
}

int main() {
	vector<int> a = {16, 30, 24, 7, 62, 45, 5, 55};
	vector<int> b = a;

	selectionSort(a);
	insertionSort(b);

	print(a);
	print(b);

	return 0;
}
Topic 8 Glossary: Key Words And Code Terms
sorting
Arranging data into a chosen order, usually ascending or descending by key.
selection sort
Repeatedly selects the smallest item from the unsorted part and swaps it into position.
insertion sort
Builds a sorted section by inserting each new item into its correct place.
Shellsort
A diminishing-increment version of insertion sort that compares items far apart first.
quicksort
A divide-and-conquer sort that partitions values around a pivot.
mergesort
A divide-and-conquer sort that divides data and merges sorted parts.
heapsort
A sort that uses a heap structure to repeatedly place the largest item.
pivot
The reference value used to partition data during quicksort.
swap
Exchanges two values, often after finding the smallest or largest item.
firstOutOfOrder
The guide's insertion-sort index for the first item not yet in the sorted portion.
temp
A temporary variable used to hold a value while other items shift or swap.
location
The insertion-sort index that moves backward to find the correct insertion point.
gap
The distance between compared items in Shellsort.
O(n log2 n)
A common efficient comparison-sort growth rate.
O(n2)
Quadratic growth seen in simple sorts such as selection and insertion sort.
Back to Exam Focus Areas

9. Binary Trees And B-Trees

Trees organize data dynamically so insertion, deletion and lookup can be more efficient than plain sequential structures. This chapter moves from general binary trees to binary search trees, AVL balancing and B-trees.

Binary tree Traversal BST Height AVL Rotations B-tree

9.1 Binary Trees

A binary tree is either empty or has a root node plus two binary subtrees called the left subtree and right subtree. Every node has at most two children.

  • The root pointer is stored outside the tree.
  • Each node stores data, a left link and a right link.
  • A leaf has no left or right child.
  • Empty subtrees are part of the definition and matter in algorithms.
Root
Left subtree
Right subtree

9.2 Binary Tree Traversal

Traversal visits every node. Since child links do not point back to the parent, recursive traversal is natural: after a subtree call completes, control returns to the parent.

Traversal Order Root Visit
PreorderRoot, left, right.Before subtrees.
InorderLeft, root, right.Between left and right.
PostorderLeft, right, root.After subtrees.

9.3 Binary Search Trees

A binary search tree gives search direction. At every node, smaller keys are in the left subtree and larger keys are in the right subtree.

  • Search starts at the root.
  • If the item equals the node, the search succeeds.
  • If the item is smaller, follow the left link.
  • If the item is larger, follow the right link.
  • If the search reaches an empty subtree, the item is absent.

9.4-9.6 BST Analysis And Nonrecursive Traversal

The performance of a BST depends on its shape. A nicely built tree behaves like repeated halving; a tree built from sorted data can become linear and behave like a linked list.

  • Search is faster when tree height is small.
  • Nonrecursive traversal uses an explicit stack to remember parent nodes.
  • Traversal functions can accept function parameters so the user controls what happens when a node is visited.

Example: For a root 4 with children 2 and 6, an explicit stack can produce preorder 4 2 6 by pushing the right child before the left child. A visit function could print each key or add it to a total.

9.7-9.8 AVL Trees And Rotations

AVL trees are height-balanced binary search trees. For each node, the heights of the left and right subtrees differ by at most 1.

  • The balance factor is based on right height minus left height.
  • Valid AVL balance factors are -1, 0 and 1.
  • Insertion and deletion may require rotations.
  • Rotations reconstruct a small part of the tree so the BST order remains valid and height balance is restored.

9.9 B-Trees

B-trees generalize search trees so a node can hold multiple keys and multiple children. They are useful when minimizing tree height matters, especially for storage systems where each node access is expensive.

Topic 9 Extended Code Example: Binary Search Tree Insert, Search And Traversal

This example uses the guide's BST rule: compare at the current node, then move left for smaller values and right for larger values. Inorder traversal prints values in sorted order.

#include <iostream>
using namespace std;

struct treeNode {
	int info;
	treeNode *llink;
	treeNode *rlink;
};

void insert(treeNode *&root, int item) {
	if (root == nullptr) {
		root = new treeNode{item, nullptr, nullptr};
		return;
	}

	if (item < root->info) {
		insert(root->llink, item);
	} else if (item > root->info) {
		insert(root->rlink, item);
	}
	// Duplicate values are ignored in this simple example.
}

bool search(treeNode *root, int item) {
	treeNode *current = root;

	while (current != nullptr) {
		if (current->info == item) {
			return true;
		} else if (item < current->info) {
			current = current->llink;
		} else {
			current = current->rlink;
		}
	}

	return false;
}

void inorder(treeNode *root) {
	if (root != nullptr) {
		inorder(root->llink);
		cout << root->info << " ";
		inorder(root->rlink);
	}
}

void destroy(treeNode *&root) {
	if (root != nullptr) {
		destroy(root->llink);
		destroy(root->rlink);
		delete root;
		root = nullptr;
	}
}

int main() {
	treeNode *root = nullptr;

	for (int value : {60, 50, 70, 58, 80, 65}) {
		insert(root, value);
	}

	cout << "Inorder sequence: ";
	inorder(root);
	cout << endl;

	cout << boolalpha << "Search 58: " << search(root, 58) << endl;

	destroy(root);
	return 0;
}
Topic 9 Glossary: Key Words And Code Terms
binary tree
A tree where each node has at most two children.
root
The top node of a tree, or the pointer that gives access to the whole tree.
leaf
A node with no children.
subtree
A smaller tree rooted at a child node.
llink
The guide's left-child pointer in a binary tree node.
rlink
The guide's right-child pointer in a binary tree node.
preorder
Traversal order: root, left subtree, right subtree.
inorder
Traversal order: left subtree, root, right subtree.
postorder
Traversal order: left subtree, right subtree, root.
BST
Binary search tree; left values are smaller and right values are larger at each node.
insert()
Adds a new node while preserving binary search tree order.
search()
Follows left or right links until an item is found or an empty subtree is reached.
destroy()
Deletes tree nodes, usually with postorder-style recursion.
AVL tree
A height-balanced binary search tree.
rotation
A restructuring step used to restore AVL balance.
B-tree
A multiway search tree designed for efficient disk/file-system style access.
Back to Exam Focus Areas

10. Graphs

Graphs model relationships using vertices and edges. The guide introduces graph terminology, memory representation, graph ADTs, depth-first and breadth-first traversal, shortest paths, minimum spanning trees and topological ordering.

Vertices Edges Adjacency list DFS BFS Dijkstra Prim

10.2 Graph Definitions And Notations

A graph G = (V, E) has a finite nonempty set of vertices and a set of edges. If the edges are ordered pairs, the graph is directed; otherwise, it is undirected.

  • Adjacent vertices are connected by an edge.
  • A loop is an edge incident on a single vertex.
  • A simple graph has no loops and no parallel edges.
  • A path is a sequence of vertices connected by edges.
  • A connected graph has a path from any vertex to any other vertex.
A
B
C
A-B, B-C, A-C

10.1 Graph Representation

Programs must store graphs in memory before processing them. The guide focuses on two common representations.

Representation How It Works Useful Reminder
Adjacency matrixAn n x n matrix stores 1 or 0 depending on whether an edge exists.Undirected graphs produce symmetric matrices.
Adjacency listAn array stores one linked list per vertex, listing adjacent vertices.Good when many possible edges are absent.

10.4 Graphs As ADTs

A graph ADT hides the representation and exposes graph operations. In the guide, graphType stores an array of linked lists, plus maxSize and gSize.

  • isEmpty checks whether the graph has no vertices.
  • createGraph builds the adjacency-list representation.
  • clearGraph deallocates graph storage.
  • printGraph, DFS and BFS expose useful graph operations.

10.5 Graph Traversals

Graph traversal is more complex than tree traversal because graphs may contain cycles and may not be connected. The guide uses a visited array to prevent repeated visits and restarts traversal at unvisited vertices.

Traversal Structure Used Behaviour
Depth-first traversalRecursion or stackGo as deep as possible before backing up.
Breadth-first traversalQueueVisit neighbours first, then their neighbours.

10.7 Shortest Path

For graphs with nonnegative edge weights, the guide introduces Dijkstra's shortest path algorithm. Starting from a source vertex, it repeatedly chooses the closest unsettled vertex and updates distances through that vertex.

  1. Initialize the smallest known weights from the source.
  2. Mark the source weight as 0.
  3. Choose the unprocessed vertex with the smallest known weight.
  4. Relax outgoing edges by checking whether going through that vertex improves a path.
  5. Repeat until shortest distances are found.
Exam trap: Shortest path uses edge weights; ordinary graph traversal order alone does not prove a weighted shortest path.

10.8-10.10 Minimum Spanning Trees And Topological Order

A spanning tree connects all vertices of a connected graph without cycles. A minimum spanning tree has the smallest total edge weight. The guide covers Prim's algorithm, then topological order for directed acyclic dependency-style graphs.

  • Prim's algorithm starts with a source vertex and repeatedly adds the cheapest edge that connects a new vertex.
  • A graph has a spanning tree only if it is connected.
  • Topological ordering places vertices before the vertices that depend on them.
  • Breadth-first topological ordering uses indegrees and a queue.

Topological trace: For A→C and B→C, start with predCount(A)=0, predCount(B)=0, and predCount(C)=2. Queue A and B. Output each, decreasing C's count. Queue C only when its count becomes 0; the result can be A, B, C or B, A, C.

Topic 10 Extended Code Example: Adjacency List With DFS And BFS

This example stores a graph as adjacency lists, then traverses it with depth-first recursion and breadth-first queue processing.

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

class graphType {
private:
	vector<vector<int>> graph;

	void dft(int vertex, vector<bool>& visited) const {
		visited[vertex] = true;
		cout << vertex << " ";

		// Visit each adjacent vertex that has not already been reached.
		for (int adjacent : graph[vertex]) {
			if (!visited[adjacent]) {
				dft(adjacent, visited);
			}
		}
	}

public:
	graphType(int vertices) : graph(vertices) {}

	void addEdge(int from, int to) {
		graph[from].push_back(to);
	}

	void depthFirstTraversal() const {
		vector<bool> visited(graph.size(), false);

		for (int v = 0; v < static_cast<int>(graph.size()); v++) {
			if (!visited[v]) {
				dft(v, visited);
			}
		}
		cout << endl;
	}

	void breadthFirstTraversal() const {
		vector<bool> visited(graph.size(), false);
		queue<int> q;

		for (int start = 0; start < static_cast<int>(graph.size()); start++) {
			if (visited[start]) continue;

			visited[start] = true;
			q.push(start);

			while (!q.empty()) {
				int current = q.front();
				q.pop();
				cout << current << " ";

				for (int adjacent : graph[current]) {
					if (!visited[adjacent]) {
						visited[adjacent] = true;
						q.push(adjacent);
					}
				}
			}
		}
		cout << endl;
	}
};

int main() {
	graphType g(6);

	g.addEdge(0, 1);
	g.addEdge(0, 5);
	g.addEdge(1, 2);
	g.addEdge(1, 3);
	g.addEdge(5, 4);

	cout << "DFS: ";
	g.depthFirstTraversal();

	cout << "BFS: ";
	g.breadthFirstTraversal();

	return 0;
}
Topic 10 Glossary: Key Words And Code Terms
graph
A structure made of vertices and edges, written as G = (V, E).
vertex
A node or point in a graph.
edge
A connection between vertices.
directed graph
A graph where edges have direction.
undirected graph
A graph where edges do not have direction.
adjacent
Describes two vertices connected by an edge.
path
A sequence of vertices connected by edges.
connected graph
A graph where a path exists between every pair of vertices.
adjacency matrix
A two-dimensional representation marking whether each edge exists.
adjacency list
A representation where each vertex stores a list of its neighbours.
visited
A Boolean tracking structure used to avoid revisiting vertices.
DFS
Depth-first search; explores deeply before backing up.
BFS
Breadth-first search; visits neighbours before moving to the next level.
dft()
The recursive helper used in the example for depth-first traversal.
breadthFirstTraversal()
A queue-based traversal that processes vertices in breadth-first order.
Dijkstra
A shortest-path algorithm for weighted graphs with nonnegative edge weights.
Prim
An algorithm for finding a minimum spanning tree in a connected weighted graph.
Back to Exam Focus Areas

Classes, Functions, Methods & Attributes

Choose a task from Topics 1–10, or use the alphabetical index to jump to a name. Each entry explains its purpose, shows an example, and links to the full chapter explanation. Names from the guide are identified separately from useful standard C++ additions.

Go to General Module Content
Scope labels: “Study-guide term” marks concepts or APIs explicitly treated in the study guide. “Useful supporting detail” adds standard C++ usage or practical clarification; it is not additional confirmed test content.

Choose a programming task

Find an API name · A–Z

Task 1 · Design a class and its contract

class, data members, and member functions Study-guide term

Does: Defines a user type that groups state and operations. Use it when: the problem has objects with maintained data and meaningful behavior. Example: class Clock { int hour; void print() const; };

Full explanation: classes

private, protected, public Study-guide term

Does: Controls access: private to the class, protected to the class and derived classes, public to clients. Use it when: protecting representation while exposing required operations. Example: private: int hour; public: int getHour() const;

Full explanation: classes and access control

Constructors and destructors Study-guide term

Does: A constructor establishes the initial object state; a destructor runs when the object lifetime ends and releases resources the object owns. Use it when: every object needs a valid starting state or owns a resource. Example: Clock() : hour(0) {} ~Clock() = default;

Full explanation: class example

Accessors, mutators, and const member functions Useful supporting detail

Does: Accessors report state; mutators change it; a trailing const promises a member function will not modify the object. Use it when: offering controlled access to private data. Example: int getHour() const { return hour; }

Full explanation: class interface

Preconditions, postconditions, and assert Study-guide concept

Does: A precondition states what must be true before a call; a postcondition states what the function guarantees afterward. assert(condition) checks a development-time assumption and terminates if it fails. Example: assert(radius > 0);

Full explanation: function contracts

Abstract data type (ADT) Study-guide term

Does: Describes a type through its name, valid values, and operations while leaving representation details hidden. Use it when: specifying what clients may do without tying them to storage choices. Example: a clock ADT offers setTime() and printTime().

Full explanation: abstraction and ADTs

UML class box Study-guide outcome

Does: Summarises a class name, attributes, operations, and visibility. Use it when: translating a design into a class interface. Example: - radius: double is private; + area(): double is public.

Full explanation: UML class notation

Task 2 · Choose a container and process a range

vector<T> Study-guide term

Does: Stores a growable sequence with random-access indexing. Use it when: indexed access is useful and items are mostly appended. Example: vector<int> marks{72, 85}; marks.push_back(91); End insertion is amortized O(1); an individual reallocation may be O(n).

Full explanation: vector

deque<T> and std::list<T> Study-guide terms

Does: A deque supports efficient operations at both ends; a list is a doubly linked sequence. Use it when: the needed end operations or linked-node behavior justify them. Example: items.push_front(x); items.push_back(y); (valid for deque and list, not vector).

Full explanation: sequence containers · Full explanation: STL list

begin() and end() Study-guide term

Does: Marks a half-open range [first, past-the-last). Use it when: traversing a container or passing a range to an algorithm. Example: for (auto it = v.begin(); it != v.end(); ++it) use(*it); Never dereference end().

Full explanation: begin() and end()

Iterator operations: *it, ++it, it != last Study-guide term

Does: Reads the current element, advances to the next, and checks whether the stopping position is reached. Use it when: writing container-independent traversal. Example: sum += *it; ++it;

Full explanation: iterators

size(), empty(), operator[], at() Useful supporting detail

Does: Reports element count, tests emptiness, accesses by index without bounds checks ([]), or accesses with bounds checking (at). Example: if (!v.empty()) cout << v.at(0);

Full explanation: vector storage and access

push_back(), push_front(), insert(), erase() Study-guide terms

Does: Adds/removes elements at an end or iterator position. Use it when: the container's insertion point matches the task. Example: auto pos = v.begin() + 1; v.insert(pos, 90); Vector middle insertion shifts later elements and invalidates some iterators.

Full explanation: insertion and erasure

std::copy(first, last, destination) Study-guide term

Does: Copies every element in [first, last) to a destination starting position. Use it when: copying a range into already-allocated output space. Example: copy(v.begin(), v.end(), out.begin()); The output must have enough writable elements.

Full explanation: copy()

ostream_iterator<T> Study-guide term

Does: Adapts an output stream so algorithms can write values to it. Use it when: printing a range with copy. Example: copy(v.begin(), v.end(), ostream_iterator<int>(cout, " ")); Include <iterator>.

Full explanation: ostream_iterator and copy

Task 5 · Apply common STL algorithms

sort(), stable_sort(), partial_sort(), list::sort(), forward_list::sort() Useful supporting detail

Does: Orders a range, preserving equivalent-element order with stable_sort or ordering only a leading part with partial_sort. Use it when: the container's iterator and element types satisfy that algorithm's requirements. Generic sorting needs random-access writable iterators; linked-list containers provide their own sort() members.

Full explanation: algorithm and container compatibility

find(), find_if(), count(), count_if() Useful supporting detail

Does: Locates a matching value, tests a predicate, or counts matching elements in an iterator range. Use it when: reading any iterable container; the range need not be sorted. These are standard-library algorithms in std.

Full explanation: algorithm and container compatibility

lower_bound(), upper_bound(), binary_search() Useful supporting detail

Does: Searches a range ordered by the same comparison rule. Use it when: finding an insertion point or checking membership in a sorted range. For set and map, use their member lookup functions. These are standard-library algorithms in std.

Full explanation: algorithm and container compatibility

transform(), for_each() Useful supporting detail

Does: Writes transformed values to an output range or applies a callable to each element. Use it when: processing iterator ranges; ensure output iterators point to writable storage or use an inserter when appending. These are standard-library algorithms in std.

Full explanation: algorithm and container compatibility

reverse(), list::reverse(), forward_list::reverse(), min_element(), max_element() Useful supporting detail

Does: Reverses writable elements, or finds the minimum/maximum element in a range. Use it when: the container exposes the iterator operations required; the generic reverse needs bidirectional writable iterators. Generic algorithms are in std.

Full explanation: algorithm and container compatibility

remove(), remove_if(), accumulate() Useful supporting detail

Does: Moves unwanted values to the end of a sequence, or folds a range into one accumulated result. Use it when: applying remove with the appropriate erase/member operation, or summing/combining values with accumulate from <numeric>. These algorithms are in std.

Full explanation: algorithm and container compatibility

Task 3 · Build, traverse, and update a linked list

nodeType, info, link, head/first Study-guide terms

Does: A node stores one item and a pointer to the next node; head/first reaches the first node. Use it when: representing a sequence with individually linked nodes. Example: struct Node { int info; Node* link; };

Full explanation: nodes and links

last, count, current Guide-style attributes

Does: last caches the tail, count caches length, and current is a temporary traversal pointer. Use it when: the list implementation needs fast end insertion/count access or safe traversal. Keep cached values accurate after mutations. Example: Node* current = first;

Full explanation: traversal discipline

new, delete, and nullptr Study-guide code terms

Does: Allocates a node, releases a node, and represents a pointer to no node. Use it when: managing a hand-built linked list. Example: Node* p = new Node{value, nullptr}; delete p; Every allocated node must be released exactly once.

Full explanation: linked-list code example

initializeList(), isEmptyList(), length(), print() Study-guide ADT operations

Does: Establishes an empty state, checks it, counts nodes, or displays items. Use it when: clients should work through the list interface rather than manipulate pointers. Example: if (!list.isEmptyList()) list.print(); Exact method names/signatures depend on the class design.

Full explanation: linked-list ADT

front(), back(), search(), retrieve() Study-guide ADT operations

Does: Reads endpoint values or finds/returns a matching item. Use it when: asking for data without exposing node representation. Empty-list behavior must be specified. Example: if (!list.isEmptyList()) firstValue = list.front();

Full explanation: linked-list ADT

insert(), deleteNode(), destroyList(), copyList() Study-guide ADT operations

Does: Adds an item, removes a matching node, frees every node, or creates a separate copy. Use it when: changing list contents or managing ownership. A copy should duplicate nodes rather than make two lists share one chain. Example: insertAfter(p, value); In a singly linked list, save the next link before bypassing and deleting a node.

Full explanation: insertion and deletion · Full explanation: list ADT and copy

operator*, operator++, operator!= on a list iterator Study-guide term

Does: Dereferences the current node, advances to its successor, and compares iterators. Use it when: making a custom list work with standard traversal patterns. Example: for (auto it = values.begin(); it != values.end(); ++it) std::cout << *it;

Full explanation: linked-list iterators

Task 4 · Solve a problem recursively

Base case and general case Study-guide terms

Does: The base case answers the smallest problem directly; the general case reduces it toward that case. Use it when: each problem can be expressed in terms of a smaller instance. Example: if (n == 0) return 1; return n * fact(n - 1);

Full explanation: recursive definitions

Recursive function and return Study-guide terms

Does: A recursive function calls itself on a smaller case; return sends a result to the previous call. Use it when: the result is naturally composed from smaller results. Example: return n * fact(n - 1);

Full explanation: problem solving with recursion

factorial(n) and rFibNum(a, b, n) Study-guide examples

Does: Computes a factorial or a term in a sequence defined from two starting values. Use it when: demonstrating how a mathematical recurrence becomes a recursive function. Example: fact(0) == 1; otherwise fact(n) == n * fact(n - 1). Validate the input domain and beware integer overflow.

Full explanation: factorial and Fibonacci examples

largest(values, low, high), recursiveSum(values, index), reversePrint(iterator, last) Study-guide examples

Does: Demonstrates recursive reduction over array and vector indexes, plus reverse-order output from a std::list using iterators. Use it when: comparing how recursion works with different container interfaces.

Full explanation: recursive container examples

reversePrint(node) Study-guide example

Does: Prints a linked list in reverse without changing its links. Use it when: demonstrating work performed after a recursive call. Example: recurse on node->link, then print node->info.

Full explanation: reverse-print example

canPlaceQueen(k, column) and queensConfiguration(k) Study-guide functions

Does: Tests whether a queen is safe in a column, then recursively tries legal placements for row k. Use it when: following the guide's n-Queens backtracking example. Example: if (canPlaceQueen(k, col)) { queen[k] = col; queensConfiguration(k + 1); } The guide's class uses its own state and exact signatures.

Full explanation: backtracking and n-Queens

Call stack and backtracking Study-guide concepts/examples

Does: Each active call retains its parameters and local state; backtracking explores a choice and returns to try another when it fails. Use it when: reasoning about unwind order or searching a tree of choices. Example: place one queen, recurse to the next row, then try the next column when that branch fails.

Full explanation: recursion and iteration costs · Full explanation: backtracking and n-Queens

Task 6 · Implement a stack and evaluate postfix

stackTop, initializeStack(), isEmptyStack(), isFullStack() Study-guide terms

Does: Tracks the top and guards operations. In an array stack, stackTop is the element count and the top is at stackTop - 1; in a linked stack it points to the first node. Example: an empty array stack has stackTop == 0.

Full explanation: array stack · Full explanation: linked stack

push(), top(), pop() Study-guide operations

Does: Adds, reads, or removes the most recent item. Use it when: work must be handled Last In First Out. Example: push A, then B; top() gives B; after pop(), top is A. Check empty before top or pop and full before pushing to a fixed array.

Full explanation: stack ADT

std::stack<T> and postfix evaluation Study-guide terms

Does: Provides a stack adapter and a way to evaluate operators after their operands. Example: 6 3 + 2 * = pushes 6 and 3, replaces them with 9, then multiplies by 2 to produce 18. For 8 3 -, pop 3 as the right operand and 8 as the left operand.

Full explanation: postfix rules · Full C++ example

Task 7 · Model a queue and a service line

initializeQueue(), isEmptyQueue(), isFullQueue(), front(), back() Study-guide operations

Does: Establishes queue state, checks guards, and reads either end. Use it when: customers or jobs leave in arrival order. Example: with A then B waiting, front() gives A and back() gives B; do not read either end of an empty queue.

Full explanation: queue operations

addQueue(), deleteQueue(), queueFront, queueRear Study-guide terms

Does: Adds at the rear and removes from the front. In a circular array, indexes wrap with (index + 1) % capacity; a count separates empty from full. Example: capacity 3, front index 2: after removal, the next front index is 0.

Full explanation: circular array · Full C++ example

priority_queue<T>, customer and server state Study-guide terms

Does: Priority selection serves the highest-priority item first; a time-driven simulation uses arrival, service, and waiting times. Example: at minute 4, if a free server takes a customer who arrived at minute 2, that customer's wait is 2 minutes. A linked queue must reset both end pointers after its last node is removed.

Full explanation: priorities · Full explanation: simulation

Task 8 · Search an ordered list or hash table

Sequential search, ordered insertion, and binary search Study-guide algorithms

Does: Sequential search checks items in order; binary search halves an already sorted array range. Example: insert 35 into [10, 30, 50] by shifting 50 right. Binary search for 50 compares a middle key and retains only the half that can contain 50. A sorted precondition is essential.

Full explanation: search and insertion · Full explanation: binary search

hash(key), linear probing, rehashing, chaining Study-guide terms

Does: Converts a key to a table index and resolves collisions. Example: with table size 7, keys 10 and 17 both map to index 3 using key % 7. Linear probing tests index 4 for 17; chaining keeps both keys in the bucket at index 3. Search and deletion must follow the same collision rule.

Full explanation: hashing · Full explanation: collisions

Task 9 · Select a sorting algorithm

selectionSort(), insertionSort(), Shellsort Study-guide algorithms

Does: Selection repeatedly chooses the smallest remaining key; insertion shifts larger keys to open a slot; Shellsort insertion-sorts spaced subsequences with shrinking gaps. Example: insertion of 2 into sorted [3, 5] shifts both keys right, yielding [2, 3, 5].

Full explanation: selection · Full explanation: insertion · Full explanation: Shellsort

Quicksort, linked-list mergesort, heapsort Study-guide algorithms

Does: Partitions around a pivot, merges sorted linked chains, or repeatedly removes the largest heap root. Example: merge [2, 7] and [3, 5] into [2, 3, 5, 7]. Mergesort and heapsort have O(n log n) worst-case time; quicksort can degrade to O(n²).

Full explanation: advanced sorts

Task 10 · Traverse and balance a tree

root, left, right, preorder, inorder, postorder Study-guide terms

Does: A binary node has at most two children; traversals visit root and subtrees in different orders. Example: root 4 with left 2 and right 6 yields preorder 4 2 6, inorder 2 4 6, and postorder 2 6 4. A queue gives level order.

Full explanation: tree structure · Full explanation: traversal

BST search, insert, delete, and nonrecursive traversal Study-guide algorithms

Does: Compares a key with the current node and follows left for smaller or right for larger. Example: search 6 in the tree above: 6 is greater than 4, so move right. Deleting a node with two children requires a replacement key from a subtree; an explicit stack can replace the call stack during depth-first traversal.

Full explanation: BST operations · Full explanation: analysis and traversal

AVL balance factor, rotations, and B-tree nodes Study-guide terms

Does: AVL rotations restore height balance; a B-tree stores several ordered keys per node and keeps leaves at the same depth. Example: inserting 1, 2, 3 into an AVL tree creates a right-right imbalance; a left rotation makes 2 the root. B-tree insertion may split a full node and promote a key.

Full explanation: AVL trees · Full explanation: B-trees

Task 11 · Represent and analyse a graph

Vertex, edge, adjacency list, adjacency matrix Study-guide terms

Does: Represents connections between vertices, with optional direction and weight. Example: edges A–B and A–C give A two neighbours in an adjacency list; a matrix marks their two A-row positions. A matrix uses O(V²) space, while a list uses O(V + E).

Full explanation: graph terms · Full explanation: representations

Depth-first search (DFS), breadth-first search (BFS) Study-guide algorithms

Does: DFS explores a path before returning; BFS visits neighbours in layers with a queue. Example: for A connected to B and C, BFS from A visits A before B and C; each vertex needs a visited mark to avoid repeats in a cycle.

Full explanation: graph traversals · Full C++ example

Dijkstra shortest path, Prim minimum spanning tree Study-guide algorithms

Does: Dijkstra improves tentative source distances for nonnegative edge weights; Prim adds the cheapest edge from the growing tree to a new vertex in a connected weighted undirected graph. Example: if A–B costs 2 and B–C costs 3, the A–B–C route costs 5; compare that with any direct A–C edge before accepting it as shortest.

Full explanation: shortest path · Full explanation: minimum spanning tree

Topological order, predCount, queue Study-guide terms

Does: Orders vertices of a directed acyclic graph so each prerequisite comes first. The breadth-first method queues all indegree-zero vertices and lowers successor counts as each is output. Example: with A→C and B→C, A and B can appear in either order, but C must follow both. A cycle prevents a complete order.

Full explanation: topological order
Back to General Module Content

Learning Lab

Use these interactive tools to practise Programming 622 concepts. Your quiz attempts are saved only in this browser.

Quiz Builder

Choose how many questions to draw from each topic, then answer and submit when ready.

Flashcards

Recall the definition before flipping the card.

Concept Challenge

Explain It

Programming 622 Video Reference

Topic 1 · Software development life cycle

Explains the stages of software development, from planning and requirements through testing and maintenance. Popularity snapshot: about 1.2M views.

Topic 1 · C++ classes and object-oriented programming

Classes, objects, constructors, encapsulation, inheritance, and polymorphism. Popularity snapshot: 3.19M views and 82K likes.

Topic 2 · Standard Template Library (STL)

Beginner introduction to C++ STL containers, including vectors, lists, maps, and more.

Topic 3 · Linked-list implementation (14 min)

A 14-minute chapter from the C/C++ data structures course, starting at the linked-list implementation lesson.

Topic 4 · Recursion with linked lists (23 min)

A focused chapter segment on printing and reversing a linked list recursively.

Topic 5 · Stack implementation (13 min)

A short segment showing stack operations implemented with an array.

Topic 6 · Queue implementation (15 min)

A short segment showing queue operations implemented with an array.

Topic 7 · Binary search

Explains binary search and compares it with linear search, with a C++ implementation. Popularity snapshot: 237K views and 2.7K likes.

Topic 7 · Hash tables and collision handling

Short introduction to hash tables and collision handling with chaining. Popularity snapshot: 1.18M views and 14K likes.

Topic 8 · Sorting algorithms visual companion

Visual comparison of merge sort, heap sort, quicksort, and other sorting methods. Use this alongside the course explanations. Popularity snapshot: 28.6M views and 564K likes.

Topic 9 · B-trees

Focused animated explanation of B-tree structure and operations. Popularity snapshot: about 1.13M views and 43K likes.

Topic 9 · Binary trees (16 min)

Chapter segment introducing binary trees and their structure.

Topic 9 · Binary search trees (19 min)

Chapter segment introducing binary search trees and their implementation.

Topic 10 · Graph basics and traversal (22 min)

A 22-minute chapter segment on graph basics, depth-first search, and breadth-first search. Demonstrations use JavaScript; focus on the algorithm and translate it into C++. Popularity snapshot for the full video: 1.49M views and 33K likes.

Programming 621 · Previous Study Reference

This section brings together the supplied Programming 621 exam pointers and chapter notes as a reference to material covered previously. Its exam-pointer labels describe Programming 621 and do not establish Programming 622 test scope.

Exam Pointer Map

Start here

These are the pointer-file topics grouped into the main exam skills. Each row points to the deeper chapter review further down the page.

Pointer topicWhat to knowGo deeper
Overloading and default argumentsSame function name, different parameter lists; default values supplied in the prototype.Chapter 4, Chapter 7
ifstream, ofstream, fstreamRead, write, or read/write file data using stream objects.Chapter 8
try, catch, throwDetect runtime problems, throw a value, and handle it in a matching catch block.Chapter 8
do-while login/menu loopsRun once before testing the condition; ideal for repeated prompts and menus.Chapter 3
Function prototypes and callsDeclare before use; match return type, function name, and parameter order.Chapter 4
calculateFine() and printReceipt()Use value-returning functions when a result is needed; use void for output/action functions.Chapter 4
Pure virtual functionsUse = 0 to make a base class abstract.Chapter 7
Initializer listsCall base constructors before the derived constructor body runs.Chapter 6
Function overridingDerived classes provide their own version of a virtual base function.Chapter 7
Object pointers and deletionStore derived objects through base pointers, call virtual functions, then delete dynamic objects.Chapter 6, Chapter 7
Pointer arithmetic and arraysMove through arrays using addresses, dereferencing, and pointer increments.Chapter 5

Functions and Overloading

Exam focus

Prototype, Call, Definition

A function prototype tells the compiler the function name, return type, and parameter list before the function is called.

double calculateFine(int daysLate);   // prototype
void printReceipt(string name, double fine);

int main()
{
    int daysLate = 3;
    double fine;

    fine = calculateFine(daysLate);    // call
    printReceipt("Maya", fine);        // call
    return 0;
}

double calculateFine(int daysLate)    // definition
{
    double fine;

    fine = daysLate * 2.50;
    return fine;
}

Overloading and Defaults

Function overloading means using the same function name with different formal parameter lists. Constructor overloading uses the same idea inside a class.

void display(int mark);
void display(double average);
void display(string name, int mark = 0);

class Student {
public:
    Student();
    Student(string n);
    Student(string n, int m);
};
Do not overload by return type only. The parameter list must be different.

File Streams

Exam focus

Stream Classes

ClassPurposeOperator pattern
ifstreamInput file stream; reads from a file.inFile >> value;
ofstreamOutput file stream; writes to a file.outFile << value;
fstreamCan read and write when opened with the correct mode.file >> and file <<

Typical Read Pattern

#include <fstream>
using namespace std;

ifstream inFile;
int mark;
int total = 0;

inFile.open("marks.txt");

if (!inFile) {
    cout << "File could not open";
} else {
    while (inFile >> mark) {
        total += mark;
    }
}

inFile.close();

Exception Handling

Exam focus

try, throw, catch

  • try contains code that may cause a runtime problem.
  • throw sends an exception value when the problem is detected.
  • catch receives and handles the thrown value.
try {
    if (daysLate < 0) {
        throw daysLate;
    }

    fine = calculateFine(daysLate);
}
catch (int value) {
    cout << "Invalid days: " << value;
}

Common Mistakes

  • A catch block only runs if something is thrown.
  • Code after throw inside the same block is skipped.
  • Exception handling does not fix bad logic; it handles exceptional cases.

do-while Login and Menu Patterns

Exam focus

Login Loop

A do-while loop is useful when the user must be prompted at least once.

do {
    cout << "Enter password: ";
    cin >> password;
} while (password != "admin");

Menu Loop

do {
    cout << "1. Add\\n2. Print\\n0. Exit\\n";
    cin >> choice;

    switch (choice) {
    case 1:
        addRecord();
        break;
    case 2:
        printRecords();
        break;
    case 0:
        cout << "Goodbye";
        break;
    default:
        cout << "Invalid choice";
    }
} while (choice != 0);

Virtual Functions and Inheritance

Exam focus

Abstract Base Class

class Student {
protected:
    string name;
public:
    Student(string n)
    {
        name = n;
    }

    virtual ~Student() {}
    virtual void print() const = 0;
};

The = 0 makes print() pure virtual. The base class becomes abstract, so you cannot create plain Student objects.

  • The base class stores common data, such as name.
  • protected lets derived classes use the member while still hiding it from the rest of the program.
  • The virtual destructor helps clean up correctly when deleting through a base-class pointer.

Derived Classes and Overriding

class FullTimeStudent : public Student {
private:
    double tuition;
public:
    FullTimeStudent(string n, double t)
        : Student(n)
    {
        tuition = t;
    }

    void print() const override {
        cout << name << " " << tuition;
    }
};
Initializer lists call the base constructor before the derived constructor body runs.
  • : public Student means FullTimeStudent is a kind of Student.
  • : Student(n) calls the base-class constructor before the derived constructor body runs.
  • tuition = t; is the more foundational way to assign the derived class's own data inside the constructor body.
  • override confirms that this function replaces a virtual function from the base class.

The Colon in Inheritance

In C++, the colon is required when one class inherits from another class. There is no simpler replacement for the inheritance line itself.

class FullTimeStudent : public Student
{
    // FullTimeStudent inherits from Student
};
  • : public Student means FullTimeStudent is a type of Student.
  • The word public keeps the base class's public members public in the derived class.
  • For exam writing, read the colon as "inherits from".
The inheritance colon is required. What can be made more foundational is the constructor body after the class has inherited.

The Tilde in Destructors

A destructor is a special member function that runs when an object is destroyed. It has the same name as the class, but starts with ~.

class Student {
public:
    virtual ~Student()
    {
        // cleanup code would go here
    }
};
  • ~Student() means "destructor for Student".
  • A destructor has no return type, not even void.
  • Use virtual on a base-class destructor when derived objects may be deleted through a base-class pointer.
  • If there is no special cleanup, the destructor body can be empty.

Iterating Through Object Pointers

Student* list[2];
list[0] = new FullTimeStudent("Asha", 12000);
list[1] = new PartTimeStudent("Ben", 3);

for (int i = 0; i < 2; i++) {
    list[i]->print();      // virtual call
}

for (int i = 0; i < 2; i++) {
    delete list[i];        // release memory
    list[i] = nullptr;     // no longer points to deleted memory
}
LineLogic
Student* list[2];The array stores addresses of Student-type objects.
new FullTimeStudent(...)A derived object is created dynamically and its address is stored in a base-class pointer.
list[i]->print()Because print() is virtual, C++ chooses the correct derived version at runtime.
delete list[i];Memory created with new must be released after use.
list[i] = nullptr;After deletion, clearly mark the pointer as pointing to no valid object.

Pointer Arithmetic

Exam focus

2D Array Traversal

int votes[2][3] = {{12, 8, 15}, {9, 17, 11}};
int* p;
p = &votes[0][0];

for (int i = 0; i < 6; i++) {
    cout << *(p + i) << " ";
}

A two-dimensional array is stored row by row in memory. The pointer starts at the first element, then p + i moves to the next integer position.

ExpressionElement reachedValue
*(p + 0)votes[0][0]12
*(p + 1)votes[0][1]8
*(p + 2)votes[0][2]15
*(p + 3)votes[1][0]9

Maximum and Total Votes

int maximum(int* p, int size)
{
    int maxValue = *p;
    for (int i = 1; i < size; i++) {
        if (*(p + i) > maxValue) {
            maxValue = *(p + i);
        }
    }
    return maxValue;
}

int totalVotes(int* p, int size)
{
    int total = 0;
    for (int i = 0; i < size; i++) {
        total += *(p + i);
    }
    return total;
}
  • *p means "the value at the address currently stored in p".
  • *(p + i) means "move i elements forward, then read the value there".
  • The size argument protects the function from reading past the end of the array.

Pointer Logic: Address, Dereference, Move

Most pointer questions use the same three-step thinking pattern. First identify the address, then decide whether the code moves the pointer, and only then read or change the value.

StepQuestion to askExample
AddressWhat memory location does the pointer hold?p = &marks[0];
MoveDoes the expression move to another element?p + 2 reaches the third element.
DereferenceDoes the code read the value at that address?*(p + 2) reads the third element's value.
int marks[4] = {70, 65, 88, 91};
int* p;
p = marks;               // same as &marks[0]

cout << *p;             // 70
cout << *(p + 2);       // 88

p++;                    // p now points at marks[1]
cout << *p;             // 65
Exam trap: p + 2 does not change p. It only calculates another address. p++ actually changes where p points.

Why Pointer Cleanup Is Necessary

new reserves memory while the program is running. That memory remains reserved until it is released with delete, even if the pointer variable goes out of scope.

ProblemMeaningCause
Memory leakAllocated memory is never returned to the program.Losing or replacing a pointer before calling delete.
Dangling pointerA pointer still contains the address of memory that has already been deleted.Calling delete without resetting the pointer.
Double deletionThe program tries to delete the same allocation more than once.Calling delete again through a dangling pointer.
Cleanup is about ownership: the code that owns memory created with new is responsible for releasing it.

The Basic Cleanup Pattern

Student* student;
student = new FullTimeStudent("Asha", 12000);

student->print();

delete student;       // destroy object and release memory
student = nullptr;    // pointer now points to nothing
  1. Use the dynamically allocated object.
  2. Call delete once when the object is no longer needed.
  3. Assign nullptr if the pointer remains in scope.
delete releases the object, but it does not automatically change the pointer variable. Without the assignment, the pointer still stores the old address.

delete vs delete[]

AllocationCorrect cleanup
p = new int;delete p;
p = new Student(...);delete p;
p = new int[10];delete[] p;
p = new Student[5];delete[] p;
int* marks;
marks = new int[5];

// use marks[0] to marks[4]

delete[] marks;
marks = nullptr;
Match the forms exactly: one object uses delete; an array created with new[] uses delete[].

When Not to Use delete

Only delete memory that your program obtained with new. Normal local variables and fixed arrays are cleaned up automatically.

int mark = 80;
int marks[5];
Student student("Asha");

// Do not delete these.

int* p;
p = &mark;              // points to existing variable

// Do not delete p.
  • Do not delete a stack/local variable.
  • Do not delete a pointer that only borrows another object's address.
  • Do not delete the same allocation through two different pointers.

Understanding nullptr

nullptr is a special value meaning that a pointer deliberately points to no object or memory location.

Student* student;
student = nullptr;

if (student != nullptr) {
    student->print();
}

delete student;        // safe: deleting nullptr does nothing
  • Initialize a pointer to nullptr when it does not yet have an address.
  • Test against nullptr before dereferencing an optional pointer.
  • Reset a pointer to nullptr after deletion when it remains available for later use.

Unsafe and Safe Comparison

// Unsafe
Student* p;
p = new FullTimeStudent("Asha", 12000);
delete p;
p->print();             // error: dangling pointer

// Safer
p = new FullTimeStudent("Asha", 12000);
delete p;
p = nullptr;

if (p != nullptr) {
    p->print();
}
Setting a pointer to nullptr does not release memory by itself. If you write p = nullptr; before delete p;, the original address is lost and the allocation leaks.

Pointer Cleanup Checklist

  1. Was the memory created with new or new[]? If not, do not delete it.
  2. Who owns the allocation and is responsible for cleanup?
  3. Use delete for one object or delete[] for a dynamic array.
  4. Delete it once, after its final use.
  5. Set the surviving pointer to nullptr.
  6. Never dereference a pointer after deletion or while it is nullptr.

Study Guide Chapter Review

Deeper notes

The chapter notes below cover the guide from the beginning, so the exam pointer topics have a foundation: problem solving, C++ basics, control structures, functions, arrays, classes, polymorphism, files, and exceptions.

Chapter 1: Programming Paradigms

Problem solving

Core Ideas

  • A programming paradigm is an approach to solving a problem using a programming language.
  • Imperative programming focuses on how steps execute.
  • Declarative programming focuses on what result is wanted.
  • Structured design divides a problem into smaller subproblems.
  • Object-oriented design identifies objects, their data, and their operations.

C++ Processing Steps

  1. Editor: create and save the source code as a .cpp file.
  2. Preprocessor: handles lines beginning with #.
  3. Compiler: checks syntax and translates source to object code.
  4. Linker: combines object code with library code.
  5. Loader: places the executable in memory.
  6. Execution: the program runs.
A compiler checks syntax. A program can compile and still produce the wrong result.

Back to exam map

Chapter 2: Introduction to C++

Syntax basics

Program Structure

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello" << endl;
    return 0;
}
  • #include <iostream> enables input and output.
  • int main() is the starting point.
  • Braces group statements into a block.
  • return 0; indicates normal program completion.

Tokens, Types, and Operators

ConceptExam meaning
TokenSmallest individual unit of a C++ program.
IdentifierName for variables, functions, classes, and objects.
Data typeSet of values and allowed operations.
Simple typesint, double, char, bool.
Structured typesArrays and classes.
Pointer typesVariables that store addresses.
Use static_cast<double>(total) when integer division would lose the fractional part.

Back to exam map

Chapter 3: Control Structures

Selection and loops

Selection

  • Sequence executes statements in order.
  • Selection chooses a path using if, if...else, or switch.
  • switch works well when one expression is compared with constant case labels.
  • Use break to prevent fall-through between cases.

Repetition

LoopBest use
whileRepeat while a condition remains true; may run zero times.
do-whileRun once first, then test; useful for menus and validation.
forCounter-controlled repetition when the number of repeats is known.
Exam menus usually need a do-while because the menu must display before the choice is tested.

Back to do-while exam section

Chapter 4: Functions

Reusable blocks

Function Basics

  • A function is a named block that performs a task.
  • A value-returning function sends a result back with return.
  • A void function performs an action without returning a value.
  • Formal parameters appear in the function heading.
  • Actual parameters are the values passed during the call.

Exam Function Pattern

double calculateFine(int daysLate)
{
    double fine;

    fine = daysLate * 2.50;
    return fine;
}

void printReceipt(string name, double fine)
{
    cout << "Name: " << name << endl;
    cout << "Fine: R" << fine << endl;
}
calculateFine() returns a value because the main program needs the fine. printReceipt() is void because it prints output.

Back to functions exam section

Chapter 5: Strings, Arrays, and Pointers

Stored collections

Arrays and Strings

  • An array stores multiple values of the same type under one name.
  • Array indexes start at 0.
  • A one-dimensional array is list-like.
  • A two-dimensional array uses rows and columns.
  • Arrays passed to functions behave like address-based data.
Important wording: a normal C++ array does not have built-in methods like marks.length(). In exams, "array methods" usually means functions you write to process arrays.

Pointer Essentials

SyntaxMeaning
int* p;p can store the address of an integer.
p = &x;Store the address of x.
*pDereference: access the value stored at the address.
p + 1Move to the next element of the same type.
Pointer arithmetic moves by element size, not by one raw byte in normal typed pointer use.

Declaring, Filling, and Printing Arrays

The size of a fixed array must be known before it is used. Most exam code passes the array and its size together.

const int SIZE = 5;
int marks[SIZE];

for (int i = 0; i < SIZE; i++) {
    cout << "Enter mark " << i + 1 << ": ";
    cin >> marks[i];
}

for (int i = 0; i < SIZE; i++) {
    cout << marks[i] << " ";
}
The last valid index is SIZE - 1. If SIZE is 5, valid indexes are 0 to 4.

Passing Arrays to Functions

When an array is passed to a function, the function receives access to the original array data. That is why changes inside the function can affect the original elements.

void printArray(const int values[], int size)
{
    for (int i = 0; i < size; i++) {
        cout << values[i] << " ";
    }
}

void increaseAll(int values[], int size)
{
    for (int i = 0; i < size; i++) {
        values[i] = values[i] + 1;
    }
}
Use const when the function should read the array but not change it.

Common Array Functions

Function patternPurposeTypical return
sumArray()Add all elements.int or double
averageArray()Calculate the mean value.double
findMax()Find the largest value.Element value
findMin()Find the smallest value.Element value
linearSearch()Find whether a target exists.Index or -1
countIf()Count values matching a condition.int
sortArray()Arrange values in order.void

Sum, Average, and Maximum

int sumArray(const int values[], int size)
{
    int total = 0;
    for (int i = 0; i < size; i++) {
        total += values[i];
    }
    return total;
}

double averageArray(const int values[], int size)
{
    int total;
    double average;

    total = sumArray(values, size);
    average = total / static_cast<double>(size);

    return average;
}

int findMax(const int values[], int size)
{
    int maxValue = values[0];
    for (int i = 1; i < size; i++) {
        if (values[i] > maxValue) {
            maxValue = values[i];
        }
    }
    return maxValue;
}

Search and Count

int linearSearch(const int values[], int size, int target)
{
    for (int i = 0; i < size; i++) {
        if (values[i] == target) {
            return i;
        }
    }
    return -1;
}

int countPasses(const int marks[], int size)
{
    int count = 0;
    for (int i = 0; i < size; i++) {
        if (marks[i] >= 50) {
            count++;
        }
    }
    return count;
}

-1 is often used to mean "not found" because it is not a valid array index.

Simple Sort Pattern

This nested-loop pattern is easy to remember for exams. It repeatedly swaps values so the smaller values move toward the front.

void sortArray(int values[], int size)
{
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (values[j] < values[i]) {
                int temp = values[i];
                values[i] = values[j];
                values[j] = temp;
            }
        }
    }
}
Sorting changes the original array because the function receives access to the array elements.

Two-Dimensional Arrays

A 2D array is useful for rows and columns, such as candidates and voting stations or students and tests.

const int ROWS = 2;
const int COLS = 3;
int votes[ROWS][COLS] = {
    {12, 8, 15},
    {9, 17, 11}
};

for (int r = 0; r < ROWS; r++) {
    for (int c = 0; c < COLS; c++) {
        cout << votes[r][c] << " ";
    }
    cout << endl;
}

Array Exam Traps

  • Do not use <= size in the loop condition. Use i < size.
  • Initialize totals before adding: int total = 0;.
  • Initialize maximum/minimum from the first element, not from a random number.
  • Pass the number of rows and columns when processing 2D arrays.
  • Use nested loops for 2D arrays: outer loop for rows, inner loop for columns.

Pointer Arithmetic Walkthrough

When an array name is used in many expressions, it behaves like the address of the first element. That means marks, &marks[0], and a pointer assigned with int* p = marks; all lead to the first element.

int marks[5] = {45, 78, 66, 91, 52};
int* p;
p = marks;

for (int i = 0; i < 5; i++) {
    cout << "Address: " << (p + i);
    cout << " Value: " << *(p + i);
}

The loop does not need square brackets. The expression *(p + i) is another way to write marks[i].

Pointer Exam Reasoning

  1. Write down the array values in order.
  2. Place the pointer on the element it currently addresses.
  3. Apply any movement such as p++, p + i, or p += 2.
  4. Only read the value when you see *.
ExpressionChanges pointer?Reads value?
pNoNo, it is the address.
*pNoYes, reads current value.
p + 1NoNo, calculates next address.
*(p + 1)NoYes, reads next value.
p++YesNo, unless combined with *.

Back to pointer exam section

Chapter 6: Classes and Inheritance

OOP foundations

Classes and Objects

  • A class groups data members and member functions into one unit.
  • An object is an instance of a class.
  • private members are accessible only inside the class.
  • public members form the interface used by the rest of the program.
  • Constructors initialize objects when they are created.

Inheritance

class Person {
protected:
    string name;
public:
    Person(string n)
    {
        name = n;
    }
};

class Student : public Person {
private:
    int studentNo;
public:
    Student(string n, int s)
        : Person(n)
    {
        studentNo = s;
    }
};
The derived constructor uses an initializer list to call the base constructor.

Base vs Derived Responsibilities

Class levelResponsibilityStudent example
Base classStores shared features and defines the common interface.Student stores name and declares print().
Derived classAdds specialized data and behavior.FullTimeStudent adds tuition.
ObjectA real instance created from a class.FullTimeStudent s("Asha", 12000);
Think "is a": a full-time student is a student, so inheritance makes sense.

Constructor Order

class PartTimeStudent : public Student {
private:
    int modules;
public:
    PartTimeStudent(string n, int m)
        : Student(n)
    {
        modules = m;
    }
};
  1. The base part, Student(n), is built first.
  2. The derived data member, modules(m), is initialized next.
  3. The constructor body runs last.
If the base class has no default constructor, the derived constructor must call a base constructor in the initializer list.

Back to OOP exam section

Chapter 7: Templates, Virtual Functions, and Overloading

Polymorphism

Virtual Functions

  • A virtual function allows the derived class version to run through a base-class pointer or reference.
  • A pure virtual function is written with = 0.
  • A class with at least one pure virtual function is abstract.
  • Concrete derived classes must override inherited pure virtual functions.

Overloading and Templates

  • Function overloading uses one function name with different parameter lists.
  • Operator overloading gives an operator a class-specific meaning.
  • Templates allow a function or class pattern to work with different data types.
template <class Type>
Type larger(Type x, Type y)
{
    if (x > y) {
        return x;
    } else {
        return y;
    }
}

Overriding Checklist

  • The base function must be marked virtual when runtime polymorphism is needed.
  • The derived function must have the same name, return type, and parameter list.
  • Use override so the compiler warns you if the signature does not match.
  • A pure virtual function must be implemented before a derived class can be used to create objects.
class PartTimeStudent : public Student {
public:
    void print() const override {
        cout << "Part-time: " << name;
    }
};

Overriding vs Overloading

ConceptMeaningExample
OverridingDerived class replaces a base virtual function.void print() const override
OverloadingSame scope uses same name with different parameters.display(int), display(string)
Exam clueLook for inheritance and virtual.Usually overriding.
Changing the parameter list in a derived class can accidentally overload instead of override. That is why override is useful.

Back to OOP exam section

Chapter 8: File I/O and Exception Handling

Runtime data

File I/O

  • Include <fstream> for file streams.
  • Use ifstream to read from files.
  • Use ofstream to write to files.
  • Use fstream when a file needs both input and output modes.
  • Always check that the file opened successfully before processing it.
StreamDirectionThink of it as
ifstreamFile to programLike cin, but from a file.
ofstreamProgram to fileLike cout, but into a file.
fstreamBoth directionsA stream that can read and write.

Exceptions

  • An exception is an undesirable situation detected while the program runs.
  • throw raises an exception.
  • catch handles the exception.
  • Multiple catch blocks can handle different thrown types.
A thrown type must match a compatible catch block. For example, throw 10; is handled by catch (int e).

Opening and Closing Files

You can open a file directly when you create the stream, or create the stream first and call open() later.

// Option 1: open immediately
ifstream inFile("marks.txt");

// Option 2: declare first, open later
ofstream outFile;
outFile.open("results.txt");

if (!outFile) {
    cout << "File could not be opened";
}

outFile.close();
Closing is good practice because it releases the file and makes sure buffered output is written.

File Open Modes

ModePurposeExample
ios::inOpen for reading.fstream file("data.txt", ios::in);
ios::outOpen for writing. Usually replaces existing content.ofstream file("data.txt", ios::out);
ios::appAppend new output at the end of the file.ofstream file("log.txt", ios::app);
ios::binaryOpen as binary data rather than normal text.fstream file("data.bin", ios::binary);
ios::out can overwrite an existing file. Use ios::app when you want to add new lines without deleting old content.

Writing to a File

Writing uses the insertion operator <<, just like cout.

ofstream outFile("receipt.txt");

if (!outFile) {
    cout << "Cannot create file";
} else {
    outFile << "Student: " << name << endl;
    outFile << "Fine: R" << fine << endl;
}

outFile.close();

Appending to a File

Appending is useful for logs, repeated receipts, marks added over time, or audit trails.

ofstream logFile("activity.txt", ios::app);

if (logFile) {
    logFile << "Menu option selected: ";
    logFile << choice << endl;
}

logFile.close();
Append mode places the write position at the end of the file before writing.

Reading Values with >>

Use the extraction operator >> when the file contains simple whitespace-separated values such as marks, totals, names without spaces, or codes.

ifstream inFile("marks.txt");
int mark;
int total = 0;
int count = 0;

while (inFile >> mark) {
    total += mark;
    count++;
}

inFile.close();
This pattern stops automatically when reading fails, usually because the end of the file has been reached.

Reading Whole Lines with getline

Use getline() when the file contains text that may include spaces, such as full names, addresses, or descriptions.

ifstream inFile("students.txt");
string line;

while (getline(inFile, line)) {
    cout << line << endl;
}

inFile.close();
inFile >> name reads only up to whitespace. getline(inFile, name) reads the whole line.

Reading Records

A record is a group of related fields. For example, each student record might have a name, mark, and status.

// File format per line:
// name mark
// Asha 78
// Ben 62

ifstream inFile("students.txt");
string name;
int mark;

while (inFile >> name >> mark) {
    cout << name << " scored " << mark << endl;
}

inFile.close();
This works well when names have no spaces. For full names, use getline() and a clearer file format.

Processing File Data

Most exam questions ask you to read a file and calculate something: total, average, highest, count, or a filtered result.

ifstream inFile("votes.txt");
int vote;
int total = 0;
int highest = 0;

while (inFile >> vote) {
    total += vote;
    if (vote > highest) {
        highest = vote;
    }
}

cout << "Total votes: " << total << endl;
cout << "Highest: " << highest << endl;

Using fstream for Read and Write

fstream is used when the same file stream may need input and output. You specify the modes when opening.

fstream file("data.txt", ios::in | ios::out);

if (!file) {
    cout << "File error";
} else {
    string word;
    file >> word;             // read

    file.clear();             // clear stream state if needed
    file << "\\nUpdated";      // write
}

file.close();
For beginner exam questions, separate ifstream and ofstream code is usually clearer unless the question specifically asks for fstream.

File State Checks

CheckMeaningCommon use
if (!file)The file stream is not usable.Check after opening.
file.is_open()The stream is connected to a file.Confirm open status.
file.eof()End of file has been reached.Less preferred for loop control.
while (file >> value)Read succeeded.Preferred read loop.
Avoid while (!file.eof()) for normal reading. It often processes the last value incorrectly. Prefer while (file >> value).

Combining Files and Exceptions

You can throw an exception when a file cannot be opened, then handle the error in a catch block.

try {
    ifstream inFile("marks.txt");

    if (!inFile) {
        throw string("marks.txt could not open");
    }

    int mark;
    while (inFile >> mark) {
        cout << mark << endl;
    }
}
catch (string message) {
    cout << "File error: " << message;
}

File Handling Exam Traps

  • Forgetting #include <fstream>.
  • Using ifstream when you need to write.
  • Using ofstream when you need to read.
  • Not checking whether the file opened.
  • Using while (!file.eof()) instead of checking whether the read worked.
  • Forgetting that ios::out can replace existing file content.
  • Using >> for full names or sentences that contain spaces.

Back to files exam section Back to exceptions exam section

Quick Review: High-Risk Mistakes

Final pass

Syntax Traps

  • Prototype ends with a semicolon; function definition does not.
  • if, while, and for conditions use parentheses.
  • Array indexes start at 0, so the last index is size - 1.

Concept Traps

  • ifstream reads; ofstream writes.
  • A pure virtual function makes the base class abstract.
  • A void function cannot be assigned to a variable.

Memory Traps

  • Use -> to access members through an object pointer.
  • Delete objects created with new.
  • Do not dereference an uninitialized pointer.

Back to top

If They Ask

Question links

Use these as quick jump links when a question asks for a specific pattern, explanation, or comparison.

Back to top