Computer Science Concepts - Learn With Examples https://learnwithexamples.org/category/computer-science-concepts/ Lets Learn things the Easy Way Tue, 04 Aug 2026 17:40:12 +0000 en-US hourly 1 https://wordpress.org/?v=7.0.4 https://i0.wp.com/learnwithexamples.org/wp-content/uploads/2026/07/cropped-learnwithexamples-icon.png?fit=32%2C32&ssl=1 Computer Science Concepts - Learn With Examples https://learnwithexamples.org/category/computer-science-concepts/ 32 32 228207193 Ransomware: How It Works and How It Spreads https://learnwithexamples.org/ransomware-how-it-works-spreads/ https://learnwithexamples.org/ransomware-how-it-works-spreads/#respond Tue, 04 Aug 2026 17:40:10 +0000 https://learnwithexamples.org/?p=848 Learn With Examples It almost never starts with a hacker in a hoodie breaking down a digital door. It starts with one password, one unpatched server, or one convincing email…

The post Ransomware: How It Works and How It Spreads appeared first on Learn With Examples.

]]>

Learn With Examples

It almost never starts with a hacker in a hoodie breaking down a digital door. It starts with one password, one unpatched server, or one convincing email — and by the time the ransom note appears, the attackers have often been inside for weeks.

Reading time13 min
LevelBeginner friendly
Includes5 real case files

Picture a Friday afternoon at a mid-sized logistics company. An accounts clerk gets an email that looks like an unpaid invoice from a supplier she deals with weekly. She opens the attachment. Nothing visibly happens, so she carries on with her day and goes home.

Nineteen days later, at 2:14 on a Sunday morning, every file server in the company locks simultaneously. Spreadsheets, delivery schedules, the customer database, even the backups on the network drive — all replaced with scrambled files and a text document explaining where to send the Bitcoin.

Those nineteen days are the part almost nobody pictures. Ransomware is not a bomb that goes off when you click. It is a burglary that ends with the locks being changed — and the burglary itself takes days or weeks, during which the intruders are quietly reading your email, finding your backups, and stealing your data before a single file is ever encrypted.

This article explains what actually happens in those weeks, how the infection gets in, and what five of the most damaging real-world attacks teach us. Everything here is defensive knowledge: how the attack chain works, so you can recognise and break it.

In one paragraph

What ransomware is

Ransomware is malicious software that makes your data unusable and sells it back to you. It scrambles your files with strong encryption, and the key needed to unscramble them is held by the attacker until you pay. Modern attacks add a second lever: before encrypting anything, they copy your sensitive data out and threaten to publish it. So even a company with perfect backups still faces a leak.

The important part for defenders: encryption is the last step. Everything before it — the break-in, the spread, the theft — is where an attack can still be stopped.

Why it stopped being about locked files

Early ransomware was crude and indiscriminate: infect as many home computers as possible, demand a few hundred dollars each, hope enough people pay. Then criminals noticed that a hospital or a factory will pay vastly more than a thousand individuals, and the whole model changed.

  • 2013 — 2015 · SPRAY AND PRAYMass email campaigns hitting individuals for a few hundred dollars. Good backups were a complete defence.
  • 2016 — 2018 · BIG GAME HUNTINGAttackers begin targeting organisations deliberately, studying revenue and pricing the ransom to fit. Demands jump into six and seven figures.
  • 2019 · DOUBLE EXTORTIONThe pivotal change: steal the data first, then encrypt. Now backups alone don’t save you — refusing to pay means your customers’ data gets published.
  • 2020 onwards · RANSOMWARE AS A SERVICEThe criminal ecosystem specialises. One group writes the software, others break into networks, others negotiate. You no longer need technical skill to run an attack.
  • 2023 onwards · EXTORTION WITHOUT ENCRYPTIONSome groups skip the encryption entirely. Stealing the data and threatening to leak it is quieter, faster, and often just as profitable.

That progression matters because it changes what “being prepared” means. Backups answer the 2015 problem. They do nothing about the 2019 one. A company today needs to survive both losing access to its data and having that data published.

The attack chain, stage by stage

Nearly every serious ransomware incident follows the same six stages. Tap through them — each stage is a place where the attack could still be caught, and knowing that is the entire point of learning the sequence.

The six stages

tap a stage
Stage 1 · Initial access

Getting through the front door

Three routes account for the overwhelming majority of break-ins: a stolen or guessed password on a remote access system without multi-factor authentication, an unpatched internet-facing server, and a convincing email that persuades someone to open something.

Real example: the Colonial Pipeline attackers in 2021 didn’t need anything sophisticated. They used a password for a disused VPN account that appeared in a batch of leaked credentials. That account had no multi-factor authentication on it.

Where it breaks: MFA everywhere, prompt patching of anything reachable from the internet, and shutting down accounts nobody uses.

Stage 2 · Establishing a foothold

Making sure they can get back in

The first thing an intruder does is make their access durable. They set up a second and third way back into the network, so losing one doesn’t lock them out. Increasingly they use ordinary administration tools that already exist on your systems, precisely because those tools don’t look suspicious.

Why it matters: this is why “we changed that password” is not a fix. If someone has been inside for two weeks, the door they came through is rarely the only one still open.

Where it breaks: endpoint monitoring that flags unusual use of legitimate admin tools, and alerting on new accounts or scheduled tasks appearing.

Stage 3 · Reconnaissance

Reading your organisation

Now they explore. Where are the file servers? Which systems would hurt most if they stopped? Where are the backups, and are they connected to the network? They read finance documents to work out what you can afford, and they check whether you have cyber insurance — because that tells them your ceiling.

The uncomfortable statistic: attackers commonly spend days to weeks inside a network before triggering anything. During that window there is nothing to see unless you are looking.

Where it breaks: network segmentation, so that compromising one laptop doesn’t grant a tour of everything, and monitoring for unusual internal scanning.

Stage 4 · Privilege escalation

Becoming the administrator

One ordinary user account isn’t enough to encrypt a company. The goal is domain administrator — the keys to everything. Attackers get there by harvesting credentials left in memory on shared machines, exploiting misconfigured permissions, or simply finding a password in a document called something like server-passwords.xlsx.

Real example: in the 2023 attack on MGM Resorts, attackers reportedly phoned the IT help desk, impersonated an employee, and talked their way into a credential reset. No exploit required — just a convincing phone call.

Where it breaks: least privilege, separate accounts for admin work, and strict identity verification for help-desk resets.

Stage 5 · Data theft

Copying everything out first

Before anything is locked, the valuable data leaves — customer records, contracts, HR files, source code. Often it is uploaded to ordinary cloud storage services, because that traffic blends in with normal business use.

Why this stage exists: it is the insurance policy against your backups. If you restore everything and refuse to pay, they still hold your data and can publish it. This is what turned ransomware from an IT problem into a legal and reputational one.

Where it breaks: monitoring for large outbound transfers, and controls on which cloud services can be reached from inside the network.

Stage 6 · Encryption and extortion

The part everybody pictures

Only now do files get encrypted, usually at night or over a holiday weekend when nobody is watching. Backups are deliberately destroyed or encrypted first. Then the ransom note appears, typically with a countdown and a link to a private chat with the attackers.

The timing is deliberate: attacks cluster around Friday nights, public holidays, and long weekends. Fewer staff, slower detection, more hours to finish the job.

Where it breaks: offline or immutable backups that cannot be reached from the network, and a tested recovery plan — the emphasis being firmly on tested.

Stages 1 to 5 are invisible to most organisations. Stage 6 is impossible to miss. Nearly all the opportunity to prevent a disaster sits in the part nobody sees.

By the time you can see ransomware, you are not preventing an attack any more. You are recovering from one that finished.

How it actually spreads

Two different questions get muddled here: how attackers get into an organisation, and how the infection moves within one. They have different answers.

Getting in: the three doors

Where attacks begin

industry incident reports, broadly consistent year to year
Stolen credentials & exposed remote accesslargest single category
Unpatched internet-facing softwarevery large
Phishing & malicious attachmentsvery large
Supply chain & trusted software updatesrarer, far bigger blast radius

Proportions vary between reports and years, but the ranking is stubbornly stable: credentials and unpatched internet-facing systems consistently beat phishing as the way in.

That last point surprises people, because security training focuses so heavily on suspicious emails. Phishing is a genuine and major route — but a remote access portal with no multi-factor authentication is a permanently open door that requires no one to make a mistake at all. It just needs one password to appear in any of the billions already circulating from previous breaches.

Moving inside: how one machine becomes five hundred

METHOD 01

Stolen credentials

The most common by far. With an administrator password, attackers simply log in to other machines. Nothing is “hacked” — they use the same tools your IT team does.

METHOD 02

Management tools

Software built to push updates to every computer at once is perfect for pushing ransomware to every computer at once. Attackers actively hunt for it.

METHOD 03

Network shares

Mapped drives are a gift. Anything a compromised user can write to, ransomware can encrypt — including that “backup” folder on the shared server.

METHOD 04

Worm-like exploits

Rare but devastating. A flaw needing no human interaction lets the infection jump machine to machine automatically, in minutes rather than days.

The backup trap. A backup drive that is permanently connected and writable is not a backup as far as ransomware is concerned — it is just more files to encrypt. Attackers look for backups deliberately and destroy them first, because a company that can restore is a company that won’t pay. Backups must be offline, immutable, or in a separate account the compromised network cannot reach.

Five real attacks, and what each one teaches

These are the incidents worth knowing, not because they were the most sophisticated, but because each exposed a different weakness.

Case files

tap a case
May 2017 · global

WannaCry — the patch nobody applied

WannaCry spread itself. It used a flaw in an old Windows file-sharing protocol that let it jump from machine to machine with no human involvement, infecting well over 200,000 computers across 150 countries in a couple of days. Britain’s NHS was hit hard: ambulances diverted, thousands of appointments and operations cancelled.

The detail that stings: Microsoft had released the patch two months earlier. Every organisation that applied updates promptly was immune. The outbreak was eventually slowed when a researcher registered a domain name found in the code that acted as an accidental kill switch.

Lesson: patching internet-facing and networked systems is not administrative housekeeping. It is the single highest-return security activity most organisations can do.

June 2017 · global

NotPetya — the one that wasn’t really ransomware

NotPetya arrived through a poisoned update to a Ukrainian tax accounting package that most companies doing business in Ukraine were required to use. It looked like ransomware and demanded payment — but the encryption was designed to be irreversible. Paying achieved nothing. It was destruction dressed as extortion.

Shipping giant Maersk lost virtually its entire global IT estate and rebuilt roughly 4,000 servers and 45,000 computers in about ten days. Total worldwide damages have been estimated at around $10 billion, making it the costliest cyberattack in history.

Lesson: software you trust and update automatically is a route into your network. And a “ransom” is not always a real offer — recovery capability matters more than negotiating ability.

May 2021 · United States

Colonial Pipeline — one password, one country queuing for fuel

The largest fuel pipeline in the US, carrying nearly half the East Coast’s supply, shut down for around six days. Panic buying emptied filling stations across several states and a state of emergency was declared.

The entry point was a single VPN account that was no longer in use, protected by a password that had leaked in an earlier breach elsewhere, with no multi-factor authentication. The company paid roughly $4.4 million in Bitcoin; US authorities later recovered a substantial portion of it. Notably, the pipeline itself was shut down as a precaution — the billing systems were the ones affected.

Lesson: disused accounts are live doors, reused passwords eventually surface in a leak, and MFA on remote access would have ended this attack before it began.

July 2021 · global

Kaseya — attacking a thousand companies at once

Rather than break into companies one by one, attackers compromised a tool that IT service providers use to manage their clients’ computers remotely. The ransomware was then pushed out through that trusted channel to roughly 1,500 downstream businesses in a single stroke — including a Swedish supermarket chain that had to close around 800 stores because its tills stopped working.

The attack landed on the Friday of a long holiday weekend in the US, when response teams were thinnest. That timing was not a coincidence.

Lesson: your security includes your suppliers’ security. Any vendor with remote access into your systems is part of your attack surface.

February 2024 · United States

Change Healthcare — the most expensive lesson in MFA

Change Healthcare processes a large share of American medical claims. When it was hit, pharmacies could not verify insurance, providers could not bill, and parts of the healthcare payment system stalled for weeks. Parent company UnitedHealth ultimately reported costs running into billions of dollars, and the breach affected the data of well over a hundred million people.

The way in was a remote access portal without multi-factor authentication. A $22 million ransom was paid for a promise to delete the stolen data — then the criminal group collapsed in an apparent exit scam, the stolen data moved to a second gang, and a fresh extortion demand followed.

Lesson: paying buys a promise from criminals, not a guarantee. And when one company sits at the centre of an industry, its security is everyone’s problem.

Why it keeps happening: the business behind it

Ransomware persists because it has become an industry with specialised roles, not a hobby for lone hackers.

RoleWhat they doWhy it matters
DevelopersBuild and maintain the software, run the leak site and payment infrastructureThey rent it out and take a cut — they rarely attack anyone directly
AffiliatesDo the actual break-ins using the rented toolkitYou don’t need technical depth to run an attack any more
Access brokersBreak into networks and sell that access to whoever wants itYour network can be sold as a product before anyone encrypts anything
NegotiatorsHandle the victim chat, apply pressure, arrange paymentProfessionalised, scripted, and experienced at reading desperation

This division of labour explains a lot. It explains why attacks are so consistent in method — affiliates follow playbooks. It explains why groups reappear under new names after being disrupted — the people move, the brand changes. And it explains why paying keeps the machine running: every payment funds the next round of development.

Should victims pay?

This is a decision for the organisation with legal counsel and law enforcement, and the honest position is that there is no clean answer. What can be said factually:

  • Paying buys a decryption key that often works imperfectly — restoration from decryptors is typically slow and incomplete.
  • Paying for deletion of stolen data buys a promise. The Change Healthcare case shows what that promise can be worth.
  • Organisations that pay are disproportionately targeted again, sometimes by the same affiliate.
  • In some jurisdictions, payments to sanctioned groups carry legal exposure of their own.
  • Recovery from clean backups is usually faster than decryption — when those backups exist and have been tested.

Which is why the real decision is made long before the ransom note: an organisation that can restore has options, and an organisation that cannot has only one.

Defending against it, in priority order

Tick these off as you read — they’re ordered by how much protection they buy per unit of effort, and the first three stop the majority of real-world attacks outright.

Defence checklist

tap to tick

If it happens anyway. Isolate affected machines from the network but don’t power them off — useful evidence lives in memory. Don’t delete anything. Contact law enforcement and your cyber insurer early, since both may have resources and requirements you need. Assume data was stolen and plan your notifications accordingly. And resist the urge to restore in a rush: rebuilding into a network the attacker still has access to is a well-trodden way to be encrypted twice.

Four myths worth deleting

“We’re too small to be a target.”

Most attacks aren’t targeted at all — they’re opportunistic, driven by automated scanning for exposed systems and weak credentials. Small organisations are attacked constantly, precisely because they’re less likely to have monitoring, and a smaller ransom is still profit for a group running dozens of attacks a month.

“Our antivirus will catch it.”

Antivirus catches known malicious files. It struggles with an attacker who logs in with valid credentials and uses your own administration tools — which is exactly what most modern intrusions look like. Detection has to cover behaviour, not just files.

