diff --git a/src/avl.jl b/src/avl.jl index a3c3ece..7686591 100644 --- a/src/avl.jl +++ b/src/avl.jl @@ -29,7 +29,7 @@ end AbstractTrees.nodevalue(node::AVLNode) = node.value -AbstractTrees.NodeType(::Type{<:AVLNode}) = HasNodeType() +AbstractTrees.NodeType(::Type{<:AVLNode}) = AbstractTrees.HasNodeType() AbstractTrees.nodetype(T::Type{<:AVLNode}) = T function AbstractTrees.printnode(io::IO, node::AVLNode) @@ -109,9 +109,9 @@ function _insert!(root, key, value) elseif bf < -1 && key > root.right.key _leftrotate!(root) elseif bf > 1 && key > root.left.key - _rightrotate!(root) + _leftrightrotate!(root) elseif bf < -1 && key < root.right.key - _leftrotate!(root) + _rightleftrotate!(root) else root end @@ -146,11 +146,9 @@ function _delete!(root, key) elseif bf < -1 && _balancefactor(root.right) ≤ 0 _leftrotate!(root) elseif bf > 1 && _balancefactor(root.left) < 0 - root.left = _leftrotate!(root.left) - _rightrotate!(root) + _leftrightrotate!(root) elseif bf < -1 && _balancefactor(root.right) > 0 - root.right = _rightrotate!(root.right) - _leftrotate!(root) + _rightleftrotate!(root) else root end @@ -182,6 +180,16 @@ function _rightrotate!(node) A end +function _leftrightrotate!(node) + node.left = _leftrotate!(node.left) + _rightrotate!(node) +end + +function _rightleftrotate!(node) + node.right = _rightrotate!(node.right) + _leftrotate!(node) +end + function _updateheight!(node) node.height = 1 + max(_height(node.left), _height(node.right)) node diff --git a/test/Project.toml b/test/Project.toml index 0c36332..aa04d0c 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,2 +1,3 @@ [deps] +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/runtests.jl b/test/runtests.jl index 9a263a1..1feb548 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,47 +1,173 @@ using TreeDataStructures +using AbstractTrees using Test @testset "TreeDataStructures.jl" begin @testset "AVLTree" begin + # insert tree = AVLTree{Int,Int}() - tree[1] = 10 tree[2] = 20 + tree[1] = 10 tree[3] = 30 - @test tree[1] == 10 @test tree[2] == 20 + @test tree[1] == 10 @test tree[3] == 30 # value conversion tree = AVLTree{Int,Float64}() - tree[1] = 10 tree[2] = 20 + tree[1] = 10 tree[3] = 30 - @test tree[1] isa Float64 - @test tree[1] == 10.0 @test tree[2] isa Float64 @test tree[2] == 20.0 + @test tree[1] isa Float64 + @test tree[1] == 10.0 @test tree[3] isa Float64 @test tree[3] == 30.0 - # tree that accept any types + # update values + tree = AVLTree{Int,Int}() + tree[2] = 20 + tree[1] = 10 + tree[3] = 30 + @test tree[2] == 20 + @test tree[1] == 10 + @test tree[3] == 30 + tree[2] = 22 + tree[1] = 11 + tree[3] = 33 + @test tree[2] == 22 + @test tree[1] == 11 + @test tree[3] == 33 + + # right rotate + tree = AVLTree{Int,Int}() + tree[3] = 30 + tree[2] = 20 + tree[1] = 10 + @test tree.root.key == 2 + @test tree.root.left.key == 1 + @test tree.root.right.key == 3 + + # left rotate + tree = AVLTree{Int,Int}() + tree[1] = 10 + tree[2] = 20 + tree[3] = 30 + @test tree.root.key == 2 + @test tree.root.left.key == 1 + @test tree.root.right.key == 3 + + # left-right rotate + tree = AVLTree{Int,Int}() + tree[3] = 30 + tree[1] = 10 + tree[2] = 20 + @test tree.root.key == 2 + @test tree.root.left.key == 1 + @test tree.root.right.key == 3 + + # right-left rotate + tree = AVLTree{Int,Int}() + tree[1] = 10 + tree[3] = 30 + tree[2] = 20 + @test tree.root.key == 2 + @test tree.root.left.key == 1 + @test tree.root.right.key == 3 + + # delete + tree = AVLTree{Int,Int}() + tree[2] = 20 + tree[1] = 10 + tree[3] = 30 + delete!(tree, 3) + @test !isnothing(tree.root) + @test !isnothing(tree.root.left) + @test isnothing(tree.root.right) + delete!(tree, 1) + @test !isnothing(tree.root) + @test isnothing(tree.root.left) + @test isnothing(tree.root.right) + delete!(tree, 2) + @test isnothing(tree.root) + + # right rotate + tree = AVLTree{Int,Int}() + tree[4] = 40 + tree[2] = 20 + tree[5] = 50 + tree[1] = 10 + tree[3] = 30 + delete!(tree, 4) + @test tree.root.key == 2 + @test tree.root.left.key == 1 + @test tree.root.right.key == 5 + @test tree.root.right.left.key == 3 + + # left rotate + # TODO + + # left-right rotate + # TODO + + # right-left rotate + # TODO + + # tree that accepts any types tree = AVLTree() - tree[1] = 1.1 tree[2] = 'A' + tree[1] = 1.1 tree[3] = "test" - @test tree[1] == 1.1 @test tree[2] == 'A' + @test tree[1] == 1.1 @test tree[3] == "test" + delete!(tree, 3) + @test !isnothing(tree.root) + @test !isnothing(tree.root.left) + @test isnothing(tree.root.right) + delete!(tree, 1) + @test !isnothing(tree.root) + @test isnothing(tree.root.left) + @test isnothing(tree.root.right) + delete!(tree, 2) + @test isnothing(tree.root) + + # AbstractTrees interface + tree = AVLTree{Int,Int}() + tree[3] = 30 + tree[2] = 20 + tree[4] = 40 + tree[1] = 10 + tree[5] = 50 + root = tree.root + @test children(root) === (root.left, root.right) + @test children(root.left) === (root.left.left,) + @test children(root.right) === (root.right.right,) + @test children(root.left.left) === () + @test children(root.right.right) === () + @test nodevalue(root) == 30 + @test nodevalue(root.left) == 20 + @test nodevalue(root.right) == 40 + @test nodevalue(root.left.left) == 10 + @test nodevalue(root.right.right) == 50 + @test NodeType(root) === HasNodeType() + @test nodetype(root) === typeof(root) # show tree = AVLTree{Int,Int}() @test sprint(show, MIME("text/plain"), tree) == "AVLTree()" - tree[1] = 10 - tree[2] = 20 tree[3] = 30 + tree[2] = 20 + tree[4] = 40 + tree[1] = 10 + tree[5] = 50 @test sprint(show, MIME("text/plain"), tree) == """ AVLTree - 2 => 20 - ├─ 1 => 10 - └─ 3 => 30""" + 3 => 30 + ├─ 2 => 20 + │ └─ 1 => 10 + └─ 4 => 40 + └─ 5 => 50""" end end