diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..e7e7cb3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,6 @@ +image: Visual Studio 2019 + +build_script: + - For /R %%I in (*.sln) do dotnet test %%I + +test: off \ No newline at end of file diff --git a/hw3B-tree/hw3B-tree.Test/TestB-tree.cs b/hw3B-tree/hw3B-tree.Test/TestB-tree.cs new file mode 100644 index 0000000..9a0acf6 --- /dev/null +++ b/hw3B-tree/hw3B-tree.Test/TestB-tree.cs @@ -0,0 +1,187 @@ +using NUnit.Framework; + +namespace Hw3B_tree.Test +{ + public class Tests + { + private BTree tree; + + public BTree Setup(int minimumDegreeOfTree) + { + tree = new BTree(minimumDegreeOfTree); + for (int i = 1; i < 19; ++i) + { + tree.Insert(i.ToString(), i.ToString()); + } + return tree; + } + + [TestCase] + public void CheckInsert() + { + var tree = Setup(2); + for (int i = 1; i <= 18; ++i) + { + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckGetValue() + { + var tree = Setup(2); + for (int i = 1; i <= 18; ++i) + { + Assert.AreEqual(i.ToString(), tree.GetValue(i.ToString())); + } + } + + [TestCase] + public void CheckChangeValueByKey() + { + var tree = Setup(2); + tree.ChangeValueByKey("5", "ololo"); + Assert.AreEqual("ololo", tree.GetValue("5")); + } + + [TestCase] + public void CheckDeleteFromRoot() + { + var tree = Setup(2); + tree.Delete("8"); + for (int i = 1; i <= 18; ++i) + { + if (i == 8) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckDeleteFromLeaf() + { + var tree = Setup(2); + tree.Delete("5"); + for (int i = 1; i <= 18; ++i) + { + if (i == 5) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckDeleteFromNotLeafWithChangeRoot() + { + var tree = Setup(2); + tree.Delete("6"); + for (int i = 1; i <= 18; ++i) + { + if (i == 6) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckDeleteFromLeafWithChangeValueInLeafs() + { + var tree = Setup(2); + tree.Delete("15"); + for (int i = 1; i <= 18; ++i) + { + if (i == 15) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckInsertWithAnotherDegree() + { + var tree = Setup(4); + for (int i = 1; i <= 18; ++i) + { + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckGetValueWithAnotherDegree() + { + var tree = Setup(4); + for (int i = 1; i <= 18; ++i) + { + Assert.AreEqual(i.ToString(), tree.GetValue(i.ToString())); + } + } + + [TestCase] + public void CheckChangeValueByKeyWithAnotherDegree() + { + var tree = Setup(4); + tree.ChangeValueByKey("5", "ololo"); + Assert.AreEqual("ololo", tree.GetValue("5")); + } + + [TestCase] + public void CheckDeleteFromRootWithAnotherDegree() + { + var tree = Setup(4); + tree.Delete("8"); + for (int i = 1; i <= 18; ++i) + { + if (i == 8) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckDeleteFromLeafWithMergeLefsWithAnotherDegree() + { + var tree = Setup(4); + tree.Delete("2"); + for (int i = 1; i <= 18; ++i) + { + if (i == 2) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + + [TestCase] + public void CheckDeleteFromLeafWithChangeValueInLeafsWithAnotherDegree() + { + var tree = Setup(4); + tree.Delete("10"); + for (int i = 1; i <= 18; ++i) + { + if (i == 10) + { + Assert.IsFalse(tree.Exists(i.ToString())); + continue; + } + Assert.IsTrue(tree.Exists(i.ToString())); + } + } + } +} \ No newline at end of file diff --git a/hw3B-tree/hw3B-tree.Test/hw3B-tree.Test.csproj b/hw3B-tree/hw3B-tree.Test/hw3B-tree.Test.csproj new file mode 100644 index 0000000..953b990 --- /dev/null +++ b/hw3B-tree/hw3B-tree.Test/hw3B-tree.Test.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + hw3B_tree.Test + + false + + + + + + + + + + + + + diff --git a/hw3B-tree/hw3B-tree.sln b/hw3B-tree/hw3B-tree.sln new file mode 100644 index 0000000..6ea80e4 --- /dev/null +++ b/hw3B-tree/hw3B-tree.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hw3B-tree", "hw3B-tree\Hw3B-tree.csproj", "{53642C16-3AA3-4A57-8380-46B9C2E9651E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hw3B-tree.Test", "hw3B-tree.Test\hw3B-tree.Test.csproj", "{A2EE96D2-650B-457E-87E5-A6410F6DF06D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {53642C16-3AA3-4A57-8380-46B9C2E9651E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53642C16-3AA3-4A57-8380-46B9C2E9651E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53642C16-3AA3-4A57-8380-46B9C2E9651E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53642C16-3AA3-4A57-8380-46B9C2E9651E}.Release|Any CPU.Build.0 = Release|Any CPU + {A2EE96D2-650B-457E-87E5-A6410F6DF06D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2EE96D2-650B-457E-87E5-A6410F6DF06D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2EE96D2-650B-457E-87E5-A6410F6DF06D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2EE96D2-650B-457E-87E5-A6410F6DF06D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A28793F3-951B-4D93-BC1D-F351834FA2F5} + EndGlobalSection +EndGlobal diff --git a/hw3B-tree/hw3B-tree/BTree.cs b/hw3B-tree/hw3B-tree/BTree.cs new file mode 100644 index 0000000..b29e31b --- /dev/null +++ b/hw3B-tree/hw3B-tree/BTree.cs @@ -0,0 +1,465 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Hw3B_tree +{ + /// + /// btree class + /// + public class BTree + { + public int MinimumDegreeOfTree { get; } + + private class Node + { + + public int CountKeys { get; set; } + public (string key, string value)[] Keys { get; set; } + + public Node[] Sons { get; set; } + + public bool Leaf { get; set; } + + public Node(int minimumDegreeOfTree, bool leaf) + { + CountKeys = 0; + Leaf = leaf; + Keys = new (string key, string value)[2 * minimumDegreeOfTree - 1]; + Sons = new Node[2 * minimumDegreeOfTree]; + } + } + + private Node root; + + public BTree(int minimumDegreeOfTree) + { + if (minimumDegreeOfTree < 2) + { + throw new ArgumentException("Минимальная степень дерева выбрана неправильно!"); + } + MinimumDegreeOfTree = minimumDegreeOfTree; + } + + private Node FindNode(string key) + { + var node = root; + while (!node.Leaf) + { + for (int i = 0; i < node.CountKeys; ++i) + { + if (key == node.Keys[i].key) + { + return node; + } + if (CompareKey(key, node.Keys[i].key)) + { + node = node.Sons[i]; + break; + } + if (i == node.CountKeys - 1) + { + node = node.Sons[i + 1]; + break; + } + } + } + for (int i = 0; i < node.CountKeys; ++i) + { + if (key == node.Keys[i].key) + { + return node; + } + } + return null; + } + + /// + /// checking for the presence of a key in a tree + /// + /// true - if key is in tree, false - if key isn't in tree + public bool Exists(string key) + { + var node = FindNode(key); + if (node == null) + { + return false; + } + for (int i = 0; i < node.CountKeys; ++i) + { + if (key == node.Keys[i].key) + { + return true; + } + } + return false; + } + + /// + /// get value by key + /// + /// return value, if we find key and return null, if we don't find key + public string GetValue(string key) + { + var node = FindNode(key); + if (node == null) + { + return null; + } + for (int i = 0; i < node.CountKeys; ++i) + { + if (key == node.Keys[i].key) + { + return node.Keys[i].value; + } + } + return null; + } + + /// + /// chenge value in tree by key + /// + /// return true,if we change value, and false, if we don't change + public bool ChangeValueByKey(string key, string value) + { + var node = FindNode(key); + if (node == null) + { + return false; + } + for (int i = 0; i < node.CountKeys; ++i) + { + if (key == node.Keys[i].key) + { + node.Keys[i].value = value; + return true; + } + } + return false; + } + + public bool CompareKey(string key, string keyInNode) + { + if (key.Length < keyInNode.Length) + { + return true; + } + else if (key.Length > keyInNode.Length) + { + return false; + } + return String.Compare(key, keyInNode) < 0; + } + + /// + /// function of insert (key,value) in tree + /// + public void Insert(string key, string value) + { + var runner = root; + if (root == null) + { + root = new Node(MinimumDegreeOfTree, true); + root.Keys[0].key = key; + root.Keys[0].value = value; + root.CountKeys++; + } + else if (root.CountKeys == (2 * MinimumDegreeOfTree) - 1) + { + var newRoot = new Node(MinimumDegreeOfTree, false); + newRoot.Sons[newRoot.CountKeys] = root; + root = newRoot; + Split(0, root); + InsertInNode(key, value, root); + } + else + { + InsertInNode(key, value, runner); + } + } + + private void Split(int index, Node node) + { + var helpNode = node.Sons[index]; + var newNode = new Node(MinimumDegreeOfTree, helpNode.Leaf); + newNode.CountKeys = MinimumDegreeOfTree - 1; + Array.Copy(helpNode.Keys, MinimumDegreeOfTree, newNode.Keys, 0, MinimumDegreeOfTree - 1); + if (!node.Leaf) + { + Array.Copy(helpNode.Sons, MinimumDegreeOfTree, newNode.Sons, 0, MinimumDegreeOfTree); + } + helpNode.CountKeys = MinimumDegreeOfTree - 1; + for (int i = node.CountKeys; i >= index + 1; i--) + { + node.Sons[i + 1] = node.Sons[i]; + } + node.Sons[index + 1] = newNode; + for (int i = node.CountKeys - 1; i >= index; i--) + { + node.Keys[i + 1].key = node.Keys[i].key; + node.Keys[i + 1].value = node.Keys[i].value; + } + node.Keys[index].key = helpNode.Keys[MinimumDegreeOfTree - 1].key; + node.Keys[index].value = helpNode.Keys[MinimumDegreeOfTree - 1].value; + node.CountKeys++; + } + + private void InsertInNode(string key, string value, Node node) + { + int index = node.CountKeys - 1; + if (node.Leaf) + { + while (index >= 0 && CompareKey(key, node.Keys[index].key)) + { + node.Keys[index + 1].key = node.Keys[index].key; + node.Keys[index + 1].value = node.Keys[index].value; + index--; + } + node.Keys[index + 1].key = key; + node.Keys[index + 1].value = value; + node.CountKeys++; + } + else + { + while (index >= 0 && CompareKey(key, node.Keys[index].key)) + { + index--; + } + index++; + if (node.Sons[index].CountKeys == (2 * MinimumDegreeOfTree - 1)) + { + Split(index, node); + if (!CompareKey(key, node.Keys[index].key)) + { + index++; + } + } + InsertInNode(key, value, node.Sons[index]); + } + } + + /// + /// finction delete (key, value) from tree + /// + public void Delete(string key) + { + var runner = root; + if (root == null) + { + return; + } + Remove(key, ref runner); + if (root.CountKeys == 0) + { + root = root.Leaf ? null : root.Sons[0]; + } + } + + private int FindKeyIndex(string key, Node node) + { + var index = 0; + while (index < node.CountKeys && CompareKey(node.Keys[index].key, key)) + { + index++; + } + return index; + } + + private void Remove(string key, ref Node cursor) + { + var index = FindKeyIndex(key, cursor); + if (index < cursor.CountKeys && cursor.Keys[index].key == key) + { + if (cursor.Leaf) + { + DeleteFromLeaf(index, cursor); + } + else + { + DeleteFromNotLeaf(index, cursor); + } + } + else + { + if (cursor.Leaf) + { + return; + } + bool check = index == cursor.CountKeys; + var helpNode = cursor.Sons[index]; + if (helpNode.CountKeys < MinimumDegreeOfTree) + { + Fill(index, ref cursor); + } + if (check && index < cursor.CountKeys) + { + cursor = cursor.Sons[index - 1]; + Remove(key, ref cursor); + } + else + { + cursor = cursor.Sons[index]; + Remove(key, ref cursor); + } + } + } + + private void DeleteFromLeaf(int index, Node node) + { + for (int i = index + 1; i < node.CountKeys; ++i) + { + node.Keys[i - 1] = node.Keys[i]; + } + node.CountKeys--; + } + + private void DeleteFromNotLeaf(int index, Node node) + { + string key = node.Keys[index].key; + var cursor = node; + if (node.Sons[index].CountKeys >= MinimumDegreeOfTree) + { + (string previousKey, string value) = GetPrev(index, cursor); + node.Keys[index].key = previousKey; + node.Keys[index].value = value; + cursor = cursor.Sons[index]; + Remove(previousKey, ref cursor); + } + else if (node.Sons[index + 1].CountKeys >= MinimumDegreeOfTree) + { + (string succKey, string value) = GetSucc(index, cursor); + node.Keys[index].key = succKey; + node.Keys[index].value = value; + cursor = node.Sons[index + 1]; + Remove(succKey, ref cursor); + } + else + { + Merge(index, ref cursor); + cursor = node.Sons[index]; + Remove(key, ref cursor); + } + } + + private (string, string) GetPrev(int index, Node node) + { + var helpNode = node.Sons[index]; + while (!helpNode.Leaf) + { + helpNode = helpNode.Sons[helpNode.CountKeys]; + } + return (helpNode.Keys[helpNode.CountKeys - 1].key, helpNode.Keys[helpNode.CountKeys - 1].value); + } + + private (string, string) GetSucc(int index, Node node) + { + var helpNode = node.Sons[index + 1]; + while (!helpNode.Leaf) + { + helpNode = helpNode.Sons[0]; + } + return (helpNode.Keys[0].key, helpNode.Keys[0].value); + } + + private void Fill(int index, ref Node cursor) + { + if (index != 0 && cursor.Sons[index - 1].CountKeys >= MinimumDegreeOfTree) + { + TakeKeyInPrev(index, ref cursor); + } + else if (index != cursor.CountKeys && cursor.Sons[index + 1].CountKeys >= MinimumDegreeOfTree) + { + TakeKeyInNext(index, ref cursor); + } + else + { + if (index != cursor.CountKeys) + { + Merge(index, ref cursor); + } + else + { + Merge(index - 1, ref cursor); + } + } + } + + private void TakeKeyInPrev(int index, ref Node cursor) + { + var child = cursor.Sons[index]; + var siblings = cursor.Sons[index - 1]; + for (int i = child.CountKeys - 1; i >= 0; --i) + { + child.Keys[i + 1] = child.Keys[i]; + } + if (!child.Leaf) + { + for (int i = child.CountKeys; i >= 0; i--) + { + child.Sons[i + 1] = child.Sons[i]; + } + } + child.Keys[0] = cursor.Keys[index - 1]; + if (!child.Leaf) + { + child.Sons[0] = siblings.Sons[siblings.CountKeys]; + } + cursor.Keys[index - 1] = siblings.Keys[siblings.CountKeys - 1]; + child.CountKeys++; + siblings.CountKeys--; + } + + private void TakeKeyInNext(int index, ref Node cursor) + { + var child = cursor.Sons[index]; + var siblings = cursor.Sons[index + 1]; + child.Keys[child.CountKeys] = cursor.Keys[index]; + if (!child.Leaf) + { + child.Sons[child.CountKeys + 1] = siblings.Sons[0]; + } + cursor.Keys[index] = siblings.Keys[0]; + for (int i = 1; i < siblings.CountKeys; ++i) + { + siblings.Keys[i - 1] = siblings.Keys[i]; + } + if (!siblings.Leaf) + { + for (int i = 1; i <= siblings.CountKeys; ++i) + { + siblings.Sons[i - 1] = siblings.Sons[i]; + } + } + child.CountKeys++; + siblings.CountKeys--; + } + + private void Merge(int index, ref Node cursor) + { + var child = cursor.Sons[index]; + var siblings = cursor.Sons[index + 1]; + child.Keys[MinimumDegreeOfTree - 1] = cursor.Keys[index]; + for (int i = 0; i < siblings.CountKeys; i++) + { + child.Keys[i + MinimumDegreeOfTree] = siblings.Keys[i]; + } + if (!child.Leaf) + { + for (int i = 0; i <= siblings.CountKeys; i++) + { + child.Sons[i + MinimumDegreeOfTree] = siblings.Sons[i]; + } + } + for (int i = index + 1; i < cursor.CountKeys; i++) + { + cursor.Keys[i - 1] = cursor.Keys[i]; + } + for (int i = index + 2; i <= cursor.CountKeys; i++) + { + cursor.Sons[i - 1] = cursor.Sons[i]; + } + child.CountKeys += siblings.CountKeys + 1; + cursor.CountKeys--; + } + } +} \ No newline at end of file diff --git a/hw3B-tree/hw3B-tree/Program.cs b/hw3B-tree/hw3B-tree/Program.cs new file mode 100644 index 0000000..68678b7 --- /dev/null +++ b/hw3B-tree/hw3B-tree/Program.cs @@ -0,0 +1,21 @@ +using System; + +namespace Hw3B_tree +{ + class Program + { + static void Main(string[] args) + { + var tree = new BTree(2); + tree.Insert("1", "1"); + tree.Insert("2", "2"); + tree.Insert("3", "3"); + tree.Insert("4", "4"); + tree.Insert("5", "5"); + tree.Insert("6", "6"); + tree.Insert("7", "7"); + tree.Insert("8", "8"); + tree.Insert("9", "9"); + } + } +} \ No newline at end of file diff --git a/hw3B-tree/hw3B-tree/hw3B-tree.csproj b/hw3B-tree/hw3B-tree/hw3B-tree.csproj new file mode 100644 index 0000000..5b12930 --- /dev/null +++ b/hw3B-tree/hw3B-tree/hw3B-tree.csproj @@ -0,0 +1,9 @@ + + + + Exe + netcoreapp3.1 + hw3B_tree + + +