“We have backups, so we’re fine.”

Backups solve half the problem, and only if they’re offline and tested. They do nothing about stolen data being published, and connected backups are routinely destroyed by the attacker before encryption begins.

“It happens instantly when someone clicks.”

Encryption is the final act, often days or weeks after the initial break-in. That gap is the defender’s real opportunity — every attack that gets detected during it is an attack you never hear about.

Check yourself

Five questions. Open each to check — the correct option is marked.

1. At what point in a ransomware attack does encryption happen?
  • Immediately when someone clicks a link
  • Last, often days or weeks after the initial break-in
  • Before the attacker gets administrator access
  • Only after the ransom is refused

Encryption is stage six of six. Everything before it is invisible to most organisations, and it’s where the attack can still be stopped.

2. What was the entry point in the Colonial Pipeline attack?
  • A zero-day exploit in industrial control systems
  • A malicious email attachment opened by an executive
  • A disused VPN account with a leaked password and no MFA
  • A compromised software update

No sophistication required. One old account, one reused password, no second factor — and half the US East Coast’s fuel supply stopped moving.

3. Why do attackers steal data before encrypting it?
  • Encryption doesn’t work without a copy
  • So that good backups don’t remove the victim’s reason to pay
  • To test whether the files are valuable
  • It makes the encryption faster

Double extortion is the answer to backups. Restore everything and refuse to pay, and they publish your customers’ data instead.

4. Which single control would have prevented the most cases in this article?
  • Antivirus software
  • Employee phishing training
  • Multi-factor authentication on internet-facing access
  • A stronger firewall

Colonial Pipeline and Change Healthcare both came down to remote access without MFA. It’s unglamorous and it’s the highest-value control most organisations can deploy.

5. Why is a permanently connected backup drive a problem?
  • It wears out faster
  • Ransomware encrypts anything writable, and attackers destroy backups first
  • It slows the network down
  • It isn’t a problem

Attackers hunt for backups deliberately, because a company that can restore is a company that won’t pay. Offline or immutable is the requirement.

Frequently asked questions

How does ransomware get onto a computer?

Most commonly through stolen or guessed credentials on remote access systems without multi-factor authentication, through unpatched internet-facing software, or through a convincing email attachment or link. Less often, through a compromised software update from a trusted supplier.

How long does a ransomware attack take?

The encryption itself takes hours. The intrusion leading up to it commonly takes days to weeks, during which attackers explore the network, escalate their access, and copy data out. The visible part is the very end of a long, quiet process.

Can encrypted files be recovered without paying?

Sometimes. Free decryptors exist for older or flawed ransomware families, and projects like No More Ransom collect them. For current strains with correctly implemented encryption, breaking it is not realistic — recovery means restoring from backups.

Does paying the ransom actually work?

Partially, often. Decryption keys usually work but restoration is typically slow and imperfect, and promises to delete stolen data are unverifiable — the Change Healthcare case ended with a second gang demanding a second ransom for the same data. Paying also marks an organisation as willing to pay.

What should a small business do first?

Turn on multi-factor authentication everywhere, especially email and any remote access. Then get backups that are disconnected from the network and actually test restoring from them. Those two steps cost very little and remove the majority of realistic attack paths.

The takeaway

Ransomware is not really a story about encryption. It’s a story about access — how someone gets in, how far they can move once inside, and how long they can stay before anyone notices. The encryption at the end is just the invoice.

Which is why the defences that matter are so unglamorous. Not clever software, but multi-factor authentication on the front door, patches applied on time, backups the attacker can’t reach, and a plan that has been rehearsed before it’s needed. Colonial Pipeline, Change Healthcare, WannaCry — every one of them turns on something a checklist would have caught.

If you take one action after reading this, make it the smallest one: check whether multi-factor authentication is switched on for your email and any remote access you use. That single setting appears in the story of more prevented attacks than anything else in this article.

ransomwarecybersecuritymalwarephishingdata breachMFA

The post Ransomware: How It Works and How It Spreads appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/ransomware-how-it-works-spreads/feed/ 0 848
Big O Notation Explained with Real Examples https://learnwithexamples.org/big-o-notation-explained/ https://learnwithexamples.org/big-o-notation-explained/#respond Tue, 04 Aug 2026 17:20:21 +0000 https://learnwithexamples.org/?p=842 · Learn With Examples · Big O is not maths for its own sake. It is a one-line answer to the only question that matters when your data grows: does…

The post Big O Notation Explained with Real Examples appeared first on Learn With Examples.

]]>

· Learn With Examples ·

Big O is not maths for its own sake. It is a one-line answer to the only question that matters when your data grows: does this code get a little slower, or does it fall off a cliff?

Reading time13 min
LevelBeginner → Practical
Includes3 explorers

Here’s the moment Big O suddenly makes sense to people. I once watched a junior developer ship a feature that checked a list of orders for duplicates. It worked perfectly. Tests passed, code review passed, demo went beautifully. Three months later the same feature took nine minutes to load and brought a support queue to a standstill.

Nothing had changed in the code. The only thing that changed was the number of orders — from about 300 to about 40,000. His approach compared every order against every other order, so the work grew with the square of the list. Going 130 times bigger made it roughly 17,000 times slower.

Big O notation is how you spot that in advance, in about ten seconds, without running anything. It’s a shorthand for describing how the work an algorithm does grows as its input grows — and once you can read it, that nine-minute disaster becomes something you catch during code review instead of during an outage.

The one-paragraph version

What Big O actually says

Big O describes the growth rate of an algorithm’s work as the input size n increases. It deliberately ignores hardware, programming language, and constant factors, because those change by a factor of two or ten — while growth rates change by a factor of millions.

O(1) means the input size doesn’t matter at all. O(log n) means work barely grows. O(n) means it grows in step. O(n²) means doubling the input quadruples the work. That last one is where systems die.

The idea in one picture

Everything else in this article is detail attached to this one shape. Watch what happens to each line as you move right along the horizontal axis — that axis is your data growing over the life of your product.

input size n → operations → O(n log n) O(n²) O(n) O(log n) O(1)
The same five classes, drawn to scale. Notice that near the left edge they’re all bunched together — which is exactly why a slow algorithm looks fine in testing and only reveals itself in production.

That bunching on the left is the trap. With 20 test records, an O(n²) function and an O(n) function both return instantly. There is no observable difference. The difference only exists at scale, which means you cannot discover it by testing on small data — you have to reason about it. That reasoning is what Big O gives you.

The classes, with examples you’ll recognise

There are only six you meet regularly. Tap through them — each one has a plain-English meaning, a real piece of code, and an everyday analogy that makes the growth rate obvious.

Complexity explorer

tap a class

O(1)  Constant time

The work never changes, no matter how big the input gets. Looking up one item takes the same time in a list of ten or ten million.

Real example: fetching a value from a dictionary or hash map, reading array[500], pushing onto a stack, checking whether a number is even.

Analogy: opening a specific page in a book when you already know the page number. The book’s thickness is irrelevant.

O(log n)  Logarithmic time

Each step throws away half the remaining work. Doubling the input adds just one extra step — which is why this class stays fast at absurd scales.

Real example: binary search in a sorted array, finding a record in a balanced tree index, the depth of a database B-tree.

Analogy: the guessing game. “Is it higher or lower?” finds a number between 1 and a million in 20 guesses, because each guess halves the range.

Bars scaled to their own maximum — notice how the growth flattens out almost immediately.

O(n)  Linear time

Work grows in lockstep with the input. Twice the data, twice the time. Perfectly respectable, and often unavoidable — if you must look at every item, you cannot beat linear.

Real example: finding the largest number in an unsorted list, counting words in a document, validating every row of a CSV.

Analogy: reading every name on a guest list to find one person. A list twice as long takes twice as long.

O(n log n)  Linearithmic time

A linear pass repeated across a logarithmic number of levels. Slightly worse than linear, dramatically better than quadratic, and the practical ceiling for comparison-based sorting.

Real example: merge sort, quicksort on average, and every sort() in every standard library you have ever used.

Analogy: splitting a deck of cards in half repeatedly, then merging the piles back in order. You touch every card at each of about ten levels.

O(n²)  Quadratic time

Every item is compared against every other item. Double the input and the work quadruples. This is the class that quietly kills features six months after launch.

Real example: the duplicate-checking loop from the opening story, bubble sort, comparing every pair of records to find matches, a nested loop over the same list.

Analogy: every guest at a party shaking hands with every other guest. Ten guests means 45 handshakes; a hundred guests means 4,950.

O(2ⁿ)  Exponential time

Adding a single item doubles the work. Usable for tiny inputs and hopeless past roughly 40 items — at n = 60 you are past the age of the universe.

Real example: generating every possible subset, naive recursive Fibonacci, brute-forcing a password, solving the travelling salesman by trying every route.

Analogy: the grain-of-rice-on-a-chessboard story. Doubling per square sounds harmless until square 64 needs more rice than exists on Earth.

Why the difference is so violent

Abstract symbols don’t frighten anyone. Actual step counts do. Pick an input size below and look at how the same six classes behave on it.

Steps required, by input size

pick an n
ClassTypical operationSteps for n = 10Time at 1 billion ops/sec
O(1)Look up a key in a hash map1instant
O(log n)Binary search a sorted list3instant
O(n)Scan every item once10instant
O(n log n)A good sort33instant
O(n²)Compare every pair100instant
O(2ⁿ)Try every subset1,0241 microseconds
ClassTypical operationSteps for n = 100Time at 1 billion ops/sec
O(1)Look up a key in a hash map1instant
O(log n)Binary search a sorted list7instant
O(n)Scan every item once100instant
O(n log n)A good sort664instant
O(n²)Compare every pair10,00010 microseconds
O(2ⁿ)Try every subsetbeyond countinglonger than the universe has existed
ClassTypical operationSteps for n = 1,000Time at 1 billion ops/sec
O(1)Look up a key in a hash map1instant
O(log n)Binary search a sorted list10instant
O(n)Scan every item once1,0001 microseconds
O(n log n)A good sort9,96610 microseconds
O(n²)Compare every pair1.0 million1 ms
O(2ⁿ)Try every subsetbeyond countinglonger than the universe has existed
ClassTypical operationSteps for n = 1,000,000Time at 1 billion ops/sec
O(1)Look up a key in a hash map1instant
O(log n)Binary search a sorted list20instant
O(n)Scan every item once1.0 million1 ms
O(n log n)A good sort19.9 million20 ms
O(n²)Compare every pair1 trillion17 minutes
O(2ⁿ)Try every subsetbeyond countinglonger than the universe has existed

Swipe the table sideways on a narrow screen →

One billion operations per second is roughly a fast modern CPU doing simple work. The point is not the exact seconds — it is how violently the bottom two rows change as n grows.

Read across the O(n²) row as you switch sizes. At a thousand items it’s a million steps — a blink. At a million items it’s a trillion steps, and your feature is now a fifteen-minute job that times out. Nothing about the code changed. Only n did.

A faster computer buys you a constant factor. A better algorithm buys you a different curve. Only one of those scales.

The two rules that make Big O readable

Big O deliberately throws information away, and the two things it throws away trip up every beginner. Both rules exist for the same reason: at large n, only the fastest-growing part matters.

Rule 1 — drop the constants

An algorithm that does 3n + 12 operations is written O(n), not O(3n + 12). That looks like cheating, but consider: at a million items, 3n is three million and is a trillion. The constant 3 is noise beside that gap. And constants change anyway — a faster CPU, a better compiler, or a different language shifts them freely, while the shape of the curve does not.

Rule 2 — keep only the dominant term

An algorithm costing n² + 500n + 9000 is O(n²). At n = 10, the 500n part is actually bigger. At n = 10,000, the term is 100 million and the rest is five million — the square has swallowed everything. Big O describes where things end up, not where they start.

The honest caveat. Those discarded constants sometimes matter enormously in practice. An O(n log n) algorithm with a huge constant can lose to an O(n²) one on lists of thirty items — which is exactly why real sorting libraries switch to insertion sort for small chunks. Big O tells you what happens as data grows. It does not promise which is faster today, on your data, at your size.

How to find the Big O of code you’re looking at

You don’t need to count operations. Four patterns cover almost everything you’ll meet in ordinary application code.

What you seeWhat it meansResult
Statements one after anotherCosts add, then the biggest one winsO(a) + O(b) → the larger of the two
A loop over the inputBody runs n timesn × cost of the body
A loop inside a loopCosts multiplyUsually O(n²)
Input halves each stepOnly log₂n steps possibleO(log n)

Two things people get wrong constantly. First, two loops side by side are not quadratic — they’re n + n = 2n, which is linear. Nesting is what multiplies, not adjacency. Second, a loop with a fixed bound isn’t linear: looping 100 times regardless of input is constant work, because 100 doesn’t grow with n.

Five snippets — guess before you open

Read each one, decide, then open it. These are the shapes that actually appear in real code.

1. Checking a list for duplicates the obvious way
for i in range(len(orders)): for j in range(i + 1, len(orders)): if orders[i].id == orders[j].id: flag(orders[i])

O(n²) — a loop inside a loop over the same list. It runs about n²/2 times, and dropping the constant leaves n². This is the exact code from the opening story. The fix is a set: add each id to a set and check membership, turning it into O(n) with O(n) extra memory. Nine minutes becomes a fraction of a second.

2. Two loops, one after the other
for user in users: send_welcome(user) for user in users: log_signup(user)

O(n) — not O(n²). Sequential loops add: n + n = 2n, and the constant 2 gets dropped. Merging them into one loop makes the code twice as fast in wall-clock terms but leaves the complexity class unchanged. That distinction — real speedup, same Big O — is worth internalising.

3. Halving the search space
lo, hi = 0, len(sorted_items) - 1 while lo <= hi: mid = (lo + hi) // 2 if sorted_items[mid] == target: return mid if sorted_items[mid] < target: lo = mid + 1 else: hi = mid - 1

O(log n) — binary search. Each pass discards half of what’s left, so a billion items takes about 30 passes. The catch is the precondition: the list must already be sorted, and sorting costs O(n log n). Sorting once to search many times is a great trade; sorting once to search once is not.

4. A nested loop that isn’t quadratic
for order in orders: # n orders for tax in TAX_BANDS: # always 7 bands apply(order, tax)

O(n) — linear, despite the nesting. The inner loop’s length is fixed at 7 and never grows with the input, so the total is 7n, and the 7 is a constant. Nesting only multiplies complexity when both loops scale with the input. Judge by what grows, not by indentation.

5. The classic recursive trap
def fib(n): if n <= 1: return n return fib(n - 1) + fib(n - 2)

O(2ⁿ) — each call spawns two more, so the call tree roughly doubles at every level. fib(30) makes about 1.6 million calls; fib(50) would run for days. Caching results (memoisation) collapses it to O(n), because each value is then computed exactly once. Same algorithm on paper, a difference of billions in practice.

Best, average and worst case

One algorithm can have three different complexities depending on what you feed it. Searching an unsorted list for a name: if it’s first, you’re done in one step; if it’s last or missing, you check all n.

Best case

The luckiest possible input. Mostly useless for planning — nobody guarantees you luck.

Average case

What typical data does. The most honest number for everyday performance work.

Worst case

