From 9b6c1a0f4a8480b49f629d39002c33cba5f9a47e Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 11:40:58 -0500 Subject: [PATCH 01/10] Btree implementation --- .../datastructures/trees/BTree.java | 299 ++++++++++++++++++ .../datastructures/trees/BTreeTest.java | 33 ++ 2 files changed, 332 insertions(+) create mode 100644 src/main/java/com/thealgorithms/datastructures/trees/BTree.java create mode 100644 src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java new file mode 100644 index 000000000000..d8cb9eb725ff --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -0,0 +1,299 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.Arrays; + +public class BTree { + static class BTreeNode { + int[] keys; + int t; // Minimum degree (defines range for number of keys) + BTreeNode[] children; + int n; // Current number of keys + boolean leaf; + + BTreeNode(int t, boolean leaf) { + this.t = t; + this.leaf = leaf; + this.keys = new int[2 * t - 1]; + this.children = new BTreeNode[2 * t]; + this.n = 0; + } + + void traverse(ArrayList result) { + for (int i = 0; i < n; i++) { + if (!leaf) { + children[i].traverse(result); + } + result.add(keys[i]); + } + if (!leaf) { + children[n].traverse(result); + } + } + + BTreeNode search(int key) { + int i = 0; + while (i < n && key > keys[i]) { + i++; + } + if (i < n && keys[i] == key) return this; + if (leaf) return null; + return children[i].search(key); + } + + void insertNonFull(int key) { + int i = n - 1; + if (leaf) { + while (i >= 0 && keys[i] > key) { + keys[i + 1] = keys[i]; + i--; + } + keys[i + 1] = key; + n++; + } else { + while (i >= 0 && keys[i] > key) { + i--; + } + if (children[i + 1].n == 2 * t - 1) { + splitChild(i + 1, children[i + 1]); + if (keys[i + 1] < key) { + i++; + } + } + children[i + 1].insertNonFull(key); + } + } + + void splitChild(int i, BTreeNode y) { + BTreeNode z = new BTreeNode(y.t, y.leaf); + z.n = t - 1; + + System.arraycopy(y.keys, t, z.keys, 0, t - 1); + if (!y.leaf) { + System.arraycopy(y.children, t, z.children, 0, t); + } + y.n = t - 1; + + for (int j = n; j >= i + 1; j--) { + children[j + 1] = children[j]; + } + children[i + 1] = z; + + for (int j = n - 1; j >= i; j--) { + keys[j + 1] = keys[j]; + } + keys[i] = y.keys[t - 1]; + n++; + } + + void remove(int key) { + int idx = findKey(key); + + if (idx < n && keys[idx] == key) { + if (leaf) { + removeFromLeaf(idx); + } else { + removeFromNonLeaf(idx); + } + } else { + if (leaf) { + return; // Key not found + } + + boolean flag = idx == n; + if (children[idx].n < t) { + fill(idx); + } + + if (flag && idx > n) { + children[idx - 1].remove(key); + } else { + children[idx].remove(key); + } + } + } + + private int findKey(int key) { + int idx = 0; + while (idx < n && keys[idx] < key) ++idx; + return idx; + } + + private void removeFromLeaf(int idx) { + for (int i = idx + 1; i < n; ++i) { + keys[i - 1] = keys[i]; + } + n--; + } + + private void removeFromNonLeaf(int idx) { + int key = keys[idx]; + if (children[idx].n >= t) { + int pred = getPredecessor(idx); + keys[idx] = pred; + children[idx].remove(pred); + } else if (children[idx + 1].n >= t) { + int succ = getSuccessor(idx); + keys[idx] = succ; + children[idx + 1].remove(succ); + } else { + merge(idx); + children[idx].remove(key); + } + } + + private int getPredecessor(int idx) { + BTreeNode cur = children[idx]; + while (!cur.leaf) { + cur = cur.children[cur.n]; + } + return cur.keys[cur.n - 1]; + } + + private int getSuccessor(int idx) { + BTreeNode cur = children[idx + 1]; + while (!cur.leaf) { + cur = cur.children[0]; + } + return cur.keys[0]; + } + + private void fill(int idx) { + if (idx != 0 && children[idx - 1].n >= t) { + borrowFromPrev(idx); + } else if (idx != n && children[idx + 1].n >= t) { + borrowFromNext(idx); + } else { + if (idx != n) { + merge(idx); + } else { + merge(idx - 1); + } + } + } + + private void borrowFromPrev(int idx) { + BTreeNode child = children[idx]; + BTreeNode sibling = children[idx - 1]; + + for (int i = child.n - 1; i >= 0; --i) { + child.keys[i + 1] = child.keys[i]; + } + + if (!child.leaf) { + for (int i = child.n; i >= 0; --i) { + child.children[i + 1] = child.children[i]; + } + } + + child.keys[0] = keys[idx - 1]; + + if (!child.leaf) { + child.children[0] = sibling.children[sibling.n]; + } + + keys[idx - 1] = sibling.keys[sibling.n - 1]; + + child.n += 1; + sibling.n -= 1; + } + + private void borrowFromNext(int idx) { + BTreeNode child = children[idx]; + BTreeNode sibling = children[idx + 1]; + + child.keys[child.n] = keys[idx]; + + if (!child.leaf) { + child.children[child.n + 1] = sibling.children[0]; + } + + keys[idx] = sibling.keys[0]; + + for (int i = 1; i < sibling.n; ++i) { + sibling.keys[i - 1] = sibling.keys[i]; + } + + if (!sibling.leaf) { + for (int i = 1; i <= sibling.n; ++i) { + sibling.children[i - 1] = sibling.children[i]; + } + } + + child.n += 1; + sibling.n -= 1; + } + + private void merge(int idx) { + BTreeNode child = children[idx]; + BTreeNode sibling = children[idx + 1]; + + child.keys[t - 1] = keys[idx]; + + for (int i = 0; i < sibling.n; ++i) { + child.keys[i + t] = sibling.keys[i]; + } + + if (!child.leaf) { + for (int i = 0; i <= sibling.n; ++i) { + child.children[i + t] = sibling.children[i]; + } + } + + for (int i = idx + 1; i < n; ++i) { + keys[i - 1] = keys[i]; + } + + for (int i = idx + 2; i <= n; ++i) { + children[i - 1] = children[i]; + } + + child.n += sibling.n + 1; + n--; + } + } + + private BTreeNode root; + private final int t; + + public BTree(int t) { + this.root = null; + this.t = t; + } + + public void traverse(ArrayList result) { + if (root != null) root.traverse(result); + } + + public boolean search(int key) { + return root != null && root.search(key) != null; + } + + public void insert(int key) { + if (root == null) { + root = new BTreeNode(t, true); + root.keys[0] = key; + root.n = 1; + } else { + if (root.n == 2 * t - 1) { + BTreeNode s = new BTreeNode(t, false); + s.children[0] = root; + s.splitChild(0, root); + int i = 0; + if (s.keys[0] < key) i++; + s.children[i].insertNonFull(key); + root = s; + } else { + root.insertNonFull(key); + } + } + } + + public void delete(int key) { + if (root == null) return; + root.remove(key); + if (root.n == 0) { + root = root.leaf ? null : root.children[0]; + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java new file mode 100644 index 000000000000..48eb510f8e46 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.*; +import java.util.*; +import org.junit.jupiter.api.Test; + +public class BTreeTest { + + @Test + public void testInsertSearchDelete() { + BTree bTree = new BTree(3); // Minimum degree t = 3 + + int[] values = {10, 20, 5, 6, 12, 30, 7, 17}; + for (int val : values) { + bTree.insert(val); + } + + for (int val : values) { + assertTrue(bTree.search(val), "Should find inserted value: " + val); + } + + ArrayList traversal = new ArrayList<>(); + bTree.traverse(traversal); + assertEquals(Arrays.asList(5, 6, 7, 10, 12, 17, 20, 30), traversal); + + bTree.delete(6); + assertFalse(bTree.search(6)); + traversal.clear(); + bTree.traverse(traversal); + assertEquals(Arrays.asList(5, 7, 10, 12, 17, 20, 30), traversal); + } +} + From d1ff189400e7ebe37c15d10eb144faf42c63a2da Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 11:53:03 -0500 Subject: [PATCH 02/10] Fix Checkstyle violations in BTree and BTreeTest --- .../thealgorithms/datastructures/trees/BTree.java | 13 ++++++------- .../datastructures/trees/BTreeTest.java | 8 ++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index d8cb9eb725ff..ca229b40722a 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -1,7 +1,6 @@ package com.thealgorithms.datastructures.trees; import java.util.ArrayList; -import java.util.Arrays; public class BTree { static class BTreeNode { @@ -36,8 +35,8 @@ BTreeNode search(int key) { while (i < n && key > keys[i]) { i++; } - if (i < n && keys[i] == key) return this; - if (leaf) return null; + if (i < n && keys[i] == key) {return this;} + if (leaf) {return null;} return children[i].search(key); } @@ -115,7 +114,7 @@ void remove(int key) { private int findKey(int key) { int idx = 0; - while (idx < n && keys[idx] < key) ++idx; + while (idx < n && keys[idx] < key) {++idx;} return idx; } @@ -262,7 +261,7 @@ public BTree(int t) { } public void traverse(ArrayList result) { - if (root != null) root.traverse(result); + if (root != null) {root.traverse(result);} } public boolean search(int key) { @@ -280,7 +279,7 @@ public void insert(int key) { s.children[0] = root; s.splitChild(0, root); int i = 0; - if (s.keys[0] < key) i++; + if (s.keys[0] < key) {i++;} s.children[i].insertNonFull(key); root = s; } else { @@ -290,7 +289,7 @@ public void insert(int key) { } public void delete(int key) { - if (root == null) return; + if (root == null) {return;} root.remove(key); if (root.n == 0) { root = root.leaf ? null : root.children[0]; diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java index 48eb510f8e46..1077e35e6e9d 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -1,7 +1,11 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.*; -import java.util.*; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; import org.junit.jupiter.api.Test; public class BTreeTest { From bad7a99c135cf3b701f5a6ced1b885e3e96fb1f9 Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 12:24:26 -0500 Subject: [PATCH 03/10] fix white space formatting errors --- .../datastructures/trees/BTree.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index ca229b40722a..36a6e9de9139 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -35,8 +35,12 @@ BTreeNode search(int key) { while (i < n && key > keys[i]) { i++; } - if (i < n && keys[i] == key) {return this;} - if (leaf) {return null;} + if (i < n && keys[i] == key) { + return this; + } + if (leaf) { + return null; + } return children[i].search(key); } @@ -114,7 +118,9 @@ void remove(int key) { private int findKey(int key) { int idx = 0; - while (idx < n && keys[idx] < key) {++idx;} + while (idx < n && keys[idx] < key) { + ++idx; + } return idx; } @@ -261,7 +267,9 @@ public BTree(int t) { } public void traverse(ArrayList result) { - if (root != null) {root.traverse(result);} + if (root != null) { + root.traverse(result); + } } public boolean search(int key) { @@ -279,7 +287,9 @@ public void insert(int key) { s.children[0] = root; s.splitChild(0, root); int i = 0; - if (s.keys[0] < key) {i++;} + if (s.keys[0] < key) { + i++; + } s.children[i].insertNonFull(key); root = s; } else { @@ -289,7 +299,9 @@ public void insert(int key) { } public void delete(int key) { - if (root == null) {return;} + if (root == null) { + return; + } root.remove(key); if (root.n == 0) { root = root.leaf ? null : root.children[0]; From 43c1470208f44fdd0ca519afdb72d79cd0731954 Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 12:40:28 -0500 Subject: [PATCH 04/10] fix white space formatting errors --- .../java/com/thealgorithms/datastructures/trees/BTree.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index 36a6e9de9139..975d8fca6fad 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -5,9 +5,9 @@ public class BTree { static class BTreeNode { int[] keys; - int t; // Minimum degree (defines range for number of keys) + int t; // Minimum degree (defines range for number of keys) BTreeNode[] children; - int n; // Current number of keys + int n; // Current number of keys boolean leaf; BTreeNode(int t, boolean leaf) { From 0cd5d16ccb7ec936cff63d42759a72cad73da3a1 Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 12:41:53 -0500 Subject: [PATCH 05/10] fixed whitespace errors --- .../java/com/thealgorithms/datastructures/trees/BTreeTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java index 1077e35e6e9d..7295182ba6ee 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -34,4 +34,3 @@ public void testInsertSearchDelete() { assertEquals(Arrays.asList(5, 7, 10, 12, 17, 20, 30), traversal); } } - From 9ad31afdd9c7950fb70c9d722247e929d49d466e Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Sun, 25 May 2025 12:47:11 -0500 Subject: [PATCH 06/10] import fix --- .../com/thealgorithms/datastructures/trees/BTreeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java index 7295182ba6ee..2b45cb3bfa2e 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -1,8 +1,8 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; From c3c3c0e826bfe34bbed016cf99fb28e789eb2ff0 Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Mon, 26 May 2025 10:12:52 -0500 Subject: [PATCH 07/10] including description and edge test cases for inserting duplicate keys, deleting non existent keys, Handling empty tree and Inserting and deleting keys that cause multiple splits or merges --- .../datastructures/trees/BTree.java | 10 +++ .../datastructures/trees/BTreeTest.java | 62 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index 975d8fca6fad..a3f25d555257 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -2,6 +2,16 @@ import java.util.ArrayList; +/** + * Implementation of a B-Tree, a self-balancing tree data structure that maintains sorted data + * and allows searches, sequential access, insertions, and deletions in logarithmic time. + * + * B-Trees are generalizations of binary search trees in that a node can have more than two children. + * They're widely used in databases and file systems. + * + * For more information: https://en.wikipedia.org/wiki/B-tree + */ + public class BTree { static class BTreeNode { int[] keys; diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java index 2b45cb3bfa2e..1aa009f249c6 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -1,8 +1,6 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.Arrays; @@ -33,4 +31,62 @@ public void testInsertSearchDelete() { bTree.traverse(traversal); assertEquals(Arrays.asList(5, 7, 10, 12, 17, 20, 30), traversal); } + + @Test + public void testEmptyTreeSearch() { + BTree bTree = new BTree(3); + assertFalse(bTree.search(42), "Search in empty tree should return false."); + } + + @Test + public void testDuplicateInsertions() { + BTree bTree = new BTree(3); + bTree.insert(15); + bTree.insert(15); // Attempt duplicate + bTree.insert(15); // Another duplicate + + ArrayList traversal = new ArrayList<>(); + bTree.traverse(traversal); + + // Should contain only one 15 + long count = traversal.stream().filter(x -> x == 15).count(); + assertEquals(1, count, "Duplicate keys should not be inserted."); + } + + @Test + public void testDeleteNonExistentKey() { + BTree bTree = new BTree(3); + bTree.insert(10); + bTree.insert(20); + bTree.delete(99); // Doesn't exist + assertTrue(bTree.search(10)); + assertTrue(bTree.search(20)); + } + + @Test + public void testComplexInsertDelete() { + BTree bTree = new BTree(2); // Smaller degree to trigger splits more easily + int[] values = {1, 3, 7, 10, 11, 13, 14, 15, 18, 16, 19, 24, 25, 26, 21, 4, 5, 20, 22, 2, 17, 12, 6}; + + for (int val : values) { + bTree.insert(val); + } + + for (int val : values) { + assertTrue(bTree.search(val)); + } + + int[] toDelete = {6, 13, 7, 4, 2, 16}; + for (int val : toDelete) { + bTree.delete(val); + assertFalse(bTree.search(val)); + } + + ArrayList remaining = new ArrayList<>(); + bTree.traverse(remaining); + + for (int val : toDelete) { + assertFalse(remaining.contains(val)); + } + } } From 622d4a318d2255408a985d971a7ede6459bd7d7a Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Mon, 26 May 2025 10:21:50 -0500 Subject: [PATCH 08/10] including description and edge test cases for inserting duplicate keys, deleting non existent keys, Handling empty tree and Inserting and deleting keys that cause multiple splits or merges --- .../datastructures/trees/BTree.java | 125 +++++++++--------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index a3f25d555257..ac6e25b1c2a3 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -13,11 +13,68 @@ */ public class BTree { + private BTreeNode root; + private int t; + + public BTree(int t) { + this.root = null; + this.t = t; + } + + public void traverse(ArrayList result) { + if (root != null) { + root.traverse(result); + } + } + + public BTreeNode search(int key) { + return (root == null) ? null : root.search(key); + } + + public void insert(int key) { + // Prevent duplicate insertions + if (search(key) != null) { + return; + } + + if (root == null) { + root = new BTreeNode(t, true); + root.keys[0] = key; + root.n = 1; + } else { + if (root.n == 2 * t - 1) { + BTreeNode s = new BTreeNode(t, false); + s.children[0] = root; + s.splitChild(0, root); + + int i = 0; + if (s.keys[0] < key) { + i++; + } + s.children[i].insertNonFull(key); + root = s; + } else { + root.insertNonFull(key); + } + } + } + + public void remove(int key) { + if (root == null) { + return; + } + + root.remove(key); + if (root.n == 0) { + root = (root.leaf) ? null : root.children[0]; + } + } + static class BTreeNode { int[] keys; - int t; // Minimum degree (defines range for number of keys) + int t; BTreeNode[] children; - int n; // Current number of keys + int n; boolean leaf; BTreeNode(int t, boolean leaf) { @@ -48,10 +105,7 @@ BTreeNode search(int key) { if (i < n && keys[i] == key) { return this; } - if (leaf) { - return null; - } - return children[i].search(key); + return (leaf) ? null : children[i].search(key); } void insertNonFull(int key) { @@ -80,7 +134,6 @@ void insertNonFull(int key) { void splitChild(int i, BTreeNode y) { BTreeNode z = new BTreeNode(y.t, y.leaf); z.n = t - 1; - System.arraycopy(y.keys, t, z.keys, 0, t - 1); if (!y.leaf) { System.arraycopy(y.children, t, z.children, 0, t); @@ -109,11 +162,9 @@ void remove(int key) { removeFromNonLeaf(idx); } } else { - if (leaf) { - return; // Key not found - } + if (leaf) return; - boolean flag = idx == n; + boolean flag = (idx == n); if (children[idx].n < t) { fill(idx); } @@ -208,7 +259,6 @@ private void borrowFromPrev(int idx) { } keys[idx - 1] = sibling.keys[sibling.n - 1]; - child.n += 1; sibling.n -= 1; } @@ -218,7 +268,6 @@ private void borrowFromNext(int idx) { BTreeNode sibling = children[idx + 1]; child.keys[child.n] = keys[idx]; - if (!child.leaf) { child.children[child.n + 1] = sibling.children[0]; } @@ -267,54 +316,4 @@ private void merge(int idx) { n--; } } - - private BTreeNode root; - private final int t; - - public BTree(int t) { - this.root = null; - this.t = t; - } - - public void traverse(ArrayList result) { - if (root != null) { - root.traverse(result); - } - } - - public boolean search(int key) { - return root != null && root.search(key) != null; - } - - public void insert(int key) { - if (root == null) { - root = new BTreeNode(t, true); - root.keys[0] = key; - root.n = 1; - } else { - if (root.n == 2 * t - 1) { - BTreeNode s = new BTreeNode(t, false); - s.children[0] = root; - s.splitChild(0, root); - int i = 0; - if (s.keys[0] < key) { - i++; - } - s.children[i].insertNonFull(key); - root = s; - } else { - root.insertNonFull(key); - } - } - } - - public void delete(int key) { - if (root == null) { - return; - } - root.remove(key); - if (root.n == 0) { - root = root.leaf ? null : root.children[0]; - } - } } From 0988c611d79fee8bad5ede61312abc9f46f7084b Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Mon, 26 May 2025 10:27:41 -0500 Subject: [PATCH 09/10] updated code to handle duplicate keys --- .../datastructures/trees/BTree.java | 128 +++++++++--------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java index ac6e25b1c2a3..2c19253b45e7 100644 --- a/src/main/java/com/thealgorithms/datastructures/trees/BTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/BTree.java @@ -13,68 +13,11 @@ */ public class BTree { - private BTreeNode root; - private int t; - - public BTree(int t) { - this.root = null; - this.t = t; - } - - public void traverse(ArrayList result) { - if (root != null) { - root.traverse(result); - } - } - - public BTreeNode search(int key) { - return (root == null) ? null : root.search(key); - } - - public void insert(int key) { - // Prevent duplicate insertions - if (search(key) != null) { - return; - } - - if (root == null) { - root = new BTreeNode(t, true); - root.keys[0] = key; - root.n = 1; - } else { - if (root.n == 2 * t - 1) { - BTreeNode s = new BTreeNode(t, false); - s.children[0] = root; - s.splitChild(0, root); - - int i = 0; - if (s.keys[0] < key) { - i++; - } - s.children[i].insertNonFull(key); - root = s; - } else { - root.insertNonFull(key); - } - } - } - - public void remove(int key) { - if (root == null) { - return; - } - - root.remove(key); - if (root.n == 0) { - root = (root.leaf) ? null : root.children[0]; - } - } - static class BTreeNode { int[] keys; - int t; + int t; // Minimum degree (defines range for number of keys) BTreeNode[] children; - int n; + int n; // Current number of keys boolean leaf; BTreeNode(int t, boolean leaf) { @@ -105,7 +48,10 @@ BTreeNode search(int key) { if (i < n && keys[i] == key) { return this; } - return (leaf) ? null : children[i].search(key); + if (leaf) { + return null; + } + return children[i].search(key); } void insertNonFull(int key) { @@ -134,6 +80,7 @@ void insertNonFull(int key) { void splitChild(int i, BTreeNode y) { BTreeNode z = new BTreeNode(y.t, y.leaf); z.n = t - 1; + System.arraycopy(y.keys, t, z.keys, 0, t - 1); if (!y.leaf) { System.arraycopy(y.children, t, z.children, 0, t); @@ -162,9 +109,11 @@ void remove(int key) { removeFromNonLeaf(idx); } } else { - if (leaf) return; + if (leaf) { + return; // Key not found + } - boolean flag = (idx == n); + boolean flag = idx == n; if (children[idx].n < t) { fill(idx); } @@ -259,6 +208,7 @@ private void borrowFromPrev(int idx) { } keys[idx - 1] = sibling.keys[sibling.n - 1]; + child.n += 1; sibling.n -= 1; } @@ -268,6 +218,7 @@ private void borrowFromNext(int idx) { BTreeNode sibling = children[idx + 1]; child.keys[child.n] = keys[idx]; + if (!child.leaf) { child.children[child.n + 1] = sibling.children[0]; } @@ -316,4 +267,57 @@ private void merge(int idx) { n--; } } + + private BTreeNode root; + private final int t; + + public BTree(int t) { + this.root = null; + this.t = t; + } + + public void traverse(ArrayList result) { + if (root != null) { + root.traverse(result); + } + } + + public boolean search(int key) { + return root != null && root.search(key) != null; + } + + public void insert(int key) { + if (search(key)) { + return; + } + if (root == null) { + root = new BTreeNode(t, true); + root.keys[0] = key; + root.n = 1; + } else { + if (root.n == 2 * t - 1) { + BTreeNode s = new BTreeNode(t, false); + s.children[0] = root; + s.splitChild(0, root); + int i = 0; + if (s.keys[0] < key) { + i++; + } + s.children[i].insertNonFull(key); + root = s; + } else { + root.insertNonFull(key); + } + } + } + + public void delete(int key) { + if (root == null) { + return; + } + root.remove(key); + if (root.n == 0) { + root = root.leaf ? null : root.children[0]; + } + } } From 84ff2380c0a76fe32a9ef06399ad7cbb03f269bc Mon Sep 17 00:00:00 2001 From: Soham Kamble <121136639+skamble2@users.noreply.github.com> Date: Mon, 26 May 2025 10:30:47 -0500 Subject: [PATCH 10/10] updated code to handle duplicate keys --- .../com/thealgorithms/datastructures/trees/BTreeTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java index 1aa009f249c6..da29e117f25d 100644 --- a/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java +++ b/src/test/java/com/thealgorithms/datastructures/trees/BTreeTest.java @@ -1,6 +1,8 @@ package com.thealgorithms.datastructures.trees; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays;