From 2695070e9a50b218c9de19cbd9c590be224ccd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:37:38 -0700 Subject: [PATCH] Add `binary-search-tree` --- config.json | 8 ++ .../binary-search-tree/.docs/instructions.md | 70 ++++++++++++ .../binary-search-tree/.meta/config.json | 18 ++++ .../binary-search-tree/.meta/example.vim | 42 ++++++++ .../binary-search-tree/.meta/tests.toml | 40 +++++++ .../binary_search_tree.vader | 101 ++++++++++++++++++ .../binary-search-tree/binary_search_tree.vim | 11 ++ lib/generate.vim | 55 +++++++++- 8 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/binary-search-tree/.docs/instructions.md create mode 100644 exercises/practice/binary-search-tree/.meta/config.json create mode 100644 exercises/practice/binary-search-tree/.meta/example.vim create mode 100644 exercises/practice/binary-search-tree/.meta/tests.toml create mode 100644 exercises/practice/binary-search-tree/binary_search_tree.vader create mode 100644 exercises/practice/binary-search-tree/binary_search_tree.vim diff --git a/config.json b/config.json index 4e6708dc..443affa0 100644 --- a/config.json +++ b/config.json @@ -654,6 +654,14 @@ "strings" ] }, + { + "slug": "binary-search-tree", + "name": "Binary Search Tree", + "uuid": "4e8df8d7-2ca6-4c2b-9e0f-1f6a05ac8c57", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "camicia", "name": "Camicia", diff --git a/exercises/practice/binary-search-tree/.docs/instructions.md b/exercises/practice/binary-search-tree/.docs/instructions.md new file mode 100644 index 00000000..7625220e --- /dev/null +++ b/exercises/practice/binary-search-tree/.docs/instructions.md @@ -0,0 +1,70 @@ +# Instructions + +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: + +![A graph with root node 4 and a single child node 2.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2.svg) + +```text + 4 + / + 2 +``` + +If we then added 6, it would look like this: + +![A graph with root node 4 and two child nodes 2 and 6.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6.svg) + +```text + 4 + / \ + 2 6 +``` + +If we then added 3, it would look like this + +![A graph with root node 4, two child nodes 2 and 6, and a grandchild node 3.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6-3.svg) + +```text + 4 + / \ + 2 6 + \ + 3 +``` + +And if we then added 1, 5, and 7, it would look like this + +![A graph with root node 4, two child nodes 2 and 6, and four grandchild nodes 1, 3, 5 and 7.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6-1-3-5-7.svg) + +```text + 4 + / \ + / \ + 2 6 + / \ / \ + 1 3 5 7 +``` + +## Credit + +The images were created by [habere-et-dispertire][habere-et-dispertire] using [PGF/TikZ][pgf-tikz] by Till Tantau. + +[habere-et-dispertire]: https://exercism.org/profiles/habere-et-dispertire +[pgf-tikz]: https://en.wikipedia.org/wiki/PGF/TikZ diff --git a/exercises/practice/binary-search-tree/.meta/config.json b/exercises/practice/binary-search-tree/.meta/config.json new file mode 100644 index 00000000..ef3478b6 --- /dev/null +++ b/exercises/practice/binary-search-tree/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "binary_search_tree.vim" + ], + "test": [ + "binary_search_tree.vader" + ], + "example": [ + ".meta/example.vim" + ] + }, + "blurb": "Insert and search for numbers in a binary tree.", + "source": "Josh Cheek" +} diff --git a/exercises/practice/binary-search-tree/.meta/example.vim b/exercises/practice/binary-search-tree/.meta/example.vim new file mode 100644 index 00000000..668c932e --- /dev/null +++ b/exercises/practice/binary-search-tree/.meta/example.vim @@ -0,0 +1,42 @@ +function! Data(treeData) abort + if empty(a:treeData) + return v:null + endif + + let l:tree = s:Node(a:treeData[0]) + for l:value in a:treeData[1:] + let l:tree = s:Insert(l:tree, l:value) + endfor + + return l:tree +endfunction + +function! SortedData(treeData) abort + return s:InOrder(Data(a:treeData)) +endfunction + +function! s:Insert(tree, value) abort + if a:tree is v:null + return s:Node(a:value) + endif + + if a:value <=# a:tree.data + let a:tree.left = s:Insert(a:tree.left, a:value) + else + let a:tree.right = s:Insert(a:tree.right, a:value) + endif + + return a:tree +endfunction + +function! s:Node(value) abort + return {'data': a:value, 'left': v:null, 'right': v:null} +endfunction + +function! s:InOrder(tree) abort + if a:tree is v:null + return [] + endif + + return s:InOrder(a:tree.left) + [a:tree.data] + s:InOrder(a:tree.right) +endfunction diff --git a/exercises/practice/binary-search-tree/.meta/tests.toml b/exercises/practice/binary-search-tree/.meta/tests.toml new file mode 100644 index 00000000..10ea6e18 --- /dev/null +++ b/exercises/practice/binary-search-tree/.meta/tests.toml @@ -0,0 +1,40 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[e9c93a78-c536-4750-a336-94583d23fafa] +description = "data is retained" + +[7a95c9e8-69f6-476a-b0c4-4170cb3f7c91] +description = "smaller number at left node" + +[22b89499-9805-4703-a159-1a6e434c1585] +description = "same number at left node" + +[2e85fdde-77b1-41ed-b6ac-26ce6b663e34] +description = "greater number at right node" + +[dd898658-40ab-41d0-965e-7f145bf66e0b] +description = "can create complex tree" + +[9e0c06ef-aeca-4202-b8e4-97f1ed057d56] +description = "can sort single number" + +[425e6d07-fceb-4681-a4f4-e46920e380bb] +description = "can sort if second number is smaller than first" + +[bd7532cc-6988-4259-bac8-1d50140079ab] +description = "can sort if second number is same as first" + +[b6d1b3a5-9d79-44fd-9013-c83ca92ddd36] +description = "can sort if second number is greater than first" + +[d00ec9bd-1288-4171-b968-d44d0808c1c8] +description = "can sort complex tree" diff --git a/exercises/practice/binary-search-tree/binary_search_tree.vader b/exercises/practice/binary-search-tree/binary_search_tree.vader new file mode 100644 index 00000000..2e8f3958 --- /dev/null +++ b/exercises/practice/binary-search-tree/binary_search_tree.vader @@ -0,0 +1,101 @@ +Execute (data is retained): + let g:treeData = ['4'] + let g:expected = {'data': '4', 'left': v:null, 'right': v:null} + AssertEqual g:expected, Data(g:treeData) + +Execute (smaller number at left node): + let g:treeData = ['4', '2'] + let g:expected = { + \ 'data': '4', + \ 'left': { + \ 'data': '2', + \ 'left': v:null, + \ 'right': v:null + \ }, + \ 'right': v:null + \ } + AssertEqual g:expected, Data(g:treeData) + +Execute (same number at left node): + let g:treeData = ['4', '4'] + let g:expected = { + \ 'data': '4', + \ 'left': { + \ 'data': '4', + \ 'left': v:null, + \ 'right': v:null + \ }, + \ 'right': v:null + \ } + AssertEqual g:expected, Data(g:treeData) + +Execute (greater number at right node): + let g:treeData = ['4', '5'] + let g:expected = { + \ 'data': '4', + \ 'left': v:null, + \ 'right': { + \ 'data': '5', + \ 'left': v:null, + \ 'right': v:null + \ } + \ } + AssertEqual g:expected, Data(g:treeData) + +Execute (can create complex tree): + let g:treeData = ['4', '2', '6', '1', '3', '5', '7'] + let g:expected = { + \ 'data': '4', + \ 'left': { + \ 'data': '2', + \ 'left': { + \ 'data': '1', + \ 'left': v:null, + \ 'right': v:null + \ }, + \ 'right': { + \ 'data': '3', + \ 'left': v:null, + \ 'right': v:null + \ } + \ }, + \ 'right': { + \ 'data': '6', + \ 'left': { + \ 'data': '5', + \ 'left': v:null, + \ 'right': v:null + \ }, + \ 'right': { + \ 'data': '7', + \ 'left': v:null, + \ 'right': v:null + \ } + \ } + \ } + AssertEqual g:expected, Data(g:treeData) + +Execute (can sort single number): + let g:treeData = ['2'] + let g:expected = ['2'] + AssertEqual g:expected, SortedData(g:treeData) + +Execute (can sort if second number is smaller than first): + let g:treeData = ['2', '1'] + let g:expected = ['1', '2'] + AssertEqual g:expected, SortedData(g:treeData) + +Execute (can sort if second number is same as first): + let g:treeData = ['2', '2'] + let g:expected = ['2', '2'] + AssertEqual g:expected, SortedData(g:treeData) + +Execute (can sort if second number is greater than first): + let g:treeData = ['2', '3'] + let g:expected = ['2', '3'] + AssertEqual g:expected, SortedData(g:treeData) + +Execute (can sort complex tree): + let g:treeData = ['2', '1', '3', '6', '7', '5'] + let g:expected = ['1', '2', '3', '5', '6', '7'] + AssertEqual g:expected, SortedData(g:treeData) diff --git a/exercises/practice/binary-search-tree/binary_search_tree.vim b/exercises/practice/binary-search-tree/binary_search_tree.vim new file mode 100644 index 00000000..ce91d5f0 --- /dev/null +++ b/exercises/practice/binary-search-tree/binary_search_tree.vim @@ -0,0 +1,11 @@ +" +" Insert a sequence of values into a binary search tree and return the tree +" or its data in sorted order. +" +function! Data(treeData) abort + " your solution goes here +endfunction + +function! SortedData(treeData) abort + " your solution goes here +endfunction diff --git a/lib/generate.vim b/lib/generate.vim index 68d0771e..1aeb9e39 100644 --- a/lib/generate.vim +++ b/lib/generate.vim @@ -113,8 +113,59 @@ function! s:filter_test_cases(cases, excluded_uuids) abort return filtered endfunction -function! s:generate_variable(name, value) - call append(line('$'), printf(' let g:%s = %s', a:name, string(a:value))) +function! s:dict_literal(dict) abort + if empty(a:dict) + return ['{}'] + endif + + let lines = ['{'] + let key_names = sort(keys(a:dict)) + let last_key = key_names[-1] + + for key_name in key_names + let key_fragment = string(key_name) . ': ' + let value = a:dict[key_name] + + if type(value) ==# type({}) + let entry_lines = s:dict_literal(value) + let entry_lines[0] = key_fragment . entry_lines[0] + else + let entry_lines = [key_fragment . string(value)] + endif + + let entry_lines = s:indent_lines(entry_lines) + if key_name !=# last_key + let entry_lines[-1] .= ',' + endif + call extend(lines, entry_lines) + endfor + + call add(lines, '}') + return lines +endfunction + +function! s:indent_lines(lines) abort + let indented = [] + for line in a:lines + call add(indented, ' ' . line) + endfor + return indented +endfunction + +function! s:generate_variable(name, value) abort + let binding = printf(' let g:%s = ', a:name) + let inline = binding . string(a:value) + + if strdisplaywidth(inline) <= 80 || type(a:value) !=# type({}) + call append(line('$'), inline) + return + endif + + let value_lines = s:dict_literal(a:value) + call append(line('$'), binding . value_lines[0]) + for line in value_lines[1:] + call append(line('$'), ' \ ' . line) + endfor endfunction function! s:generate_assert(test, arguments) abort