The input designed to hurt you. This is what Big O usually reports, and what you should plan around.

The default assumption when someone says “this is O(n log n)” is worst case, unless they say otherwise. That’s deliberately pessimistic, and it’s the right default: your worst case will eventually arrive, and it tends to arrive at the busiest moment. Quicksort is the cautionary tale — O(n log n) on average, O(n²) when the pivots go badly, and the input that triggers it is the very ordinary case of already-sorted data.

Space complexity counts too

Big O describes memory as readily as time. An in-place sort uses O(1) extra space; merge sort needs a scratch array, so O(n). The duplicate-checking fix above trades memory for speed — building a set of every id costs O(n) space to save you from O(n²) time. That trade is almost always worth taking, and recognising when it’s available is most of practical optimisation.

Where this actually matters

Code review

Spotting a nested loop over two growing collections takes seconds and prevents the outage three months later.

Choosing data structures

Array lookup by index is O(1); searching an array is O(n); a hash map turns that search into O(1). Most real speedups are structure changes, not clever code.

Database work

An index converts an O(n) table scan into an O(log n) lookup. That’s the whole reason indexes exist.

Interviews

“What’s the complexity?” is asked in nearly every technical interview, and the follow-up is always “can you do better?”

Don’t over-apply it. If your list has 50 items and always will, an O(n²) loop is completely fine and probably clearer to read. Big O matters when n can grow — and the real skill is asking “how big can this get?” before optimising anything. Most performance work is wasted on code that was never going to be the bottleneck.

Check yourself

Five questions. Open each to check — the correct option is marked.

1. An algorithm takes 4n + 200 steps. What is its Big O?
  • O(4n + 200)
  • O(n)
  • O(200)
  • O(n²)

Drop constants and lower-order terms — only the growth rate survives, and this one grows linearly.

2. Two separate loops over the same list of n items. Total complexity?
  • O(n²)
  • O(n)
  • O(2n²)
  • O(log n)

Sequential loops add — n + n = 2n, which is O(n). Only nested loops multiply.

3. Which class describes binary search on a sorted array?
  • O(1)
  • O(log n)
  • O(n)
  • O(n log n)

Each comparison eliminates half the remaining candidates, so a billion items needs only about 30 steps.

4. Your O(n²) function handles 1,000 records in one second. Roughly how long for 10,000?
  • 10 seconds
  • About 100 seconds
  • 1 second
  • 3 seconds

Ten times the input means a hundred times the work, because the growth is quadratic. This is the calculation to do before shipping, not after.

5. You replace a nested-loop duplicate check with a hash set. What changed?
  • Time O(n²) → O(n), memory unchanged
  • Time O(n²) → O(n), memory O(1) → O(n)
  • Nothing changes
  • Time gets worse, memory improves

You bought a much better time complexity by spending memory on the set. Recognising that trade is most of practical optimisation.

Frequently asked questions

What is Big O notation in simple terms?

It’s a shorthand for how an algorithm’s work grows as its input grows. O(n) means the work grows in step with the data; O(n²) means doubling the data quadruples the work. It describes the shape of that growth, not a measurement in seconds.

Why do we ignore constants in Big O?

Because constants depend on hardware, language and compiler, and they change by small factors — while growth rates change by factors of millions. At a million items the difference between 3n and n is trivial; the difference between n and n² is a trillion steps.

Is O(1) always faster than O(n)?

Not necessarily at small sizes. A constant-time operation with heavy overhead can lose to a linear scan of ten items. Big O describes behaviour as n grows large, so it’s a statement about trends, not a guarantee about any particular input.

What’s a good Big O to aim for?

O(1) and O(log n) are excellent, O(n) is usually fine and often unavoidable, O(n log n) is the practical target for sorting. Treat O(n²) as a warning sign worth investigating, and anything exponential as unusable beyond tiny inputs.

Do I need Big O if I’m not doing interviews?

Yes, though you’ll use it informally. You don’t need the formal maths, but you do need the instinct that says “this loop is inside that loop, and both grow with the data” — that instinct is what stops a working feature becoming an outage once real data arrives.

The takeaway

Big O answers one question: as your data grows, does this code degrade gently or catastrophically? O(1) and O(log n) barely notice. O(n) and O(n log n) scale honestly. O(n²) and worse are fine on toy data and fatal on real data.

You don’t need to derive it formally. You need to look at a function and ask two questions: what grows here, and is anything nested inside anything else that also grows? That habit catches the overwhelming majority of real performance problems, long before they reach production.

Try it on something you wrote this week. Find your longest loop, ask what its input can realistically grow to, and check whether anything scaling sits inside it. That’s the whole practice — and it takes about thirty seconds once the six shapes above are familiar.

big o notationtime complexityalgorithmsspace complexityperformancecomputer science

The post Big O Notation Explained with Real Examples appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/big-o-notation-explained/feed/ 0 842
Sorting Algorithms Compared: Bubble vs Merge vs Quick https://learnwithexamples.org/bubble-vs-merge-vs-quick/ https://learnwithexamples.org/bubble-vs-merge-vs-quick/#respond Tue, 04 Aug 2026 16:14:19 +0000 https://learnwithexamples.org/?p=834 Learn With Examples · Computer Science Three ways to put a list in order. One you’d never ship, one you can trust with anything, and one that beats them both…

The post Sorting Algorithms Compared: Bubble vs Merge vs Quick appeared first on Learn With Examples.

]]>

Learn With Examples · Computer Science

Three ways to put a list in order. One you’d never ship, one you can trust with anything, and one that beats them both — until the day it doesn’t. Here’s how each actually works, watched step by step.

Hand a deck of shuffled cards to three people and watch what they do. The first goes along the row swapping any two neighbours in the wrong order, over and over, until a full pass changes nothing. The second splits the deck in half, sorts each half, then merges the two sorted piles by repeatedly taking the smaller of the two top cards. The third picks one card, throws everything smaller to the left and everything bigger to the right, then repeats on each side.

Those three people just performed bubble sort, merge sort and quicksort. No pseudocode required — the ideas are that physical. What separates them is not cleverness but how many comparisons they need, and that difference explodes as the deck gets bigger.

This article walks through all three step by step, then answers the question that actually matters at work: which one do you reach for, and when does the popular choice betray you?

The 20-second version

Bubble sort compares neighbours and swaps them. Beautifully simple, quadratic time, useless beyond a few hundred items. Learn it, then never ship it.

Merge sort splits, sorts, and merges. Reliably O(n log n) in every case and stable, but needs extra memory the size of your list.

Quicksort partitions around a pivot. Usually the fastest of the three in practice and sorts in place, but a bad pivot can drag it down to quadratic.

Step through all three

Pick an algorithm, then tap through the numbered steps. Cyan marks a comparison, pink a value being moved, amber quicksort’s pivot. All three start from the same six numbers — count how many steps each one needs.

Step through all three

tap the numbers

Step 1 of 15 — 5 was bigger than 1 — swap them

Step 2 of 15 — 5 was bigger than 4 — swap them

Step 3 of 15 — 5 was bigger than 2 — swap them

Step 4 of 15 — 5 and 8 are already in order — leave them

Step 5 of 15 — 8 was bigger than 3 — swap them

Step 6 of 15 — 1 and 4 are already in order — leave them

Step 7 of 15 — 4 was bigger than 2 — swap them

Step 8 of 15 — 4 and 5 are already in order — leave them

Step 9 of 15 — 5 was bigger than 3 — swap them

Step 10 of 15 — 1 and 2 are already in order — leave them

Step 11 of 15 — 2 and 4 are already in order — leave them

Step 12 of 15 — 4 was bigger than 3 — swap them

Step 13 of 15 — 1 and 2 are already in order — leave them

Step 14 of 15 — 2 and 3 are already in order — leave them

Step 15 of 15 — A full pass with no swaps — the list is sorted

Step 1 of 22 — Merging the sorted pieces [1] and [4]

Step 2 of 22 — Smaller front card is 1 — write it into slot 2

Step 3 of 22 — One pile is empty — copy 4 across

Step 4 of 22 — Merging the sorted pieces [5] and [1, 4]

Step 5 of 22 — Smaller front card is 1 — write it into slot 1

Step 6 of 22 — Smaller front card is 4 — write it into slot 2

Step 7 of 22 — One pile is empty — copy 5 across

Step 8 of 22 — Merging the sorted pieces [8] and [3]

Step 9 of 22 — Smaller front card is 3 — write it into slot 5

Step 10 of 22 — One pile is empty — copy 8 across

Step 11 of 22 — Merging the sorted pieces [2] and [3, 8]

Step 12 of 22 — Smaller front card is 2 — write it into slot 4

Step 13 of 22 — One pile is empty — copy 3 across

Step 14 of 22 — One pile is empty — copy 8 across

Step 15 of 22 — Merging the sorted pieces [1, 4, 5] and [2, 3, 8]

Step 16 of 22 — Smaller front card is 1 — write it into slot 1

Step 17 of 22 — Smaller front card is 2 — write it into slot 2

Step 18 of 22 — Smaller front card is 3 — write it into slot 3

Step 19 of 22 — Smaller front card is 4 — write it into slot 4

Step 20 of 22 — Smaller front card is 5 — write it into slot 5

Step 21 of 22 — One pile is empty — copy 8 across

Step 22 of 22 — Every level merged — the list is sorted

Step 1 of 17 — Pivot is 3 — everything smaller goes left of it

Step 2 of 17 — 5 is not smaller than 3 — leave it

Step 3 of 17 — 1 is smaller than 3 — move it left

Step 4 of 17 — 4 is not smaller than 3 — leave it

Step 5 of 17 — 2 is smaller than 3 — move it left

Step 6 of 17 — 8 is not smaller than 3 — leave it

Step 7 of 17 — Pivot 3 drops into its final position, permanently

Step 8 of 17 — Pivot is 2 — everything smaller goes left of it

Step 9 of 17 — Pivot 2 drops into its final position, permanently

Step 10 of 17 — Pivot is 4 — everything smaller goes left of it

Step 11 of 17 — 5 is not smaller than 4 — leave it

Step 12 of 17 — 8 is not smaller than 4 — leave it

Step 13 of 17 — Pivot 4 drops into its final position, permanently

Step 14 of 17 — Pivot is 5 — everything smaller goes left of it

Step 15 of 17 — 8 is not smaller than 5 — leave it

Step 16 of 17 — Pivot 5 drops into its final position, permanently

Step 17 of 17 — Every pivot placed — the list is sorted

Same starting list every time: [5, 1, 4, 2, 8, 3]. Count the steps each algorithm needs — that gap is the whole argument.

Bubble sort: the one everyone learns first

Bubble sort does exactly one thing: walk the list, compare each pair of neighbours, swap them if they’re out of order. Repeat until a full pass produces no swaps at all. Large values “bubble” to the end one position per pass, which is where the name comes from.

Sort [5, 1, 4, 2] by hand:

PassComparisonList after
15 vs 1 → swap[1, 5, 4, 2]
15 vs 4 → swap[1, 4, 5, 2]
15 vs 2 → swap[1, 4, 2, 5]
21 vs 4 → keep[1, 4, 2, 5]
24 vs 2 → swap[1, 2, 4, 5]
3no swaps made[1, 2, 4, 5] ✓
// bubble sort with the early-exit optimisation function bubbleSort(a) { for (let i = 0; i < a.length - 1; i++) { let swapped = false; for (let j = 0; j < a.length - 1 - i; j++) { if (a[j] > a[j + 1]) { [a[j], a[j + 1]] = [a[j + 1], a[j]]; swapped = true; } } if (!swapped) break; // already sorted, stop early } return a; }

The cost is brutal. Every pass compares nearly every pair, and you need close to n passes, so the work grows with . Ten items cost about 45 comparisons. A thousand items cost roughly half a million. Ten thousand cost fifty million. You can feel that in a browser tab.

The one situation where bubble sort isn’t embarrassing is nearly sorted data. With the early-exit check above, an already-sorted list is verified in a single pass of n-1 comparisons — that’s O(n), better than merge sort’s best case. If you have a list where one element occasionally drifts out of place, a bubble pass is a perfectly reasonable repair. That’s a narrow niche, and insertion sort usually fills it better, but it’s real.

Why teach it at all, then? Because it makes the shape of the problem visible. Once you’ve watched bubble sort waste 20 comparisons re-checking pairs it already knows are fine, the motivation behind divide-and-conquer stops being abstract. It’s the algorithm you learn in order to want a better one.

Merge sort: split, sort, stitch

Merge sort refuses to compare distant items at all. Instead it breaks the list in half, again and again, until every piece has a single element — and a single element is sorted by definition. Then it walks back up, merging pairs of sorted pieces.

The merge step is where the magic sits, and it’s the part worth understanding physically. You have two sorted piles face up. Look at the top card of each. Take the smaller one. Repeat. Because both piles are sorted, the smallest remaining card is always one of the two you’re looking at — you never search, you never backtrack.

Merging [1, 4] and [2, 3]:

Left pileRight pileCompareOutput
[1, 4][2, 3]1 vs 2 → take 1[1]
[4][2, 3]4 vs 2 → take 2[1, 2]
[4][3]4 vs 3 → take 3[1, 2, 3]
[4][ ]right empty → copy 4[1, 2, 3, 4]

Count the levels: halving a list of 1,000 takes about 10 splits to reach single items, because 2¹⁰ is 1,024. Each level does roughly n comparison work to merge everything back. Ten levels × a thousand items ≈ 10,000 operations, against bubble sort’s half a million. That’s the entire argument for O(n log n), and it’s why merge sort’s worst case, best case and average case are all the same — the splitting doesn’t care what the data looks like.

Two properties make merge sort the professional’s safe choice:

It’s stable

Equal items keep their original relative order. Sort orders by date, then by customer, and the date order survives inside each customer. Quicksort does not promise this.

It’s predictable

No input can make it slow. For latency-sensitive systems, a guaranteed ceiling beats a lower average with a nasty tail.

It works off-disk

Merging needs only the front of each pile, so you can sort a 500 GB file with 8 GB of RAM. This is how external sorting works.

It costs memory

The standard version needs a scratch array as big as the input. On memory-tight systems, that’s the dealbreaker.

Quicksort: pick a pivot, throw everything to one side

Quicksort also divides and conquers, but it partitions before recursing rather than after. Choose one element as the pivot. Rearrange the list so everything smaller sits left of it and everything larger sits right. The pivot is now in its final position, permanently. Then repeat on the left chunk and the right chunk.

Take [7, 2, 9, 4, 5] with 5 as the pivot. Walk the rest: 7 is bigger (right), 2 is smaller (left), 9 is bigger (right), 4 is smaller (left). You get [2, 4] 5 [7, 9]. One pass, and 5 is done forever. Now solve the two small pieces the same way.

// quicksort, Lomuto partition scheme function quickSort(a, lo = 0, hi = a.length - 1) { if (lo >= hi) return a; const pivot = a[hi]; let i = lo; for (let j = lo; j < hi; j++) { if (a[j] < pivot) { [a[i], a[j]] = [a[j], a[i]]; i++; } } [a[i], a[hi]] = [a[hi], a[i]]; // pivot into place quickSort(a, lo, i - 1); quickSort(a, i + 1, hi); return a; }

