diff --git a/config.json b/config.json index 45d681ab..77eb083f 100644 --- a/config.json +++ b/config.json @@ -26,8 +26,8 @@ "unlocked_by": null, "difficulty": 1, "topics": [ - "strings", - "arity" + "arity", + "strings" ] }, { @@ -841,6 +841,18 @@ "lists", "recursion" ] + }, + { + "slug": "binary-search-tree", + "uuid": "a8f7f17e-567b-43ec-9ee5-6288367f0ae4", + "core": false, + "unlocked_by": "strain", + "difficulty": 3, + "topics": [ + "recursion", + "searching", + "trees" + ] } ] } diff --git a/exercises/binary-search-tree/README.md b/exercises/binary-search-tree/README.md new file mode 100644 index 00000000..0e5531b5 --- /dev/null +++ b/exercises/binary-search-tree/README.md @@ -0,0 +1,84 @@ +# Binary Search Tree + +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 + +## Running tests + +In order to run the tests, issue the following command from the exercise +directory: + +For running the tests provided, `rebar3` is used as it is the official build and +dependency management tool for erlang now. Please refer to [the tracks installation +instructions](http://exercism.io/languages/erlang/installation) on how to do that. + +In order to run the tests, you can issue the following command from the exercise +directory. + +```bash +$ rebar3 eunit +``` + +## Questions? + +For detailed information about the Erlang track, please refer to the +[help page](http://exercism.io/languages/erlang) on the Exercism site. +This covers the basic information on setting up the development +environment expected by the exercises. + +## Source + +Josh Cheek [https://twitter.com/josh_cheek](https://twitter.com/josh_cheek) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/binary-search-tree/rebar.config b/exercises/binary-search-tree/rebar.config new file mode 100644 index 00000000..db5d9076 --- /dev/null +++ b/exercises/binary-search-tree/rebar.config @@ -0,0 +1,30 @@ +%% Erlang compiler options +{erl_opts, [debug_info, warnings_as_errors]}. + +{deps, [{erl_exercism, "0.1.2"}]}. + +{dialyzer, [ + {warnings, [underspecs, no_return]}, + {get_warnings, true}, + {plt_apps, top_level_deps}, % top_level_deps | all_deps + {plt_extra_apps, []}, + {plt_location, local}, % local | "/my/file/name" + {plt_prefix, "rebar3"}, + {base_plt_apps, [stdlib, kernel, crypto]}, + {base_plt_location, global}, % global | "/my/file/name" + {base_plt_prefix, "rebar3"} +]}. + +%% eunit:test(Tests) +{eunit_tests, []}. +%% Options for eunit:test(Tests, Opts) +{eunit_opts, [verbose]}. + +%% == xref == + +{xref_warnings, true}. + +%% xref checks to run +{xref_checks, [undefined_function_calls, undefined_functions, + locals_not_used, exports_not_used, + deprecated_function_calls, deprecated_functions]}. diff --git a/exercises/binary-search-tree/src/binary_search_tree.app.src b/exercises/binary-search-tree/src/binary_search_tree.app.src new file mode 100644 index 00000000..73eb2f6e --- /dev/null +++ b/exercises/binary-search-tree/src/binary_search_tree.app.src @@ -0,0 +1,9 @@ +{application, binary_search_tree, + [{description, "exercism.io - binary-search-tree"}, + {vsn, "1.0.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, + stdlib]}, + {env, []} + ]}. diff --git a/exercises/binary-search-tree/src/binary_search_tree.erl b/exercises/binary-search-tree/src/binary_search_tree.erl new file mode 100644 index 00000000..08d23b35 --- /dev/null +++ b/exercises/binary-search-tree/src/binary_search_tree.erl @@ -0,0 +1,23 @@ +-module(binary_search_tree). + +-export([empty/0, data/1, left/1, right/1, insert/2, sorted_data/1]). + +%% empty tree +empty() -> undefined. + +%% data at the top of _BST +data(_BST) -> undefined. + +%% left subtree +left(_BST) -> undefined. + +%% right subtree +right(_BST) -> undefined. + +%% insert _Value into _BST +insert(_BST, _Value) -> undefined. + +%% convert _BST to a sorted list +sorted_data(_BST) -> undefined. + + diff --git a/exercises/binary-search-tree/src/example.erl b/exercises/binary-search-tree/src/example.erl new file mode 100644 index 00000000..1d104fb5 --- /dev/null +++ b/exercises/binary-search-tree/src/example.erl @@ -0,0 +1,18 @@ +-module(example). + +-export([empty/0, data/1, left/1, right/1, insert/2, sorted_data/1]). + +empty() -> empty. + +data({V,_,_}) -> V. + +left({_, L,_}) -> L. + +right({_, _, R}) -> R. + +insert(empty, V) -> {V, empty, empty}; +insert({V1, L, R}, V2) when V1 < V2 -> {V1, L, insert(R, V2)}; +insert({V1, L, R}, V2) when V1 >= V2 -> {V1, insert(L, V2), R}. + +sorted_data(empty) -> []; +sorted_data({V, L, R}) -> sorted_data(L) ++ [V] ++ sorted_data(R). diff --git a/exercises/binary-search-tree/test/binary_search_tree_tests.erl b/exercises/binary-search-tree/test/binary_search_tree_tests.erl new file mode 100644 index 00000000..b22e2f16 --- /dev/null +++ b/exercises/binary-search-tree/test/binary_search_tree_tests.erl @@ -0,0 +1,80 @@ +%% These tests were manually created from canonical data, version 1.0.0 +%% (https://github.com/exercism/problem-specifications/blob/d5a4db7d282b16110e376f0ccfa91c80967b8b40/exercises/binary-search-tree/canonical-data.json) +-module(binary_search_tree_tests). + +-include_lib("erl_exercism/include/exercism.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +'1_data_is_retained_test'() -> + T = binary_search_tree:insert(binary_search_tree:empty(), 4), + ?assertMatch(4, binary_search_tree:data(T)). + +'2_smaller_number_at_left_node_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 4), + T2 = binary_search_tree:insert(T1, 2), + ?assertMatch(4, binary_search_tree:data(T2)), + ?assertMatch(2, binary_search_tree:data(binary_search_tree:left(T2))). + +'3_same number_at_left_node_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 4), + T2 = binary_search_tree:insert(T1, 4), + ?assertMatch(4, binary_search_tree:data(T2)), + ?assertMatch(4, binary_search_tree:data(binary_search_tree:left(T2))). + +'4_greater_number_at_right_node_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 4), + T2 = binary_search_tree:insert(T1, 5), + ?assertMatch(4, binary_search_tree:data(T2)), + ?assertMatch(5, binary_search_tree:data(binary_search_tree:right(T2))). + +'5_can_create_complex_tree_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 4), + T2 = binary_search_tree:insert(T1, 2), + T3 = binary_search_tree:insert(T2, 6), + T4 = binary_search_tree:insert(T3, 1), + T5 = binary_search_tree:insert(T4, 3), + T6 = binary_search_tree:insert(T5, 5), + T7 = binary_search_tree:insert(T6, 7), + ?assertMatch(4, binary_search_tree:data(T7)), + ?assertMatch(2, binary_search_tree:data(binary_search_tree:left(T7))), + ?assertMatch(1, binary_search_tree:data(binary_search_tree:left(binary_search_tree:left(T7)))), + ?assertMatch(3, binary_search_tree:data(binary_search_tree:right(binary_search_tree:left(T7)))), + ?assertMatch(6, binary_search_tree:data(binary_search_tree:right(T7))), + ?assertMatch(5, binary_search_tree:data(binary_search_tree:left(binary_search_tree:right(T7)))), + ?assertMatch(7, binary_search_tree:data(binary_search_tree:right(binary_search_tree:right(T7)))). + +'6_can_sort_single_number_test'() -> + T = binary_search_tree:insert(binary_search_tree:empty(), 2), + ?assertMatch([2], binary_search_tree:sorted_data(T)). + +'7_can_sort_if_second_number_is_smaller_than_first_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 2), + T2 = binary_search_tree:insert(T1, 1), + ?assertMatch([1, 2], binary_search_tree:sorted_data(T2)). + +'8_can_sort_if_second_number_is_same_as_first_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 2), + T2 = binary_search_tree:insert(T1, 2), + ?assertMatch([2, 2], binary_search_tree:sorted_data(T2)). + +'9_can_sort_if_second_number_is_greater_than_first_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 2), + T2 = binary_search_tree:insert(T1, 3), + ?assertMatch([2, 3], binary_search_tree:sorted_data(T2)). + +'10_can_sort_complex_tree_test'() -> + T0 = binary_search_tree:empty(), + T1 = binary_search_tree:insert(T0, 2), + T2 = binary_search_tree:insert(T1, 1), + T3 = binary_search_tree:insert(T2, 3), + T4 = binary_search_tree:insert(T3, 6), + T5 = binary_search_tree:insert(T4, 7), + T6 = binary_search_tree:insert(T5, 5), + ?assertMatch([1, 2, 3, 5, 6, 7], binary_search_tree:sorted_data(T6)).