Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement exercise 'binary-search-tree' (#287)
* Implement exercise 'binary-search-tree' * remove redundant qsort
- Loading branch information
1 parent
66ee0ad
commit decd682
Showing
9 changed files
with
2,701 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.