On average the pivot lands somewhere near the middle, the problem halves each time, and you get O(n log n) — typically with a smaller constant factor than merge sort, because quicksort swaps within the original array instead of copying into a scratch one. Fewer memory writes, better cache behaviour, faster in the real world.

The trap. Suppose you always pick the last element as pivot, and the list is already sorted. Every pivot is the largest remaining value, so one side gets everything and the other gets nothing. You’ve turned O(n log n) into O(n²) — and the input that triggers it is the most common input in the world: data that’s already in order. Real implementations dodge this by picking a random pivot, or the median of the first, middle and last elements.

Race them on the same data

Same shuffled array, three algorithms, one operation per tick. This isn’t a wall-clock benchmark — it’s an operations count, which is what the big-O notation is actually measuring.

Head-to-head race

tick to start
Bubble ~1,800 comparisons
Merge ~300 comparisons
Quick ~250 comparisons

Each bar advances at a speed proportional to the work its algorithm really does on a shuffled 60-item list. Quick and merge finish while bubble sort is still grinding through its second pass.

On random data quicksort usually finishes first, merge close behind, bubble far back. Hand all three an already-sorted list, though, and the ranking inverts completely: bubble sort exits after a single clean pass, while quicksort with a naive last-element pivot collapses into its worst case. Same three algorithms, opposite result, purely because the input changed shape.

Big-O tells you how an algorithm behaves as data grows. It doesn’t tell you which one wins on your data. Only the shape of your input decides that.

The numbers, side by side

BubbleMergeQuick
Best caseO(n)O(n log n)O(n log n)
AverageO(n²)O(n log n)O(n log n)
Worst caseO(n²)O(n log n)O(n²)
Extra memoryO(1)O(n)O(log n)
Stable?YesYesNo
In place?YesNoYes
Use it whenTeaching, or tiny nearly-sorted listsYou need guarantees, stability, or external sortingYou want raw speed on in-memory data

Those symbols get abstract fast, so put real numbers on them. Pick a list size and watch the gap between quadratic and logarithmic growth open up.

How bad does n² get?

pick a list size
45Bubble ops (n²)
33Merge / quick ops
1.4×Times more work
instantBubble time est.
5,000Bubble ops (n²)
664Merge / quick ops
7.5×Times more work
under 1 msBubble time est.
500,000Bubble ops (n²)
9,966Merge / quick ops
50×Times more work
5 msBubble time est.
5 billionBubble ops (n²)
1,660,964Merge / quick ops
3,010×Times more work
50 secondsBubble time est.

Notice how the ratio behaves. At 100 items bubble sort does around 15 times more work — annoying, survivable. At 100,000 items it does over 3,000 times more. That is the difference between a page that renders instantly and one that hangs the browser for a minute. Complexity classes don’t matter much at small scale and matter enormously at large scale, which is exactly why beginners under-rate them.

So which one do you actually use?

Honest answer for day-to-day work: call your language’s built-in sort. It’s been tuned by people who do nothing else. But knowing what’s under it tells you when to override it.

Sorting objects by two fields

You need stability — merge sort, or a built-in that guarantees it. Sort by the secondary key first, then the primary.

Big array, memory is tight

Quicksort. In-place, cache-friendly, only recursion stack overhead. Just randomise the pivot.

Data bigger than RAM

Merge sort. It’s the only one of the three that sorts chunks on disk and merges streams.

Worst case must be bounded

Merge sort. Real-time and latency-critical systems care about the ceiling, not the average.

Fewer than ~20 items

Use insertion sort. Its overhead is so low it beats the clever algorithms at small sizes — which is why real implementations switch to it below a threshold.

Explaining sorting to someone

Bubble sort, once. Then show them the step-through above and let the operation count make the argument.

What most standard libraries actually run is a hybrid. Introsort starts as quicksort, counts its recursion depth, and switches to heapsort if the pivots are going badly — giving quicksort’s speed with a guaranteed O(n log n) ceiling. Timsort, used in Python and Java for objects, is merge sort that detects runs of already-ordered data and exploits them, which makes it startlingly fast on the semi-sorted data that real applications produce. Both are engineering answers to the trade-offs on this page.

Check yourself

Five questions. Open each one to check yourself — the correct option is marked.

1. Why is merge sort’s worst case the same as its best case?
  • It checks the data first
  • The splitting is fixed and doesn’t depend on the values
  • It uses extra memory
  • It’s stable

Merge sort always halves the list, whatever’s in it. The number of levels is fixed at log₂n, so no input can make it slow.

2. Which input makes naive quicksort hit its worst case?
  • Random data
  • Data with many duplicates only
  • Already-sorted data with a last-element pivot
  • Very short lists

Every pivot becomes the largest remaining value, so one partition gets everything. The fix is a random or median-of-three pivot.

3. What does “stable” mean for a sorting algorithm?
  • Equal items keep their original relative order
  • It never crashes
  • It always takes the same time
  • It uses no extra memory

Stability lets you sort by one field, then another, and keep the first ordering inside groups. Quicksort doesn’t guarantee it.

4. Roughly how many comparisons does bubble sort need for 1,000 items?
  • About 1,000
  • About 10,000
  • About 500,000
  • About 1,000,000

Roughly n²/2 — half a million. Merge sort handles the same list in about 10,000.

5. You must sort a 400 GB log file on a machine with 16 GB of RAM. Which approach?
  • Quicksort, it’s in place
  • Merge sort, sorting chunks then merging streams
  • Bubble sort, it uses no extra memory
  • None can do it

External merge sort only needs the front of each sorted run in memory at a time — the classic solution to sorting more data than you can hold.

Frequently asked questions

Which sorting algorithm is fastest?

For general in-memory data, quicksort is usually fastest in practice because it sorts in place with excellent cache behaviour. Merge sort is faster in the worst case, since quicksort can degrade to O(n²) with bad pivots. Bubble sort is slowest by a wide margin at any meaningful size.

Is bubble sort ever useful in real code?

Rarely, but not never. On a list that’s already nearly sorted, the early-exit version runs in O(n) and is trivial to write and verify. For anything else, insertion sort does the same job better and your language’s built-in sort beats both.

Why does quicksort beat merge sort if merge sort has a better worst case?

Constant factors. Quicksort swaps elements inside the original array; merge sort copies into a scratch array and back on every level. Fewer memory writes and better cache locality mean quicksort typically wins on wall-clock time even when both are O(n log n).

What does O(n log n) actually mean in plain terms?

The work grows a little faster than the list size, but nowhere near as fast as squaring it. Double the items and you do slightly more than double the work — whereas O(n²) means doubling the items quadruples the work. That gap is what makes large-scale sorting feasible at all.

Which sort do Python, Java and JavaScript use?

Python uses Timsort, a merge sort variant that detects existing sorted runs. Java uses Timsort for objects and a dual-pivot quicksort for primitives. Most JavaScript engines use Timsort-style stable sorts for Array.sort(). All three are hybrids built from the ideas on this page.

The takeaway

Bubble sort compares neighbours and pays for it quadratically. Merge sort splits the problem into halves whose cost adds up to n log n, every single time, at the price of extra memory. Quicksort partitions around a pivot and is usually the fastest of the three — as long as the pivot is chosen sensibly.

The deeper lesson isn’t which one wins. It’s that the same task can be organised in ways whose costs diverge by a factor of thousands, and that the winning approach depends on the shape of your data, not on the elegance of the code. Step back through the three walkthroughs above and count what each one needed on the very same six numbers — then imagine those gaps at a million items. That is the thing worth remembering.

bubble sortmerge sortquicksortbig-Oalgorithmsdata structures

The post Sorting Algorithms Compared: Bubble vs Merge vs Quick appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/bubble-vs-merge-vs-quick/feed/ 0 834
AVL Trees Explained — Self-Balancing Trees That Never Get Lopsided https://learnwithexamples.org/avl-trees-explained/ https://learnwithexamples.org/avl-trees-explained/#respond Thu, 16 Jul 2026 11:34:28 +0000 https://learnwithexamples.org/?p=758 AVL Trees Explained — Self-Balancing Trees That Never Get Lopsided Data Structures · Algorithms Learn With Examples A regular binary search tree can degrade into a linked list — making…

The post AVL Trees Explained — Self-Balancing Trees That Never Get Lopsided appeared first on Learn With Examples.

]]>
AVL Trees Explained — Self-Balancing Trees That Never Get Lopsided

Data Structures · Algorithms

Learn With Examples

A regular binary search tree can degrade into a linked list — making every search O(n) instead of O(log n). AVL trees fix this with automatic rotations that keep the tree perfectly balanced after every insert and delete. This guide walks you through the balance factor, all four rotation types, and lets you build and balance an AVL tree interactively in real time.

📖 20 min read 🌲 Live AVL tree builder 🔄 Rotation visualiser 💻 Python code ❓ Quiz included

Section 01

The Problem with Unbalanced BSTs

A Binary Search Tree (BST) is a beautiful data structure: every node’s left child is smaller, every right child is larger. Searching is O(log n) — you eliminate half the tree at each step. In theory.

In practice, the order you insert values determines the shape of the tree. Insert values in sorted order — 1, 2, 3, 4, 5 — and you get this:

That is not a tree — it is a linked list. Searching for 5 now requires visiting every single node. Your O(log n) search has degraded to O(n). The whole advantage of a BST is gone.

O(log n)
Balanced BST search — halves remaining nodes each step
O(n)
Degenerate BST search — visits every node in the worst case
1962
Year AVL trees were invented by Adelson-Velsky and Landis
±1
Maximum allowed balance factor in any AVL node

⚠️ The Real-World Risk

Any application that inserts data in sorted or near-sorted order — timestamps, auto-incrementing IDs, alphabetical names — is at risk of BST degeneration. This is why production databases and language runtimes use self-balancing trees, not plain BSTs.

Section 02

What is an AVL Tree?

An AVL tree (named after its inventors Adelson-Velsky and Landis) is a self-balancing binary search tree. It maintains all the BST properties — left child smaller, right child larger — but adds one additional constraint:

|height(left subtree) − height(right subtree)| ≤ 1
For every node in the tree — at every level

After every insertion or deletion, the AVL tree checks this constraint at every affected node. If any node becomes unbalanced, it performs one or two rotations — local restructuring operations — to restore balance. The tree never needs to be rebuilt from scratch.

📏

Height of a Node

The number of edges on the longest path from that node to a leaf. A leaf node has height 0. A null pointer has height −1.

⚖️

Balance Factor

BF = height(left) − height(right). In a valid AVL tree, every node’s BF is −1, 0, or +1. Any other value triggers a rotation.

🔄

Rotations

Local restructuring of 2–3 nodes that restores balance without violating the BST property. There are four types: LL, RR, LR, RL.

📐

Guaranteed Height

An AVL tree with n nodes has height at most 1.44 × log₂(n). A degenerate BST has height n. This gap is enormous for large n.

Section 03

The Balance Factor — The Key Metric

Every node in an AVL tree stores a balance factor (BF) — the difference between the height of its left subtree and the height of its right subtree.

Balance FactorMeaningAVL Valid?Action
BF = −2Right subtree is 2 tallerInvalidRotate left (RR or RL)
BF = −1Right subtree is 1 tallerValidNo action needed
BF = 0Both subtrees equal heightValid — perfectNo action needed
BF = +1Left subtree is 1 tallerValidNo action needed
BF = +2Left subtree is 2 tallerInvalidRotate right (LL or LR)

📌 Stored or Computed?

In most implementations the balance factor (or simply the height) is stored at each node and updated during insertions and deletions. This makes it O(1) to check — no need to traverse the subtree to measure height every time.

Python
class AVLNode:
    def __init__(self, key):
        self.key    = key
        self.left   = None
        self.right  = None
        self.height = 1   # new node starts at height 1

def get_height(node):
    return node.height if node else 0

def get_balance(node):
    return get_height(node.left) - get_height(node.right) if node else 0

def update_height(node):
    node.height = 1 + max(get_height(node.left), get_height(node.right))

Section 04

The Four Rotations — How AVL Rebalances

When the balance factor at a node becomes ±2, the AVL tree performs a rotation. There are four types, each handling a different pattern of imbalance. They all share one beautiful property: they restore balance while preserving the BST ordering property.

CaseWhen it happensBF of unbalanced nodeBF of childFix
LL (Left-Left)Insertion in left subtree of left child+2≥ 0Single right rotation
RR (Right-Right)Insertion in right subtree of right child−2≤ 0Single left rotation
LR (Left-Right)Insertion in right subtree of left child+2< 0Left rotate child, then right rotate node
RL (Right-Left)Insertion in left subtree of right child−2> 0Right rotate child, then left rotate node

Right Rotation (LL case)

The unbalanced node z has BF = +2. Its left child y is taller. We rotate right around z — y takes z’s position, z becomes y’s right child.

Python — Right Rotation
def rotate_right(z):
    y = z.left          # y is the left child of z
    T3 = y.right        # T3 is the right subtree of y

    # Perform rotation
    y.right = z         # z becomes the right child of y
    z.left  = T3        # T3 moves to z's left (BST property preserved)

    # Update heights (z first, then y since y is now higher)
    update_height(z)
    update_height(y)

    return y             # y is the new root of this subtree

Left Rotation (RR case)

Python — Left Rotation
def rotate_left(z):
    y = z.right         # y is the right child of z
    T2 = y.left         # T2 is the left subtree of y

    # Perform rotation
    y.left  = z         # z becomes the left child of y
    z.right = T2        # T2 moves to z's right

    update_height(z)
    update_height(y)

    return y             # y is the new root

🔄 Double Rotations

LR and RL cases require two rotations. For LR: first rotate the left child leftward (turning LR into LL), then rotate the unbalanced node rightward. For RL: first rotate the right child rightward (turning RL into RR), then rotate the unbalanced node leftward. After a double rotation the tree is always balanced.

Section 05

Interactive Rotation Visualiser

Click any rotation to see the before and after states side by side. Notice how the BST ordering property is preserved — an in-order traversal of both trees gives the same sequence.

  Rotation Visualiser
Before (Unbalanced)
After (Balanced)
Click a rotation type above to see how it works.

Section 06

Live AVL Tree Builder

Insert and delete values to build your own AVL tree. The tree rebalances automatically after every operation. Balance factors are shown inside each node — green means balanced, red means a rotation just happened.

  AVL Tree Builder
Insert a value to begin building the AVL tree.

Section 07

Insertion Algorithm — Step by Step

AVL insertion combines standard BST insertion with a recursive rebalancing pass back up to the root.

1

Insert like a normal BST

Recursively traverse the tree comparing the new key with each node. Go left if smaller, right if larger. Insert at the correct null position.

2

Update heights on the way back up

As the recursion unwinds, update the height of each ancestor node: height = 1 + max(height(left), height(right)).

3

Check balance factor at each ancestor

Compute BF = height(left) − height(right) at each node. If |BF| ≤ 1, the node is balanced. Continue up the tree.

4

Identify the case and rotate if BF = ±2

Determine which of the four cases applies (LL, RR, LR, RL) by checking the balance factor of the child. Perform the appropriate single or double rotation.

5

Continue checking upward

After a rotation, continue checking balance factors up to the root. In practice at most one rotation is needed per insertion (but deletion may need O(log n) rotations).

