C · Course navigation

C: k-nearest-neighbors classification

Sort training points by distance to a query and let the k closest vote.

On this page
Before you start

C structs, qsort, and the math library.

  • Sort training points by distance to a query and let the k closest vote.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

k-nearest neighbours classifies a new point by looking at the k closest training points and choosing the most common label among them. There is no training step: the training data itself is the model. The distance is usually Euclidean, computed with sqrt, which needs the math library (link with -lm).

The program stores each training point with its label and its distance to the query, sorts by distance with qsort, and counts the labels among the first k. A small k follows noise, while a large k blurs the boundary. When features have very different scales, they should be normalized first.

Code example

C
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  double x, y;
  char label;
  double dist;
} Point;

static int by_distance(const void *p, const void *q) {
  double a = ((const Point *)p)->dist;
  double b = ((const Point *)q)->dist;
  return (a > b) - (a < b);
}

static char classify(const Point *train, int n, double qx, double qy, int k) {
  Point copy[8];
  for (int i = 0; i < n; i++) {
    copy[i] = train[i];
    copy[i].dist = hypot(copy[i].x - qx, copy[i].y - qy);
  }
  qsort(copy, (size_t)n, sizeof copy[0], by_distance);

  int votes_a = 0, votes_b = 0;
  for (int i = 0; i < k; i++) {
    if (copy[i].label == 'A') {
      votes_a++;
    } else {
      votes_b++;
    }
  }
  return votes_a > votes_b ? 'A' : 'B';
}

int main(void) {
  Point train[] = {
    {1, 1, 'A', 0}, {1, 2, 'A', 0}, {2, 1, 'A', 0},
    {6, 6, 'B', 0}, {7, 6, 'B', 0}, {6, 7, 'B', 0},
  };
  printf("(5,5) -> %c\n", classify(train, 6, 5, 5, 3));
  printf("(2,2) -> %c\n", classify(train, 6, 2, 2, 3));
  printf("(3,3) with k=5 -> %c\n", classify(train, 6, 3, 3, 5));
  return 0;
}
Output
(5,5) -> B
(2,2) -> A
(3,3) with k=5 -> A

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1#include <math.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5typedef struct {
6 double x, y;
7 char label;
8 double dist;
9} Point;
10
11static int by_distance(const void *p, const void *q) {
12 double a = ((const Point *)p)->dist;
13 double b = ((const Point *)q)->dist;
14 return (a > b) - (a < b);
15}
16
17static char classify(const Point *train, int n, double qx, double qy, int k) {
18 Point copy[8];
19 for (int i = 0; i < n; i++) {
20 copy[i] = train[i];
21 copy[i].dist = hypot(copy[i].x - qx, copy[i].y - qy);
22 }
23 qsort(copy, (size_t)n, sizeof copy[0], by_distance);
24
25 int votes_a = 0, votes_b = 0;
26 for (int i = 0; i < k; i++) {
27 if (copy[i].label == 'A') {
28 votes_a++;
29 } else {
30 votes_b++;
31 }
32 }
33 return votes_a > votes_b ? 'A' : 'B';
34}
35
36int main(void) {
37 Point train[] = {
38 {1, 1, 'A', 0}, {1, 2, 'A', 0}, {2, 1, 'A', 0},
39 {6, 6, 'B', 0}, {7, 6, 'B', 0}, {6, 7, 'B', 0},
40 };
41 printf("(5,5) -> %c\n", classify(train, 6, 5, 5, 3));
42 printf("(2,2) -> %c\n", classify(train, 6, 2, 2, 3));
43 printf("(3,3) with k=5 -> %c\n", classify(train, 6, 3, 3, 5));
44 return 0;
45}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Distance from each training point to the query.—
—
Sort the points from nearest to farthest.—
—
Each of the k nearest points votes for its label.—
—
The three nearest points to (5,5) all belong to class B.—
(5,5) -> B
With k = 5 the vote includes some B points, but A still has three of the five.—
(5,5) -> B
(2,2) -> A
(3,3) with k=5 -> A

Behind the scenes

How C processes this code

The C compiler produces a native program. Variables occupy memory, and execution enters main.

Cost and common mistakes

O(n log n) per query for n training points with this sort. A selection of the k nearest takes O(n).

  • The copy array has room for 8 points. A larger training set needs a larger array or dynamic allocation.
  • Ties between classes occur with an even k. Pick an odd k for two classes or define a tie-break.
  • The classifier compares the query with every training point, so it gets slow as the data grows.

Try it yourself

How would you handle three classes?

Reveal answer and reasoning

Keep a vote count per class, for example in an array indexed by label, and return the label with the largest count.

Further reading