Skip to content

Computational Thinking | Leaving Cert

Computational thinking is a problem-solving approach that involves breaking down complex problems, Identifying patterns, abstracting details, and designing algorithms. This topic covers Decomposition, pattern recognition, abstraction, algorithm design, finite state machines, and Regular expressions.

Breaking a complex problem into smaller, more manageable sub-problems.

Example (OL): “Build a calculator application.”

Decompose into:

  1. User interface (display, buttons).
  2. Input handling (button presses).
  3. Arithmetic operations (add, subtract, multiply, divide).
  4. Output display.

Each sub-problem can be solved independently and then combined.

Example (HL): “Build a student management system.”

Level 1: Student records, course enrolment, grading, reporting.

Level 2 (under Student records): Add student, edit student, delete student, search student.

Level 3 (under Add student): Validate name, validate ID, check for duplicates, save to database.

Identifying similarities or trends in data or problems.

Example (OL): When processing exam results, notice that the same steps apply to each subject: Read marks, calculate average, assign grade. The pattern can be generalised into a single function That accepts different data.

Example (HL): In a shopping system, the pattern for processing orders is the same regardless of Product type: validate order, check stock, process payment, generate receipt. A single processOrder function handles all product types.

Focusing on the essential features of a problem while ignoring irrelevant details.

Example (OL): When modelling a traffic system, focus on the number of cars, speed, and traffic Lights, while ignoring the colour of the cars or the brand.

Example (HL): In object-oriented programming, a Vehicle class abstracts common properties (wheels, speed, colour) while Car``BicycleAnd Truck inherit and specialise these properties.

Developing a step-by-step solution to the problem.

Worked Example (OL). Design an algorithm to find the highest score in a class.

  1. Initialise highest to 0.
  2. For each student in the class: a. Read the student”s score. B. If score > highest, set highest = score.
  3. Output highest.

Hiding the implementation details of a data structure and exposing only the operations.

class Stack:
def __init__(self):
self._items = []
def push(self, item):
self._items.append(item)
def pop(self):
if not self.is_empty():
return self._items.pop()
raise IndexError("Stack is empty")
def peek(self):
if not self.is_empty():
return self._items[-1]
raise IndexError("Stack is empty")
def is_empty(self):
return len(self._items) == 0
def size(self):
return len(self._items)

The user of the Stack class does not need to know that a list is used internally.

Hiding the details of a procedure behind a well-defined interface.

def calculate_gpa(grades):
grade_points = {"H1": 100, "H2": 88, "H3": 77, "H4": 66, "H5": 56, "H6": 46}
total = sum(grade_points.get(g, 0) for g in grades)
return total / len(grades)
gpa = calculate_gpa(["H1", "H2", "H3", "H1"])
print(f"GPA: {gpa:.1f}")

A finite state machine (FSM) is a model of computation that consists of:

  1. A finite set of states.
  2. A set of input symbols.
  3. A transition function (determines the next state).
  4. An initial state.
  5. A set of accepting (final) states.

A vending machine accepts 50c and 1 euro coins and dispenses a drink costing 1.50 euro.

States: S0S_0 (0c), S1S_1 (50c), S2S_2 (1 euro), S3S_3 (1.50 euro — dispense).

Inputs: 50c, 1 euro.

Current stateInputNext stateOutput
S0S_050cS1S_1
S0S_01 euroS2S_2
S1S_150cS2S_2
S1S_11 euroS3S_3Dispense drink
S2S_250cS3S_3Dispense drink
S2S_21 euroS3S_3Dispense drink, return 50c
50c 50c 50c
S0 -----> S1 -----> S2 -----> S3 (dispense)
| ^
| 1 euro | 1 euro
+--------- S2 ----------------+
1 euro
class VendingMachine:
def __init__(self):
self.state = "S0"
def insert_coin(self, coin):
transitions = {
("S0", "50c"): ("S1", ""),
("S0", "1e"): ("S2", ""),
("S1", "50c"): ("S2", ""),
("S1", "1e"): ("S3", "Dispense drink"),
("S2", "50c"): ("S3", "Dispense drink"),
("S2", "1e"): ("S3", "Dispense drink, return 50c"),
}
key = (self.state, coin)
if key in transitions:
self.state, output = transitions[key]
if output:
print(output)
if self.state == "S3":
self.state = "S0"
else:
print("Invalid input")
machine = VendingMachine()
machine.insert_coin("50c")
machine.insert_coin("50c")
machine.insert_coin("50c")

Worked Example (HL). Design a turnstile FSM. The turnstile is initially locked. Inserting a coin Unlocks it. Pushing it when unlocked lets one person through and locks it again.

States: Locked, Unlocked. Inputs: coin, push.