Python — Full AVL Insert
def insert(node, key):
    # Step 1: Standard BST insertion
    if not node:
        return AVLNode(key)

    if key < node.key:
        node.left  = insert(node.left,  key)
    elif key > node.key:
        node.right = insert(node.right, key)
    else:
        return node  # duplicate key — ignore

    # Step 2: Update height
    update_height(node)

    # Step 3: Get balance factor
    bf = get_balance(node)

    # Step 4: Determine case and rotate
    # LL Case
    if bf > 1 and key < node.left.key:
        return rotate_right(node)

    # RR Case
    if bf < -1 and key > node.right.key:
        return rotate_left(node)

    # LR Case
    if bf > 1 and key > node.left.key:
        node.left = rotate_left(node.left)
        return rotate_right(node)

    # RL Case
    if bf < -1 and key < node.right.key:
        node.right = rotate_right(node.right)
        return rotate_left(node)

    return node  # node is balanced — no rotation needed

Section 08

Time and Space Complexity

OperationAverageWorst CaseWhy
SearchO(log n)O(log n)Height is always ≤ 1.44 log₂(n) — guaranteed
InsertO(log n)O(log n)BST insert + at most 2 rotations + height updates up to root
DeleteO(log n)O(log n)BST delete + up to O(log n) rotations on the path back to root
SpaceO(n)O(n)One node per element; each stores key, left, right, height

✅ The Guarantee that Matters

The key advantage of AVL trees over plain BSTs is that all operations are O(log n) in the worst case — guaranteed, regardless of insertion order. A plain BST can degrade to O(n). The cost is slightly higher constant factors due to rotation bookkeeping.

Section 09

AVL vs Red-Black Trees vs Plain BST

PropertyPlain BSTAVL TreeRed-Black Tree
Search worst caseO(n)O(log n)O(log n)
Insert worst caseO(n)O(log n)O(log n)
Rotations per insert0At most 2At most 2
Rotations per delete0O(log n)At most 3
Balance strictnessNoneStrict (BF ≤ 1)Loose (height ≤ 2 log n)
Search speedVariesFaster (shorter tree)Slightly slower
Insert/Delete speedVariesSlightly slowerFaster
Best use caseStatic dataRead-heavy workloadsWrite-heavy workloads
Used inSimple lookupsDatabases, compilersLinux kernel, Java TreeMap

💡 Which to Choose?

Choose AVL trees when your workload is read-heavy — the stricter balance means a shorter tree and faster lookups. Choose Red-Black trees when your workload is write-heavy — fewer rotations on deletion makes writes cheaper. In practice, most language standard libraries use Red-Black trees (Java’s TreeMap, C++ std::map) for their balanced write performance.

Section 10

Real-World Uses of AVL Trees

ApplicationHow AVL trees help
Database indexesMany database engines use AVL or similar balanced trees for in-memory indexes where read performance is critical. Guaranteed O(log n) lookup regardless of data insertion order.
Compilers (symbol tables)Compilers store identifiers (variable names, functions) in AVL trees for fast O(log n) lookup during compilation. GCC historically used AVL trees.
Memory allocatorsSome memory allocators track free memory blocks in AVL trees, enabling fast O(log n) search for a block of the right size.
Geometry / computational geometrySweep-line algorithms for intersections, polygon clipping, and spatial queries use balanced BSTs (including AVL) to maintain the event queue.
Network routing tablesFast prefix lookups for IP routing can be implemented with AVL trees in software routers, giving guaranteed lookup time per packet.
Python’s sortedcontainers libraryThe SortedList, SortedDict, and SortedSet in Python’s popular sortedcontainers library use B-tree variants inspired by AVL balancing.

Section 11

Complete Python Implementation

Python — Complete AVL Tree
class AVLNode:
    def __init__(self, key):
        self.key = key; self.left = self.right = None; self.height = 1

class AVLTree:
    def _h(self, n): return n.height if n else 0
    def _bf(self, n): return self._h(n.left) - self._h(n.right) if n else 0
    def _upd(self, n): n.height = 1 + max(self._h(n.left), self._h(n.right))

    def _rr(self, z):   # right rotation
        y = z.left; z.left = y.right; y.right = z
        self._upd(z); self._upd(y); return y

    def _lr(self, z):   # left rotation
        y = z.right; z.right = y.left; y.left = z
        self._upd(z); self._upd(y); return y

    def _balance(self, node, key):
        self._upd(node)
        bf = self._bf(node)
        if bf > 1:
            if key > node.left.key: node.left = self._lr(node.left)  # LR
            return self._rr(node)                                       # LL
        if bf < -1:
            if key < node.right.key: node.right = self._rr(node.right) # RL
            return self._lr(node)                                       # RR
        return node

    def insert(self, root, key):
        if not root: return AVLNode(key)
        if   key < root.key: root.left  = self.insert(root.left,  key)
        elif key > root.key: root.right = self.insert(root.right, key)
        else: return root
        return self._balance(root, key)

    def _min_node(self, n):
        while n.left: n = n.left
        return n

    def delete(self, root, key):
        if not root: return root
        if   key < root.key: root.left  = self.delete(root.left,  key)
        elif key > root.key: root.right = self.delete(root.right, key)
        else:
            if not root.left:  return root.right
            if not root.right: return root.left
            temp = self._min_node(root.right)
            root.key = temp.key
            root.right = self.delete(root.right, temp.key)
        return self._balance(root, root.key)

    def inorder(self, root):
        return (self.inorder(root.left) + [root.key] + self.inorder(root.right)) if root else []

# Usage
tree = AVLTree()
root = None
for v in [10, 20, 30, 40, 50, 25]:   # sorted order — would break plain BST
    root = tree.insert(root, v)

print("Inorder:", tree.inorder(root))    # → [10, 20, 25, 30, 40, 50]
print("Height:", root.height)             # → 3 (not 6 as it would be unbalanced)

root = tree.delete(root, 20)
print("After delete:", tree.inorder(root)) # → [10, 25, 30, 40, 50]

Section 12

Knowledge Quiz

Six questions to test your AVL tree understanding.

  AVL Trees Quiz
Question 1 of 6

The post AVL Trees Explained — Self-Balancing Trees That Never Get Lopsided appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/avl-trees-explained/feed/ 0 758
How Binary Search Works — with Visual Examples https://learnwithexamples.org/how-binary-search-works-visual-examples/ https://learnwithexamples.org/how-binary-search-works-visual-examples/#respond Wed, 15 Jul 2026 16:10:39 +0000 https://learnwithexamples.org/?p=736 How Binary Search Works — with Visual Examples Algorithms · Beginner Binary search is one of the most elegant algorithms ever written. It finds any value in a sorted list…

The post How Binary Search Works — with Visual Examples appeared first on Learn With Examples.

]]>
How Binary Search Works — with Visual Examples

Algorithms · Beginner

Binary search is one of the most elegant algorithms ever written. It finds any value in a sorted list of a million items in just 20 steps. This guide shows you exactly how — with live visualizations, code in 3 languages, and a speed comparison you can feel.

📖 15 min read 🎮 3 interactive demos 💻 Python · JS · Java ❓ Quiz at the end

The Problem Binary Search Solves

Imagine you have a sorted list of 1,000,000 numbers and you need to find whether the number 742,891 is in it. The naive approach — checking every single element one by one — would take up to a million comparisons in the worst case.

Binary search solves this in at most 20 comparisons. Not 20,000. Not 2,000. Twenty. That’s the power of dividing the problem in half at every step.

Linear Search 1,000,000 steps
Binary Search 20 steps

📌 One Requirement

Binary search only works on a sorted array. This is the most important rule. We’ll revisit why at the end.

The Phone Book Intuition

You’ve already used binary search in real life. When you look up a name in a phone book (or a word in a dictionary), you don’t start from page 1 and flip forward.

You open to the middle. If the name you want comes alphabetically before the middle page, you throw away the right half and repeat on the left. If it comes after, you throw away the left half and repeat on the right. Each time, you eliminate half the remaining possibilities.

💡 Key Insight

Every comparison eliminates half of the remaining elements. That’s why 1,000,000 items only needs log₂(1,000,000) ≈ 20 steps. The algorithm grows with the logarithm of the input, not the input itself.

How It Works — Step by Step

Binary search uses three pointers on the array: low, mid, and high. Here’s the algorithm:

1

Set low = 0, high = last index

Start with the full array in scope. Low points to the first element, high points to the last.

2

Calculate mid = (low + high) / 2

Find the middle index. In Python/Java use integer division: (low + high) // 2 to avoid float issues.

3

Compare array[mid] with target

Three possible outcomes: equal (found it! return mid), target is smaller (search left half: high = mid − 1), target is larger (search right half: low = mid + 1).

4

Repeat until found or low > high

If low exceeds high, the target is not in the array — return −1 (or null/None depending on your language).

⚠️ Integer Overflow Tip

In languages like Java/C++, computing (low + high) / 2 can overflow for very large arrays. The safe formula is: low + (high - low) / 2.

Live Visualizer — Try It Yourself

Enter any number between 1 and 99 and watch binary search find it step by step. The yellow cell is the current midpoint being checked, grey cells are eliminated, and green means found.

  Binary Search Visualizer
Press ▶ Search to start, or Step → to go one step at a time.

Code in Python, JavaScript & Java

Python

Python
def binary_search(arr, target):
    low, high = 0, len(arr) - 1

    while low <= high:
        mid = low + (high - low) // 2  # safe from integer overflow

        if arr[mid] == target:
            return mid              # found! return the index
        elif arr[mid] < target:
            low = mid + 1           # target is in right half
        else:
            high = mid - 1          # target is in left half

    return -1                       # not found


# Example usage
numbers = [2, 7, 13, 19, 25, 34, 37, 46, 58, 72]
result = binary_search(numbers, 37)
print(f"Found at index: {result}")  # → Found at index: 6
print(binary_search(numbers, 99))   # → -1 (not found)

JavaScript

JavaScript
function binarySearch(arr, target) {
  let low = 0;
  let high = arr.length - 1;

  while (low <= high) {
    const mid = low + Math.floor((high - low) / 2);

    if (arr[mid] === target)  return mid;      // found
    if (arr[mid] < target)   low = mid + 1;   // search right
    else                       high = mid - 1;  // search left
  }

  return -1; // not found
}

// Example
const nums = [2, 7, 13, 19, 25, 34, 37, 46, 58, 72];
console.log(binarySearch(nums, 37));  // 6
console.log(binarySearch(nums, 99));  // -1

Java

Java
public class BinarySearch {

    public static int binarySearch(int[] arr, int target) {
        int low = 0;
        int high = arr.length - 1;

        while (low <= high) {
            int mid = low + (high - low) / 2; // avoids overflow

            if (arr[mid] == target)  return mid;
            if (arr[mid] <  target)  low  = mid + 1;
            else                       high = mid - 1;
        }

        return -1; // not found
    }

    public static void main(String[] args) {
        int[] nums = {2, 7, 13, 19, 25, 34, 37, 46, 58, 72};
        System.out.println(binarySearch(nums, 37)); // 6
        System.out.println(binarySearch(nums, 99)); // -1
    }
}

Time & Space Complexity

Binary search is one of the most efficient search algorithms. Its performance is measured using Big O notation.

Case Time Complexity What it means
Best Case O(1) Target is at the midpoint on the first check
Average Case O(log n) Halves the search space each iteration
Worst Case O(log n) Target not found after exhausting all halves
Space (iterative) O(1) Only stores low, mid, high — no extra memory
Space (recursive) O(log n) Call stack grows with each recursive call

📐 The log n explained

log₂(n) is the number of times you can halve n before reaching 1. For n = 1,024 → log₂(1024) = 10 steps. For n = 1,048,576 (1 million) → just 20 steps. Doubling the input only adds one more step.

Binary vs Linear Search — The Speed Race

Drag the slider to set the array size and watch how many steps each algorithm needs. The difference becomes dramatic very quickly.

  Speed Comparison Demo
1,000
Linear
500 steps
Binary
10 steps

* Linear shows average-case (n/2). Binary shows worst-case (log₂n). Both searching the same array.

The One Rule You Must Not Break

Binary search requires a sorted array. This is non-negotiable. Here’s why: when the algorithm looks at the midpoint and finds the target is smaller, it assumes everything to the right is also larger. In an unsorted array, that assumption is false — and the algorithm silently gives wrong answers.

Python — What goes wrong with unsorted input
# ❌ WRONG — unsorted array
unsorted = [37, 2, 72, 13, 25]
binary_search(unsorted, 2)   # returns -1 even though 2 is there!

# ✅ CORRECT — sort first
sorted_arr = sorted(unsorted)  # [2, 13, 25, 37, 72]
binary_search(sorted_arr, 2)  # returns 0 ✓

# Tip: Python has bisect module built-in for production use
import bisect
idx = bisect.bisect_left(sorted_arr, 2)
print(sorted_arr[idx] == 2)    # True

⛔ Common Mistake

Sorting takes O(n log n) time. If you’re only searching once, sorting + binary search is slower than linear search. Binary search pays off when you search the same sorted data many times — the sort cost is paid once, searches are O(log n) forever.

Where Binary Search Is Used in Real Life

Binary search isn’t just a textbook exercise. It runs inside software you use every day.

🗄️

Database Indexes

B-trees (the data structure behind MySQL, PostgreSQL indexes) use a generalized form of binary search to find rows in milliseconds across millions of records.

📦

Package Managers

npm, pip, and apt use binary search on sorted version lists to find compatible package versions quickly.

🎮

Game Development

Finding which tile a player is on, collision detection bounds, and sorted leaderboard lookups all use binary search variants.

🔤

Spell Checkers

Dictionaries are sorted. When you type a word, spell checkers run binary search on the dictionary to verify it in O(log n) time.

📡

Git Bisect

Git’s git bisect command uses binary search through your commit history to find which commit introduced a bug.

🌐

IP Routing

Routers use binary search on sorted prefix tables to find the right network path for packets — billions of times per second.

Knowledge Quiz

Five questions to lock in what you’ve learned.

  Binary Search Quiz
Question 1 of 5

The post How Binary Search Works — with Visual Examples appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/how-binary-search-works-visual-examples/feed/ 0 736
How Large Language Models Work — Deep Dive https://learnwithexamples.org/how-llm-work/ https://learnwithexamples.org/how-llm-work/#respond Wed, 15 Jul 2026 14:26:09 +0000 https://learnwithexamples.org/?p=727 Deep Dive · AI & Machine Learning Inside every AI assistant — ChatGPT, Claude, Gemini — is the same remarkable architecture. This guide pulls it apart layer by layer: tokens,…

The post How Large Language Models Work — Deep Dive appeared first on Learn With Examples.

]]>

Deep Dive · AI & Machine Learning

Inside every AI assistant — ChatGPT, Claude, Gemini — is the same remarkable architecture. This guide pulls it apart layer by layer: tokens, transformers, attention, and training. No PhD required.

📖 20 min read 🧪 4 interactive demos ❓ Knowledge quiz 💻 Real code examples

What is a Large Language Model?

A Large Language Model (LLM) is a neural network trained to predict the next word (or token) in a sequence of text. That’s it. The remarkable thing is how much intelligence emerges from doing this one task at massive scale.

