Skip to content

Commit

Permalink
Implement exercise 'binary-search-tree' (#287)
Browse files Browse the repository at this point in the history
* Implement exercise 'binary-search-tree'

* remove redundant qsort
  • Loading branch information
vlzware authored and ryanplusplus committed Mar 16, 2018
1 parent 66ee0ad commit decd682
Show file tree
Hide file tree
Showing 9 changed files with 2,701 additions and 0 deletions.
13 changes: 13 additions & 0 deletions config.json
Expand Up @@ -629,6 +629,19 @@
],
"unlocked_by": "beer-song",
"uuid": "c1392944-08fd-45c3-b508-41d682f832d3"
},
{
"core": false,
"difficulty": 6,
"slug": "binary-search-tree",
"topics": [
"structs",
"arrays",
"recursion",
"control_flow_loops"
],
"unlocked_by": "binary-search",
"uuid": "1b33f528-8f29-4582-a268-07be2d1a4516"
}
],
"foregone": [],
Expand Down
53 changes: 53 additions & 0 deletions exercises/binary-search-tree/README.md
@@ -0,0 +1,53 @@
Insert and search for numbers in a binary tree.

When we need to represent sorted data, an array does not make a good
data structure.

Say we have the array `[1, 3, 4, 5]`, and we add 2 to it so it becomes
`[1, 3, 4, 5, 2]` now we must sort the entire array again! We can
improve on this by realizing that we only need to make space for the new
item `[1, nil, 3, 4, 5]`, and then adding the item in the space we
added. But this still requires us to shift many elements down by one.

Binary Search Trees, however, can operate on sorted data much more
efficiently.

A binary search tree consists of a series of connected nodes. Each node
contains a piece of data (e.g. the number 3), a variable named `left`,
and a variable named `right`. The `left` and `right` variables point at
`nil`, or other nodes. Since these other nodes in turn have other nodes
beneath them, we say that the left and right variables are pointing at
subtrees. All data in the left subtree is less than or equal to the
current node's data, and all data in the right subtree is greater than
the current node's data.

For example, if we had a node containing the data 4, and we added the
data 2, our tree would look like this:

4
/
2

If we then added 6, it would look like this:

4
/ \
2 6

If we then added 3, it would look like this

4
/ \
2 6
\
3

And if we then added 1, 5, and 7, it would look like this

4
/ \
/ \
2 6
/ \ / \
1 3 5 7

25 changes: 25 additions & 0 deletions exercises/binary-search-tree/makefile
@@ -0,0 +1,25 @@
CFLAGS = -std=c99
CFLAGS += -g
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -pedantic
CFLAGS += -Werror

VFLAGS = --quiet
VFLAGS += --tool=memcheck
VFLAGS += --leak-check=full
VFLAGS += --error-exitcode=1

test: tests.out
@./tests.out

memcheck: tests.out
@valgrind $(VFLAGS) ./tests.out
@echo "Memory check passed"

clean:
rm -rf *.o *.out *.out.dSYM

tests.out: test/test_binary_search_tree.c src/binary_search_tree.c src/binary_search_tree.h
@echo Compiling $@
@cc $(CFLAGS) src/binary_search_tree.c test/vendor/unity.c test/test_binary_search_tree.c -o tests.out
102 changes: 102 additions & 0 deletions exercises/binary-search-tree/src/example.c
@@ -0,0 +1,102 @@
#include "binary_search_tree.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#define CHUNK_SIZE 100
static bool mem_ok;
static int *parsed_tree;
static size_t parsed_len;

static void free_tree(node_t * head)
{
if (head == NULL)
return;
free_tree(head->right);
free_tree(head->left);
free(head);
}

static node_t *add_node(node_t * head, int data)
{
if (!mem_ok)
return NULL;

if (head == NULL) {
head = malloc(sizeof(node_t));
if (head == NULL) {
fprintf(stderr, "Memory error!\n");
mem_ok = false;
return NULL;
}
head->data = data;
head->left = NULL;
head->right = NULL;

} else if (head->data >= data) {
head->left = add_node(head->left, data);

} else if (head->data < data) {
head->right = add_node(head->right, data);

}
return head;
}

static void walk_tree(node_t * head)
{
if (head == NULL || !mem_ok)
return;

walk_tree(head->left);
parsed_tree[parsed_len++] = head->data;
if (parsed_len % CHUNK_SIZE == 0) {
parsed_tree =
realloc(parsed_tree, sizeof(int) * (parsed_len + CHUNK_SIZE));
if (parsed_tree == NULL) {
fprintf(stderr, "Memory error!\n");
mem_ok = false;
return;
}
}
walk_tree(head->right);
}

node_t *build_tree(int *tree_data, size_t tree_data_len)
{
if (tree_data == NULL || tree_data_len == 0)
return NULL;

node_t *head = NULL;
mem_ok = true;
for (size_t i = 0; i < tree_data_len; i++) {
node_t *tmp = add_node(head, tree_data[i]);
if (tmp == NULL) {
free_tree(head);
return NULL;
}
head = tmp;
}

return head;
}

int *sorted_data(node_t * head)
{
if (head == NULL)
return NULL;

parsed_tree = malloc(sizeof(int) * CHUNK_SIZE);
if (parsed_tree == NULL) {
fprintf(stderr, "Memory error!\n");
return NULL;
}

mem_ok = true;
parsed_len = 0;
walk_tree(head);
if (!mem_ok)
return NULL;

return parsed_tree;
}
16 changes: 16 additions & 0 deletions exercises/binary-search-tree/src/example.h
@@ -0,0 +1,16 @@
#ifndef BINARY_SEARCH_TREE_H
#define BINARY_SEARCH_TREE_H
#include <stddef.h>

typedef struct node node_t;

struct node {
node_t *right;
node_t *left;
int data;
};

node_t *build_tree(int *tree_data, size_t tree_data_len);
int *sorted_data(node_t * tree);

#endif

0 comments on commit decd682

Please sign in to comment.