StateInputNext StateOutput
LockedcoinUnlocked
LockedpushLocked
UnlockedcoinUnlocked
UnlockedpushLockedPerson through

A regular expression (regex) is a pattern used to match strings.

SymbolMeaning
.Any single character
*Zero or more of the preceding element
+One or more of the preceding element
?Zero or one of the preceding element
^Start of string
$End of string
[abc]Any one character in the set
[^abc]Any character NOT in the set
\dAny digit (0-9)
\wAny word character (alphanumeric + underscore)
{n}Exactly n occurrences
{n,m}Between n and m occurrences
import re
pattern = r"[\w.]+@[\w.]+\.\w+"
emails = ["user@example.com", "invalid-email", "john.doe@university.ie"]
for email in emails:
if re.match(pattern, email):
print(f"Valid: {email}")
else:
print(f"Invalid: {email}")
text = "Call 01-2345678 or 021-8765432 for help."
phones = re.findall(r"\d{2,3}-\d{7}", text)
print(f"Phone numbers found: {phones}")
date_pattern = r"^\d{2}/\d{2}/\d{4}$"
dates = ["14/04/2026", "2026-04-14", "1/1/2026"]
for d in dates:
if re.match(date_pattern, d):
print(f"Valid date: {d}")
else:
print(f"Invalid date: {d}")

Worked Example (HL). Write a regex for Irish phone numbers in format (0XX) XXXXXXX.

Pattern: ^\(\d{2}\) \d{6}$

pattern = r"^\(\d{2}\) \d{6}$"
print(re.match(pattern, "(01) 2345678")) # Match
print(re.match(pattern, "01-2345678")) # No match

Break down the problem into sub-problems, then refine each sub-problem further until each step is Simple enough to implement.

Example (HL): Calculate the average of a list of numbers, excluding the highest and lowest.

Level 1:

  1. Read the list of numbers.
  2. Remove the highest and lowest.
  3. Calculate the average of the remaining numbers.
  4. Output the result.

Level 2 (refine step 2): 2.1 Find the maximum value and its index. 2.2 Find the minimum value and Its index. 2.3 Remove both from the list.

Level 3 (implement):

def trimmed_average(numbers):
if len(numbers) < 3:
return None
numbers.remove(max(numbers))
numbers.remove(min(numbers))
return sum(numbers) / len(numbers)
scores = [45, 67, 82, 91, 55, 73, 88]
avg = trimmed_average(scores[:])
print(f"Trimmed average: {avg:.1f}")

A trace table records the values of variables as an algorithm executes.

Example: Trace the following code for n = 5.

def mystery(n):
result = 1
for i in range(1, n + 1):
result = result * i
return result
iresult
11
22
36
424
5120

The function computes n!n! (factorial). For n=5n = 5: 5!=1205! = 120.

Worked Example (HL). Trace the following code for n = 6:

def mystery2(n):
total = 0
for i in range(1, n):
if n % i == 0:
total += i
return total
in % in % i == 0total
10True1
20True3
30True6
42False6
51False6

The function computes the sum of proper divisors of nn. For n=6n = 6: 1+2+3=61 + 2 + 3 = 6. A number Equal to the sum of its proper divisors is called a perfect number.

Backtracking is a systematic trial-and-error approach. If a partial solution leads to a dead end, The algorithm backtracks and tries a different path.

Place nn queens on an n×nn \times n chessboard so that no two queens threaten each other.

def solve_n_queens(n):
def is_safe(board, row, col):
for i in range(col):
if board[i] == row or \
abs(board[i] - row) == abs(i - col):
return False
return True
def solve(board, col):
if col == n:
solutions.append(board[:])
return
for row in range(n):
if is_safe(board, row, col):
board[col] = row
solve(board, col + 1)
board[col] = -1
solutions = []
solve([-1] * n, 0)
return solutions
result = solve_n_queens(4)
print(f"Number of solutions for 4-queens: {len(result)}")

A greedy algorithm makes the locally optimal choice at each step, hoping to find a global optimum.

Make change using the minimum number of coins (with denominations 50c, 20c, 10c, 5c, 2c, 1c).

def make_change(amount, coins=None):
if coins is None:
coins = [50, 20, 10, 5, 2, 1]
result = {}
for coin in coins:
if amount >= coin:
count = amount // coin
result[coin] = count
amount -= count * coin
if amount == 0:
break
return result
change = make_change(87)
print(f"Change: {change}")
## Output: Change: {50: 1, 20: 1, 10: 1, 5: 1, 2: 1}

  • Hardware explains the processor architecture that determines how efficiently algorithms execute.
  • Programming covers the coding techniques used to implement algorithms in practical software systems.
  • Databases applies algorithmic concepts to database indexing, sorting, and query optimisation.