When GPT-4 writes a poem, Claude explains a legal contract, or Gemini summarizes a research paper — all of them are doing the same fundamental thing: given a sequence of tokens, predict what comes next, one token at a time.

💡 Core Insight

An LLM isn’t “thinking” in the way humans do. It’s a very sophisticated pattern-completion engine — but trained on so much text that the patterns it learns encode reasoning, facts, language, and style.

📏

Scale

GPT-4 has ~1.8 trillion parameters. That’s 1,800,000,000,000 numbers that define its behaviour.

📚

Data

Trained on hundreds of billions of words scraped from the web, books, and code repositories.

Compute

Training large models costs tens of millions of dollars in GPU time and energy.

🔁

Architecture

Almost all modern LLMs use the Transformer architecture, invented at Google in 2017.

Tokenization — How Text Becomes Numbers

Computers can’t process raw text. The first step is breaking text into tokens — small chunks that the model understands. Tokens aren’t always full words; they can be parts of words, punctuation, or even single characters.

Modern LLMs use Byte Pair Encoding (BPE) tokenization. Common words like “the” become single tokens; rare words like “tokenization” might split into [“token”, “ization”]. Each token gets mapped to a unique integer ID.

Python
# Using OpenAI's tiktoken library (same tokenizer as GPT-4)
import tiktoken

encoder = tiktoken.get_encoding("cl100k_base")

text = "Large Language Models are fascinating!"
tokens = encoder.encode(text)

print(tokens)
# → [35, 18, 33, 5400, 46, 18, 13, 1234, ...]

print([encoder.decode([t]) for t in tokens])
# → ['Large', ' Language', ' Models', ' are', ' fas', 'cinating', '!']

print(f"Text length: {len(text)} chars → {len(tokens)} tokens")
# A useful rule of thumb: ~4 chars per token in English
  Interactive Demo — Tokenizer

Type any text below to see how it gets split into tokens. Each color = one token.


⚠️ Why Tokens Matter

LLMs have a context window — a maximum number of tokens they can process at once (e.g., 128,000 tokens for Claude 3). Long documents must be chunked to fit. Knowing this helps you understand why very long conversations or documents can cause issues.

Embeddings — Numbers That Carry Meaning

Once we have token IDs, each ID gets mapped to an embedding — a list of hundreds or thousands of decimal numbers called a vector. Think of it as coordinates in a very high-dimensional space.

The magic: similar words end up close together in this space. The word “king” and “queen” are near each other. “Paris” is to “France” what “Tokyo” is to “Japan”. The model learns these relationships entirely from predicting text — nobody hand-labels the geometry.

dim 1 dim 2 king queen prince royalty Paris Tokyo London capitals dog cat fox animals Simplified 2D view of embedding space (real models use 4,096+ dimensions)
Python
# Each token becomes a high-dimensional vector
# GPT-4 uses 12,288-dimensional embeddings!

token_id = 5400  # "fascinating"

# The embedding table has shape: [vocab_size, d_model]
embedding = embedding_table[token_id]

# embedding is now a vector like:
# [0.23, -0.51, 0.87, 0.12, ..., -0.34]
# 4096 numbers capturing the "meaning" of this token

The Transformer Architecture

The Transformer, introduced in the landmark 2017 paper “Attention Is All You Need” by Google researchers, is the engine powering almost every modern LLM.

A Transformer is a stack of identical layers. GPT-4 has ~96 of them. Each layer has two main components: a Multi-Head Attention block and a Feed-Forward Network. Information flows through all layers sequentially, each one refining the representation of the input.

Token Embeddings + Positional Encoding TRANSFORMER LAYER (×N) Multi-Head Self-Attention Feed-Forward Network Next Token Prediction 96 layers in GPT-4
1

Input Embedding + Positional Encoding

Each token is converted to a vector. Since attention has no built-in sense of order, a positional encoding is added — a pattern that tells the model where in the sequence each token sits.

2

Multi-Head Self-Attention

The most important layer. Every token “looks at” every other token and decides how much to attend to it. We’ll explore this interactively in the next section.

3

Feed-Forward Network

Each token position passes through an independent MLP (two linear layers with a non-linearity). This is where most of the model’s “factual knowledge” is stored.

4

Residual Connections + Layer Norm

After each sub-layer, the original input is added back (residual connection) and then normalized. This is what allows training of very deep networks without gradients vanishing.

Attention — The Secret Ingredient

The attention mechanism lets the model understand that in “The cat sat on the mat because it was tired”, the word “it” refers to “cat”, not “mat”. This kind of long-range dependency was the main weakness of older models (RNNs).

For each token, the model computes three vectors: a Query (what am I looking for?), a Key (what do I contain?), and a Value (what do I contribute?). Attention scores come from matching Queries against Keys across all positions.

Python (simplified)
import torch
import torch.nn.functional as F

def scaled_dot_product_attention(Q, K, V):
    d_k = Q.shape[-1]  # dimension of key vectors

    # 1. Compute attention scores: how much does each query match each key?
    scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)

    # 2. Normalize scores into a probability distribution (0–1, sum to 1)
    weights = F.softmax(scores, dim=-1)

    # 3. Weighted sum of Value vectors
    output = torch.matmul(weights, V)

    return output, weights

# "Multi-Head" = run this 8 or 16 times in parallel with different Q,K,V projections
# Then concatenate and project back down. Each head can "look" at different things.
  Interactive Demo — Attention Visualization

Click a word below to see which words in the sentence it “attends to” most strongly.

← Click a word to see its attention pattern

🔬 Multi-Head Attention

Modern LLMs run 32–128 attention heads in parallel, each looking for different relationships. One head might track pronouns, another tracks verb-subject agreement, another tracks named entities. The outputs are concatenated and projected.

Training — Learning from the Internet

Training an LLM means adjusting billions of parameters so the model gets better at predicting the next token. The training data is a massive corpus: web pages, books, Wikipedia, code, scientific papers.

The training loop is elegant: take a sequence of tokens, hide the last one, ask the model to predict it, measure the error (cross-entropy loss), and adjust all parameters slightly in the direction that reduces the error (backpropagation + gradient descent). Repeat this hundreds of billions of times.

Model Parameters Training Tokens Est. Cost
GPT-3 (2020) 175B 300B ~$4.6M
LLaMA 2 (2023) 70B 2T ~$3M
GPT-4 (2023) ~1.8T (est.) ~13T ~$100M
Claude 3 Opus (2024) Undisclosed Undisclosed Undisclosed
Python — Training Loop (simplified)
for batch in dataloader:
    tokens = batch['input_ids']     # shape: [batch, seq_len]
    inputs  = tokens[:, :-1]          # all tokens except last
    targets = tokens[:, 1:]           # all tokens except first

    # Forward pass — predict next token at every position
    logits = model(inputs)             # [batch, seq_len, vocab_size]

    # Measure how wrong we are
    loss = F.cross_entropy(
        logits.reshape(-1, vocab_size),
        targets.reshape(-1)
    )

    # Backward pass — compute gradients
    loss.backward()

    # Update parameters (Adam optimizer)
    optimizer.step()
    optimizer.zero_grad()

Temperature — Controlling Randomness

When an LLM predicts the next token, it produces a probability distribution over the entire vocabulary (~50,000–100,000 tokens). Temperature is a dial that controls how peaked or flat this distribution is.

Low temperature → the model almost always picks the most probable token (deterministic, repetitive). High temperature → the model samples more randomly from lower-probability tokens (creative, unpredictable, sometimes nonsensical).

  Interactive Demo — Temperature

Adjust the temperature to see how it affects the output for the prompt: “The weather today is…”

0.0 2.0
0.7

RLHF — Teaching the Model to Be Helpful

A pretrained LLM predicts text — but that doesn’t mean it’s helpful, safe, or honest. A model might complete “How do I make a bomb?” with a recipe, because that’s what a human might write on the internet.

Reinforcement Learning from Human Feedback (RLHF) — used by OpenAI, Anthropic, Google, and others — aligns the model with human preferences. The process has three stages:

1

Supervised Fine-Tuning (SFT)

Human contractors write ideal responses to thousands of prompts. The model is fine-tuned on these examples. Now it knows the format of helpful answers, but not yet which answers are better.

2

Reward Model Training

Humans rank pairs of model outputs: “Response A is better than B”. A separate neural network — the Reward Model — is trained to predict these rankings, scoring any response from 0 to 1.

3

Reinforcement Learning (PPO)

The LLM generates responses. The Reward Model scores them. The RL algorithm (Proximal Policy Optimization) nudges the LLM’s parameters to produce higher-scoring responses. This loop runs for thousands of steps.

📌 Beyond RLHF

Newer approaches include Direct Preference Optimization (DPO), which skips the reward model and directly trains on preference data — simpler and often equally effective. Anthropic’s Claude uses a variant called Constitutional AI (CAI), where the model is trained to critique its own responses against a set of principles.

Limitations & Hallucinations

LLMs are powerful but imperfect. Understanding their failure modes makes you a much better user (and builder) of AI systems.

👻

Hallucinations

Models generate fluent text even when they don’t “know” something. They can fabricate citations, facts, and names with full confidence.

📅

Knowledge Cutoff

Training data ends at a fixed date. The model has no awareness of events after that point unless given context.

📐

Math & Reasoning

LLMs are next-token predictors, not calculators. They can struggle with precise arithmetic and multi-step logical chains.

🪟

Context Window

They can only “see” a fixed window of tokens at once. Very long inputs require chunking strategies like RAG.

✅ Best Practices

Always verify factual claims from LLMs against primary sources. Use them for drafting, brainstorming, and reasoning assistance — not as authoritative sources of truth. Provide grounding context in your prompts, and use tools like search or code execution to extend their capabilities.

Knowledge Check

Test what you’ve learned. Five questions covering the key ideas from this article.

  Quiz — How Well Do You Know LLMs?
Question 1 of 5

The post How Large Language Models Work — Deep Dive appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/how-llm-work/feed/ 0 727
Stacks in Data Structures https://learnwithexamples.org/stacks-in-data-structures/ https://learnwithexamples.org/stacks-in-data-structures/#respond Fri, 03 Oct 2025 09:48:55 +0000 https://learnwithexamples.org/?p=621 Stacks in Data Structures: Push & Pop with Undo/Redo Example Push & Pop with Undo/Redo Example In the world of computer science and programming, data structures form the foundation of…

The post Stacks in Data Structures appeared first on Learn With Examples.

]]>
Stacks in Data Structures: Push & Pop with Undo/Redo Example

Push & Pop with Undo/Redo Example

In the world of computer science and programming, data structures form the foundation of efficient algorithm design. Among these fundamental structures, the stack stands out as one of the most elegant and widely-used concepts. Whether you’re browsing web pages, writing code in an editor, or executing function calls in a program, stacks are working behind the scenes to make it all possible.

This comprehensive guide will take you through everything you need to know about stacks, from basic concepts to real-world applications, with a special focus on the popular undo/redo functionality that we use every day.

What is a Stack?

A stack is a linear data structure that follows a specific order for its operations. Imagine a stack of plates in your kitchen—you can only add a new plate on top, and when you need a plate, you take one from the top. You cannot remove a plate from the middle or bottom without first removing all the plates above it. This is precisely how a stack data structure works in computer science.

Key Principle: Stacks follow the LIFO (Last In, First Out) principle, meaning the last element added to the stack will be the first one to be removed. Think of it as a “first in, last out” mechanism.

Visual Representation of a Stack

Element 4 (Top)
Element 3
Element 2
Element 1 (Bottom)
↑ Push (Add) | Pop (Remove) ↓

Core Operations of a Stack

A stack supports several fundamental operations that define its behavior. Understanding these operations is crucial for implementing and using stacks effectively.

1. Push Operation

The push operation adds an element to the top of the stack. When you push an element, it becomes the new top element, and the stack size increases by one. This operation has a time complexity of O(1), making it extremely efficient.

2. Pop Operation

The pop operation removes and returns the top element from the stack. After a pop operation, the element below becomes the new top. If you try to pop from an empty stack, it results in a stack underflow error. Like push, pop also operates in O(1) time.

3. Peek (or Top) Operation

The peek operation returns the top element without removing it from the stack. This allows you to inspect what’s at the top without modifying the stack structure.

4. isEmpty Operation

The isEmpty operation checks whether the stack contains any elements. It returns true if the stack is empty and false otherwise.

5. Size Operation

The size operation returns the number of elements currently in the stack.

Operation Description Time Complexity
Push Add element to top O(1)
Pop Remove element from top O(1)
Peek View top element O(1)
isEmpty Check if stack is empty O(1)
Size Get number of elements O(1)

Implementation of a Stack

Stacks can be implemented using arrays or linked lists. Here’s a simple implementation using JavaScript that demonstrates the core concepts:

class Stack { constructor() { this.items = []; } // Push element to stack push(element) { this.items.push(element); } // Pop element from stack pop() { if (this.isEmpty()) { return “Stack is empty”; } return this.items.pop(); } // Peek at top element peek() { if (this.isEmpty()) { return “Stack is empty”; } return this.items[this.items.length – 1]; } // Check if stack is empty isEmpty() { return this.items.length === 0; } // Get stack size size() { return this.items.length; } }

Interactive Stack Demo

Try Push and Pop Operations

Stack is empty
Status: Stack is empty | Size: 0

Real-World Application: Undo/Redo Functionality

One of the most practical and widely-used applications of stacks is implementing undo and redo functionality in text editors, graphics programs, and various software applications. This feature allows users to reverse their recent actions and restore previous states, significantly improving user experience and productivity.

How Undo/Redo Works with Stacks

The undo/redo mechanism uses two stacks:

  • Undo Stack: Stores the history of actions performed by the user
  • Redo Stack: Stores actions that have been undone and can be reapplied

When a user performs an action (like typing text), that action is pushed onto the undo stack. When the user clicks undo, the most recent action is popped from the undo stack and pushed onto the redo stack. If the user then clicks redo, the action is popped from the redo stack and pushed back onto the undo stack.

Important: When a new action is performed after an undo, the redo stack is cleared. This prevents inconsistent states where redone actions might conflict with new actions.

Interactive Undo/Redo Demo

Text Editor with Undo/Redo

Undo Stack: Empty
Redo Stack: Empty

Undo/Redo Implementation

class UndoRedoManager { constructor() { this.undoStack = []; this.redoStack = []; } // Perform new action executeAction(action) { this.undoStack.push(action); this.redoStack = []; // Clear redo stack } // Undo last action undo() { if (this.undoStack.length > 0) { let action = this.undoStack.pop(); this.redoStack.push(action); return action; } return null; } // Redo last undone action redo() { if (this.redoStack.length > 0) { let action = this.redoStack.pop(); this.undoStack.push(action); return action; } return null; } }

Other Real-World Applications of Stacks

Beyond undo/redo functionality, stacks are used in numerous other applications:

1. Function Call Stack

When a program executes functions, the system uses a call stack to keep track of function calls. Each time a function is called, its execution context is pushed onto the stack. When the function completes, its context is popped off.

2. Expression Evaluation

Stacks are essential for evaluating mathematical expressions and converting between infix, prefix, and postfix notations. Compilers use stacks to parse and evaluate expressions in code.

3. Browser History

Web browsers use stacks to implement the back button functionality. Each visited page is pushed onto the stack, and clicking back pops the most recent page.

4. Backtracking Algorithms

Many algorithms, such as maze solving, game state exploration, and puzzle solving, use stacks to keep track of paths and enable backtracking to previous states.

5. Syntax Checking

Compilers and text editors use stacks to check for balanced parentheses, brackets, and braces in code. Opening symbols are pushed onto the stack, and closing symbols pop them off.

Advantages and Limitations

Advantages of Stacks

  • Simple and easy to implement
  • Efficient O(1) time complexity for push and pop operations
  • Useful for managing function calls and recursion
  • Natural fit for problems requiring LIFO order
  • Memory efficient when implemented properly

Limitations of Stacks

  • Limited access—only the top element is directly accessible
  • Fixed size in array-based implementations (can cause overflow)
  • Not suitable for searching or accessing middle elements
  • Requires careful management to avoid stack overflow or underflow

Best Practices for Using Stacks

To effectively use stacks in your programs, consider these best practices:

  1. Always check for empty stacks: Before popping or peeking, verify the stack isn’t empty to prevent errors
  2. Choose the right implementation: Use arrays for simple cases and linked lists when dynamic sizing is important
  3. Consider memory constraints: Be mindful of stack size limits, especially in recursive algorithms
  4. Document stack usage: Clearly document what each stack stores and its purpose in your code
  5. Handle edge cases: Plan for empty stacks, full stacks, and invalid operations

Conclusion

Stacks are fundamental data structures that power countless applications we use daily. From the undo button in your text editor to the function calls in every program you run, stacks work silently behind the scenes to make computing efficient and intuitive. Understanding how stacks work—particularly the push and pop operations—is essential for any programmer or computer science student.

The undo/redo example demonstrates how a simple data structure can enable powerful user experiences. By maintaining two stacks and carefully managing state transitions, we can create robust systems that allow users to explore, experiment, and correct their actions without fear.

As you continue your journey in programming and data structures, you’ll find stacks appearing in unexpected places. Whether you’re implementing a compiler, designing an algorithm, or building a user interface, the stack’s elegant simplicity and powerful capabilities make it an indispensable tool in your programming toolkit.

Key Takeaway: Master the stack, and you master a fundamental building block of computer science. Its LIFO principle, combined with efficient O(1) operations, makes it perfect for managing sequential operations, tracking history, and enabling reversible actions in software applications.

Also check: Arrays Explained with Real-Life Examples

The post Stacks in Data Structures appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/stacks-in-data-structures/feed/ 0 621
OSI Model Explained: 7 Layers with Real-World Examples https://learnwithexamples.org/osi-model-explained/ https://learnwithexamples.org/osi-model-explained/#respond Fri, 19 Sep 2025 08:45:09 +0000 https://learnwithexamples.org/?p=592 OSI Model Explained: 7 Layers with Real-World Examples Understanding Network Communication The OSI (Open Systems Interconnection) model is a conceptual framework that standardizes the communication functions of a telecommunication or…

The post OSI Model Explained: 7 Layers with Real-World Examples appeared first on Learn With Examples.

]]>
OSI Model Explained: 7 Layers with Real-World Examples

Understanding Network Communication

The OSI (Open Systems Interconnection) model is a conceptual framework that standardizes the communication functions of a telecommunication or computing system. Think of it as a blueprint that helps different computer systems communicate with each other, regardless of their underlying architecture.

What is the OSI Model?

Imagine you’re sending a letter to a friend in another country. You write the message, put it in an envelope, address it, take it to the post office, and rely on various transportation methods to deliver it. The OSI model works similarly for digital communication, breaking down the complex process of network communication into seven distinct layers, each with specific responsibilities.

Developed by the International Organization for Standardization (ISO) in 1984, the OSI model serves as a universal reference point for understanding how data travels from one computer to another across a network. Each layer performs specific functions and communicates only with the layers directly above and below it, creating a structured approach to network communication.

The 7 Layers of the OSI Model

Click on each layer to explore its functions and real-world examples:

Layer 7: Application Layer
Layer 6: Presentation Layer
Layer 5: Session Layer
Layer 4: Transport Layer
Layer 3: Network Layer
Layer 2: Data Link Layer
Layer 1: Physical Layer

Layer 7: Application Layer

Function: This is the layer closest to the user. It provides network services directly to applications and end-users.

What it does: Handles high-level protocols, representation, encoding, and dialog control. It’s where user applications interact with the network.

Real-World Examples:

  • Web Browsing: When you type www.google.com in your browser
  • Email: Sending and receiving emails through Gmail or Outlook
  • File Transfer: Uploading files to Google Drive or Dropbox
  • Video Streaming: Watching videos on YouTube or Netflix

Common Protocols:

HTTP/HTTPS SMTP FTP DNS DHCP

Layer 6: Presentation Layer

Function: Translates data between the application layer and the network. It’s responsible for data encryption, compression, and format conversion.

What it does: Ensures that data sent from one system can be understood by another, handling different data formats and encryption.

Real-World Examples:

  • Image Formats: Converting JPEG to PNG or displaying images in web browsers
  • Data Encryption: HTTPS encryption when you shop online
  • Text Encoding: Converting ASCII to Unicode for international characters
  • Video Compression: MP4, AVI format handling in media players

Common Protocols:

SSL/TLS JPEG GIF MPEG ASCII

Layer 5: Session Layer

Function: Manages sessions or connections between applications. It establishes, manages, and terminates connections between local and remote applications.

What it does: Controls dialogues and connections, manages full-duplex, half-duplex, or simplex communications.

Real-World Examples:

  • Video Calls: Zoom or Skype maintaining connection during a call
  • Database Sessions: SQL database connections staying active
  • Web Sessions: Shopping cart maintaining items while you browse
  • Remote Desktop: RDP sessions for remote computer access

Common Protocols:

NetBIOS RPC SQL NFS PPTP

Layer 4: Transport Layer

Function: Ensures reliable data transfer between end systems. It handles error correction, flow control, and retransmission of lost data.

What it does: Breaks large messages into smaller packets and reassembles them at the destination, ensuring data integrity.

Real-World Examples:

  • Web Traffic: TCP ensuring all webpage data arrives correctly
  • File Downloads: Ensuring downloaded files are complete and uncorrupted
  • Live Streaming: UDP allowing real-time video with some data loss tolerance
  • Online Gaming: UDP for fast response times in multiplayer games

Common Protocols:

TCP UDP SCTP SPX

Layer 3: Network Layer

Function: Handles routing of data packets between different networks. It determines the best path for data to travel from source to destination.

What it does: Manages logical addressing (IP addresses) and routing decisions across multiple networks.

Real-World Examples:

  • Internet Routing: Your request to visit a website finding the best path through internet routers
  • GPS Navigation: Finding the best route from your location to a destination
  • Corporate Networks: Data traveling between different office locations
  • VPN Connections: Routing encrypted traffic through secure tunnels

Common Protocols:

IP ICMP ARP OSPF BGP

Layer 2: Data Link Layer

Function: Handles communication between adjacent network nodes. It provides error detection and correction for the physical layer.

What it does: Manages frame formatting, MAC addresses, and controls access to the physical transmission medium.

Real-World Examples:

  • Ethernet Networks: Computers communicating on a local network switch
  • WiFi Connections: Your device connecting to a wireless access point
  • Bluetooth: Pairing devices and managing short-range communication
  • Network Switches: Forwarding data between devices on the same network

Common Protocols:

Ethernet WiFi (802.11) PPP Frame Relay ATM

Layer 1: Physical Layer

Function: Handles the physical transmission of raw binary data over communication channels. It defines electrical, mechanical, and procedural specifications.

What it does: Converts digital bits into electrical signals, radio waves, or light pulses for transmission over physical media.

Real-World Examples:

  • Ethernet Cables: Physical copper wires carrying electrical signals
  • Fiber Optic Cables: Light pulses traveling through glass fibers
  • Radio Waves: WiFi and cellular signals transmitted through air
  • USB Cables: Physical connection between devices

Common Technologies:

Copper Wire Fiber Optic Radio Frequency Infrared Bluetooth

Memory Trick to Remember the Layers

Here’s a popular mnemonic to remember the OSI layers from top to bottom:

“All People Seem To Need Data Processing”
  • Application
  • Presentation
  • Session
  • Transport
  • Network
  • Data Link
  • Physical

Interactive Demo: Data Flow Through OSI Layers

Watch how a simple web request travels through the OSI model:

📱 Application Layer: User types “Hello World” in web browser
🔒 Presentation Layer: Encrypts data using HTTPS/TLS
🔗 Session Layer: Establishes HTTP session with server
📦 Transport Layer: TCP breaks data into packets, adds port numbers
🗺 Network Layer: Adds IP addresses for routing
📡 Data Link Layer: Adds MAC addresses for local delivery
⚡ Physical Layer: Converts to electrical/optical signals
📥 Data received and processed in reverse order at destination

Real-World Application Examples

📧 Email Communication

Application: Outlook/Gmail interface

Presentation: Text encoding, attachment compression

Session: SMTP session management

Transport: TCP ensures reliable delivery

Network: IP routing to mail server

Data Link: Ethernet frame to local router

Physical: Electrical signals over cable

🎥 Video Streaming

Application: Netflix/YouTube player

Presentation: Video compression (H.264/H.265)

Session: Streaming session management

Transport: UDP for real-time delivery

Network: IP routing with QoS

Data Link: WiFi 802.11 frames

Physical: Radio waves from router

🛒 Online Shopping

Application: Shopping cart interface

Presentation: HTTPS encryption for security

Session: Shopping session cookies

Transport: TCP for data integrity

Network: IP routing to e-commerce server

Data Link: Ethernet switching

Physical: Fiber optic connections

☁ Cloud Storage

Application: Google Drive/OneDrive

Presentation: File compression and encryption

Session: Authentication and sync sessions

Transport: TCP for file integrity

Network: IP routing to cloud servers

Data Link: Multiple link technologies

Physical: Various transmission media

OSI vs TCP/IP Model Comparison

OSI Layer OSI Function TCP/IP Layer Common Protocols
Application User interface and network services Application HTTP, HTTPS, FTP, SMTP, DNS
Presentation Data encryption and compression SSL/TLS, JPEG, MPEG, ASCII
Session Session management NetBIOS, RPC, SQL
Transport Reliable data transfer Transport TCP, UDP
Network Routing and logical addressing Internet IP, ICMP, ARP, OSPF
Data Link Error detection and MAC addressing Network Access Ethernet, WiFi, PPP
Physical Physical transmission of bits Copper, Fiber, Radio

Why is the OSI Model Important?

🎯 Benefits of Understanding the OSI Model:

  • Troubleshooting: When your internet isn’t working, you can systematically check each layer to identify the problem
  • Network Design: Architects use the OSI model to design robust network infrastructures
  • Protocol Development: New networking protocols are developed with OSI layers in mind
  • Education: Provides a standardized way to teach and learn networking concepts
  • Interoperability: Ensures different vendors’ equipment can work together
  • Security: Each layer can implement specific security measures

Common Troubleshooting Using OSI Layers

🔧 Practical Troubleshooting Approach:

Problem: Website won’t load

  1. Physical Layer: Check if network cables are connected, WiFi is on
  2. Data Link Layer: Verify network adapter is working, getting MAC address
  3. Network Layer: Check if you have an IP address, can ping router
  4. Transport Layer: Test if specific ports are accessible
  5. Session Layer: Check if authentication/sessions are working
  6. Presentation Layer: Verify if encryption/certificates are valid
  7. Application Layer: Test if the web browser or application is functioning

Conclusion

The OSI model serves as a fundamental framework for understanding network communication. While modern networking often uses the simplified TCP/IP model in practice, the OSI model remains invaluable for education, troubleshooting, and designing network solutions. Each layer has distinct responsibilities, and understanding these layers helps network professionals design better systems, troubleshoot problems more effectively, and ensure reliable communication between diverse systems.

Whether you’re browsing the web, sending emails, streaming videos, or working with cloud applications, all these activities rely on the principles outlined in the OSI model. The next time you click a link or send a message, remember the complex but elegant process happening behind the scenes across all seven layers!

🚀 Next Steps for Learning:

  • Explore specific protocols at each layer in detail
  • Practice network troubleshooting using the OSI approach
  • Learn about network security at different OSI layers
  • Study how modern technologies like cloud computing map to OSI layers
  • Experiment with network analysis tools like Wireshark

The post OSI Model Explained: 7 Layers with Real-World Examples appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/osi-model-explained/feed/ 0 592
Arrays Explained with Real-Life Examples https://learnwithexamples.org/arrays-explained-with-real-life-examples/ https://learnwithexamples.org/arrays-explained-with-real-life-examples/#respond Thu, 28 Aug 2025 18:29:39 +0000 https://learnwithexamples.org/?p=574 Arrays Explained with Real-Life Examples Imagine organizing your music playlist, arranging seats in a theater, or creating a shopping list. What do all these activities have in common? They all…

The post Arrays Explained with Real-Life Examples appeared first on Learn With Examples.

]]>
Arrays Explained with Real-Life Examples

Imagine organizing your music playlist, arranging seats in a theater, or creating a shopping list. What do all these activities have in common? They all involve organizing items in a specific order – and that’s exactly what arrays do in programming!

What is an Array?

An array is a fundamental data structure that stores multiple items of the same type in a single variable. Think of it as a container with numbered compartments, where each compartment can hold one piece of data. Just like apartments in a building have addresses (apartment numbers), each element in an array has an index (position number) starting from 0.

Visual Representation of an Array

Here’s how an array looks conceptually:

0
Apple
1
Banana
2
Orange
3
Grape
4
Mango

Key Point: Array indexing starts at 0, not 1! So the first element is at index 0, the second at index 1, and so on.

Real-Life Example 1: Theater Seating Arrangement

Let’s explore arrays using a theater seating system. In a theater, seats are arranged in rows and numbered sequentially. This is exactly how arrays work – each seat has a specific position (index) and can hold one person (data).

Interactive Theater Seating Demo

Click on any seat to toggle between available (green) and occupied (red):

// JavaScript Array for Theater Seating let theaterSeats = [ false, // Seat 0: Available true, // Seat 1: Occupied false, // Seat 2: Available true, // Seat 3: Occupied false // Seat 4: Available ]; // Access specific seat console.log(theaterSeats[0]); // false (available) console.log(theaterSeats[1]); // true (occupied) // Check total seats console.log(theaterSeats.length); // 5 seats

Why Arrays are Perfect for Seating

  • Direct Access: Want to check seat 15? Just access seats[15] – no need to count from seat 1!
  • Efficient Updates: Booking or canceling a seat takes the same amount of time regardless of position
  • Sequential Processing: Easy to iterate through all seats to count available ones
  • Fixed Size: Theater has a fixed number of seats, just like arrays have a defined size

Real-Life Example 2: Grocery Shopping List

A grocery list is another perfect example of arrays in real life. Each item on your list has a position, and you can add, remove, or check off items. Let’s see how this translates to programming concepts.

Interactive Grocery List Manager

// JavaScript Array for Grocery List let groceryList = [ “Milk”, “Bread”, “Eggs”, “Apples”, “Cheese” ]; // Array Operations groceryList.push(“Tomatoes”); // Add to end groceryList.unshift(“Yogurt”); // Add to beginning groceryList.splice(2, 1); // Remove item at index 2 let firstItem = groceryList[0]; // Get first item

Memory Organization: How Arrays Work Behind the Scenes

Understanding how arrays are stored in memory helps explain why they’re so efficient for certain operations.

Array Memory Layout

Arrays store elements in contiguous memory locations:

Index:
0
1
2
3
4
Memory:
1000
1004
1008
1012
1016
Value:
Apple
Banana
Orange
Grape
Mango

This contiguous storage is why accessing array[100] takes the same time as accessing array[0] – the computer can calculate the exact memory location instantly!

Common Array Operations with Interactive Examples

Array Operations Playground

Types of Arrays

1. Static Arrays

Like reserved theater seats – fixed size that cannot change once created.

// C++ Static Array int scores[5] = {85, 92, 78, 96, 88}; // Size is fixed at 5 elements // Java Static Array int[] temperatures = new int[7]; // Fixed size of 7

2. Dynamic Arrays

Like an expandable shopping list – can grow or shrink as needed.

// JavaScript Dynamic Array let playlist = [“Song1”, “Song2”]; playlist.push(“Song3”); // Now has 3 elements playlist.push(“Song4”); // Now has 4 elements // Python Dynamic List shopping_cart = [“Item1”, “Item2”] shopping_cart.append(“Item3”) # Automatically expands

Multidimensional Arrays: Beyond Single Lists

Sometimes we need to organize data in multiple dimensions, like a seating chart with rows and columns, or a chess board.

2D Array: Movie Theater Layout

Click seats to toggle availability. This demonstrates a 2D array where we have rows and columns:

Available Occupied
// 2D Array for Movie Theater (3 rows, 6 seats each) let theater = [ [true, false, true, true, false, true], // Row 0 [false, false, true, false, true, true], // Row 1 [true, true, false, false, false, true] // Row 2 ]; // Access seat in Row 1, Column 3 console.log(theater[1][3]); // false (occupied) // Book a seat theater[0][1] = false; // Book seat in Row 0, Column 1

Arrays vs Other Data Structures

Operation Array Linked List Real-Life Analogy
Access by Index O(1) – Very Fast O(n) – Slow Finding apartment by number vs following directions
Insert at Beginning O(n) – Slow O(1) – Fast Adding person to front of theater row vs joining a line
Insert at End O(1) – Fast O(1) – Fast Adding item to shopping list end
Memory Usage Efficient Extra overhead Compact apartment building vs houses with long driveways

Common Array Algorithms

1. Linear Search

Like checking each seat in a theater one by one to find your friend.

function findItem(array, target) { for (let i = 0; i < array.length; i++) { if (array[i] === target) { return i; // Found at index i } } return -1; // Not found }

2. Binary Search (for sorted arrays)

Like opening a phone book to the middle and deciding which half to search next.

function binarySearch(sortedArray, target) { let left = 0; let right = sortedArray.length – 1; while (left <= right) { let mid = Math.floor((left + right) / 2); if (sortedArray[mid] === target) return mid; if (sortedArray[mid] < target) left = mid + 1; else right = mid - 1; } return -1; }

Practical Applications of Arrays

1. Image Processing

Digital images are 2D arrays where each element represents a pixel’s color value. A 1920×1080 image is essentially a 2D array with 1920 columns and 1080 rows.

2. Game Development

Game boards (like Tic-tac-toe, Chess, or Sudoku) are represented as 2D arrays. Each position stores the current piece or state.

3. Database Records

Arrays store query results, where each element represents a database row. This allows efficient processing of multiple records.

4. Music Streaming

Your playlist is an array of songs. Shuffle feature randomly reorders the array, while repeat functionality cycles through array elements.

Best Practices for Working with Arrays

✅ Do’s

  • Always check array bounds before accessing elements
  • Use meaningful variable names: studentGrades instead of arr
  • Consider using built-in methods like map(), filter(), reduce()
  • Initialize arrays with expected size when possible for better performance
  • Use const for arrays that won’t be reassigned (the contents can still change)

❌ Don’ts

  • Don’t access array elements without checking if index exists
  • Don’t modify array size frequently in loops (use appropriate data structure)
  • Don’t use arrays for key-value pairs (use objects/maps instead)
  • Don’t assume array indices are continuous if elements were deleted

Conclusion

Arrays are fundamental building blocks in programming, much like how organizing systems work in real life. Whether you’re managing a theater seating chart, organizing a grocery list, or processing digital images, arrays provide an efficient and intuitive way to store and manipulate collections of data.

The key advantages of arrays include:

  • Fast Access: O(1) time to access any element by index
  • Memory Efficiency: Elements stored in contiguous memory locations
  • Cache Friendly: Sequential access patterns work well with CPU cache
  • Simplicity: Easy to understand and implement

Understanding arrays thoroughly provides a solid foundation for learning more complex data structures and algorithms. As you continue your programming journey, you’ll find that many advanced concepts build upon the simple yet powerful array structure.

Ready to Practice?

Try implementing these array operations in your favorite programming language:

  • Create a student grade tracker
  • Build a simple playlist manager
  • Implement a basic seat reservation system
  • Design a shopping cart with add/remove functionality

The post Arrays Explained with Real-Life Examples appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/arrays-explained-with-real-life-examples/feed/ 0 574
IP Address vs. MAC Address: What’s the Difference? https://learnwithexamples.org/ip-address-vs-mac-address/ https://learnwithexamples.org/ip-address-vs-mac-address/#respond Wed, 16 Jul 2025 07:48:51 +0000 https://learnwithexamples.org/?p=484 IP Address vs. MAC Address: What’s the Difference? 🏠 The Postal System Analogy Imagine you’re sending a letter to a friend. You need two key pieces of information: where they…

The post IP Address vs. MAC Address: What’s the Difference? appeared first on Learn With Examples.

]]>
IP Address vs. MAC Address: What’s the Difference?

🏠 The Postal System Analogy

Imagine you’re sending a letter to a friend. You need two key pieces of information: where they live (their home address) and who they are (their name on the mailbox). In networking, IP addresses work like home addresses – they tell us where to send data on the internet. MAC addresses work like unique ID tags – they identify the specific device, like a person’s name on their mailbox.

Understanding the Basics

In the world of computer networking, two types of addresses play crucial roles in ensuring data reaches its destination: IP addresses and MAC addresses. While both serve as identifiers, they operate at different levels of network communication and serve distinct purposes. Understanding the difference between these addressing systems is fundamental to grasping how modern networks function.

Think of network communication like a sophisticated postal system. Just as the postal service needs both a street address to know where to deliver mail and a recipient’s name to ensure it reaches the right person, computer networks use both IP and MAC addresses to route data efficiently and accurately.

📬 Postal System vs Network Communication

Sender’s Address
(Source IP)
Post Office
(Router)
Recipient’s Address
(Destination IP)
Mailbox Name
(MAC Address)

What is an IP Address?

An Internet Protocol (IP) address is a unique numerical identifier assigned to every device connected to a network that uses the Internet Protocol for communication. It serves as a logical address that can change based on the device’s location within the network topology.

IP addresses come in two main versions: IPv4 and IPv6. IPv4 addresses consist of four numbers separated by dots (like 192.168.1.1), while IPv6 addresses use a longer hexadecimal format to accommodate the growing number of internet-connected devices.

🌐 Interactive IP Address Demo

Your Device
Click a button to see IP examples

🏠 IP Address as Home Address

Just like your home address changes when you move to a new house, your device’s IP address changes when you connect to different networks. When you’re at home, your laptop might have the IP address 192.168.1.100. When you take it to a coffee shop, it might become 10.0.0.50. The device is the same, but its “network address” changes based on location.

Types of IP Addresses

Private IP Addresses: These are used within local networks and are not routable on the internet. Common private IP ranges include 192.168.x.x, 10.x.x.x, and 172.16.x.x to 172.31.x.x.

Public IP Addresses: These are globally unique addresses assigned by Internet Service Providers (ISPs) and are used to identify devices on the internet.

Common IP Address Examples:
Private: 192.168.1.1 (typical home router)
Private: 10.0.0.1 (common in corporate networks)
Public: 8.8.8.8 (Google’s DNS server)
Loopback: 127.0.0.1 (localhost – your own device)

What is a MAC Address?

A Media Access Control (MAC) address is a unique hardware identifier assigned to every network interface controller (NIC) during manufacturing. Unlike IP addresses, MAC addresses are permanent and cannot be changed under normal circumstances (though they can be spoofed through software).

MAC addresses consist of 12 hexadecimal characters, typically displayed in pairs separated by colons or hyphens (like 00:1A:2B:3C:4D:5E). The first half identifies the manufacturer, while the second half is a unique identifier for that specific device.

🔧 Interactive MAC Address Demo

Network Device
Click a button to explore MAC addresses

🏷 MAC Address as Device ID

Think of a MAC address like a social security number or a serial number on an appliance. Just as your social security number stays the same regardless of where you live, your network card’s MAC address remains constant whether you’re at home, at work, or traveling. It’s permanently “burned” into the hardware during manufacturing.

MAC Address Structure

A MAC address is divided into two parts: the Organizationally Unique Identifier (OUI) and the device-specific identifier. The OUI (first 24 bits) identifies the manufacturer, while the remaining 24 bits provide a unique identifier for each device from that manufacturer.

MAC Address Breakdown:
Example: 00:1A:2B:3C:4D:5E
OUI: 00:1A:2B (identifies manufacturer)
Device ID: 3C:4D:5E (unique device identifier)

Key Differences: IP vs MAC Addresses

Aspect IP Address MAC Address
Purpose Logical addressing for network routing Physical identification of network hardware
Changeability Can change (dynamic/static assignment) Permanent (burned into hardware)
Scope Global (internet-wide routing) Local (within same network segment)
Format IPv4: 192.168.1.1
IPv6: 2001:db8::1
00:1A:2B:3C:4D:5E
Layer Network Layer (Layer 3) Data Link Layer (Layer 2)
Assignment DHCP server or manual configuration Manufacturer during production

How They Work Together

IP and MAC addresses work in tandem to ensure successful data transmission. When you send data across a network, your device uses IP addresses to determine the destination, but it needs MAC addresses to physically deliver the data packets within each network segment.

📡 Network Communication Layers

Application Layer – Your Email/Web Browser
Network Layer – IP Address Routing
Data Link Layer – MAC Address Delivery
Physical Layer – Electrical Signals

🔄 Address Resolution Protocol (ARP) Demo

ARP (Address Resolution Protocol) translates IP addresses to MAC addresses within a local network.

Click “Show ARP Process” to see how it works!

Real-World Examples and Applications

Example 1: Home Network

When you connect your smartphone to your home Wi-Fi, your router assigns it an IP address (like 192.168.1.100) through DHCP. However, your phone’s Wi-Fi adapter has a permanent MAC address (like 00:1A:2B:3C:4D:5E) that identifies it uniquely on the local network.

🏠 Home Network Scenario

Smartphone
IP: 192.168.1.100
MAC: 00:1A:2B:3C:4D:5E
Laptop
IP: 192.168.1.101
MAC: 00:1A:2B:3C:4D:5F
Router
IP: 192.168.1.1
MAC: 00:1A:2B:3C:4D:60

Example 2: Corporate Network

In a large office building, hundreds of devices might share the same network infrastructure. Each device has a unique MAC address, but they all receive IP addresses from the same corporate IP range (like 10.0.0.x). The MAC addresses help switches deliver data to the correct physical port, while IP addresses handle routing between different network segments.

Example 3: Internet Communication

When you visit a website, your computer uses IP addresses to route packets across the internet. However, at each network hop, routers use MAC addresses to forward packets to the next router in the path. The MAC addresses change at each hop, but the IP addresses remain the same throughout the journey.

💡 Did You Know?

Every time a packet travels through a router, the MAC addresses in the packet header are replaced with new ones for the next network segment, but the IP addresses remain unchanged. This is why IP addresses are called “logical” addresses (they stay the same end-to-end) while MAC addresses are “physical” addresses (they change at each network hop).

Security Implications

Both IP and MAC addresses have important security implications. IP addresses can be used to track general location and network activity, while MAC addresses can be used for device fingerprinting and tracking within local networks.

⚠ Privacy Considerations

MAC Address Privacy: Since MAC addresses are unique and permanent, they can be used to track devices across different networks. Modern operating systems now use MAC address randomization to enhance privacy.

IP Address Privacy: Your public IP address can reveal your approximate location and ISP. VPNs and proxy servers can help mask your real IP address.

Troubleshooting with IP and MAC Addresses

Network administrators regularly use both IP and MAC addresses for troubleshooting and network management. Understanding how to work with both types of addresses is essential for diagnosing connectivity issues.

Common Network Commands:
Windows: ipconfig /all (shows both IP and MAC)
Mac/Linux: ifconfig (shows both IP and MAC)
Windows: arp -a (shows ARP table)
Ping: ping 192.168.1.1 (tests IP connectivity)

Common Troubleshooting Scenarios

Scenario 1: Device Can’t Connect to Network
Check if the device has a valid IP address. If it shows 169.254.x.x (Windows) or similar, the device couldn’t obtain an IP address from the DHCP server.

Scenario 2: Duplicate IP Address
Two devices accidentally configured with the same IP address will cause conflicts. The MAC addresses help identify which physical devices are involved.

Scenario 3: MAC Address Filtering
Some routers use MAC address filtering for security. If a device can’t connect, check if its MAC address is on the allowed list.

Future Considerations

As technology evolves, both IP and MAC addressing systems continue to adapt. IPv6 adoption is growing to address the shortage of IPv4 addresses, while MAC address randomization is becoming more common to protect user privacy.

The Internet of Things (IoT) is creating billions of new connected devices, each requiring both IP and MAC addresses. Understanding these fundamental networking concepts becomes increasingly important as our world becomes more connected.

🔮 Looking Forward

With IPv6, we have enough addresses for every grain of sand on Earth to have its own IP address. MAC addresses are also evolving, with new standards like EUI-64 providing more addresses for the growing number of network devices.

Conclusion

IP and MAC addresses serve complementary but distinct roles in network communication. IP addresses provide logical addressing for routing data across networks, much like street addresses guide mail delivery. MAC addresses provide physical identification of network hardware, similar to unique serial numbers on devices.

Understanding the difference between these addressing systems is crucial for anyone working with networks, from basic home networking to complex enterprise infrastructure. While IP addresses handle the “where” of network communication, MAC addresses handle the “what” – together, they ensure that data reaches its intended destination reliably and efficiently.

As networking technology continues to evolve, these fundamental concepts remain essential building blocks for understanding how modern networks operate. Whether you’re troubleshooting connectivity issues, designing network security policies, or simply curious about how your devices communicate, the relationship between IP and MAC addresses forms the foundation of network communication.

🎯 Final Analogy

Think of network communication like a sophisticated delivery system: IP addresses are like GPS coordinates that guide packages across the globe, while MAC addresses are like the specific delivery truck that actually carries the package to your doorstep. Both are necessary, both serve different purposes, and both work together to ensure successful delivery.

Also check: What is DNS? Complete Guide with Examples

The post IP Address vs. MAC Address: What’s the Difference? appeared first on Learn With Examples.

]]>
https://learnwithexamples.org/ip-address-vs-mac-address/feed/ 0 484