1实现二叉树的基本操作(插入、删除、查找、遍历)。
下面是用Java实现二叉树的基本操作的代码示例:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; BinaryTree() { root = null; } void insert(int key) { root = insertRec(root, key); } Node insertRec(Node root, int key) { if (root == null) { root = new Node(key); return root; } if (key < root.key) root.left = insertRec(root.left, key); else if (key > root.key) root.right = insertRec(root.right, key); return root; } void deleteKey(int key) { root = deleteRec(root, key); } Node deleteRec(Node root, int key) { if (root == null) return root; if (key < root.key) root.left = deleteRec(root.left, key); else if (key > root.key) root.right = deleteRec(root.right, key); else { if (root.left == null) return root.right; else if (root.right == null) return root.left; root.key = minValue(root.right); root.right = deleteRec(root.right, root.key); } return root; } int minValue(Node root) { int minv = root.key; while (root.left != null) { minv = root.left.key; root = root.left; } return minv; } void inorder() { inorderRec(root); } void inorderRec(Node root) { if (root != null) { inorderRec(root.left); System.out.print(root.key + " "); inorderRec(root.right); } } boolean search(int key) { return searchRec(root, key); } boolean searchRec(Node root, int key) { if (root == null) return false; if (root.key == key) return true; return key < root.key ? searchRec(root.left, key) : searchRec(root.right, key); } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.insert(50); tree.insert(30); tree.insert(20); tree.insert(40); tree.insert(70); tree.insert(60); tree.insert(80); System.out.println("Inorder traversal of the given tree"); tree.inorder(); System.out.println("\n\nDelete 20"); tree.deleteKey(20); System.out.println("Inorder traversal of the modified tree"); tree.inorder(); System.out.println("\n\nDelete 30"); tree.deleteKey(30); System.out.println("Inorder traversal of the modified tree"); tree.inorder(); System.out.println("\n\nDelete 50"); tree.deleteKey(50); System.out.println("Inorder traversal of the modified tree"); tree.inorder(); } }
这段代码定义了一个二叉搜索树,并实现了插入、删除、查找和中序遍历的操作。在主函数中,我们创建了一个二叉树实例,向其中插入了一些节点,然后进行了删除和遍历操作。
2判断一个二叉树是否为平衡二叉树。
要判断一个二叉树是否为平衡二叉树,我们需要检查每个节点的左右子树的高度差是否不超过1。如果所有节点都满足这个条件,那么这棵树就是平衡的。
下面是用Java实现的一个方法来判断二叉树是否平衡:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; boolean isBalanced() { return checkHeight(root) != -1; } int checkHeight(Node node) { if (node == null) { return 0; } int leftHeight = checkHeight(node.left); if (leftHeight == -1) return -1; // 左子树不平衡 int rightHeight = checkHeight(node.right); if (rightHeight == -1) return -1; // 右子树不平衡 if (Math.abs(leftHeight - rightHeight) > 1) { return -1; // 当前节点不平衡 } else { return Math.max(leftHeight, rightHeight) + 1; // 返回当前节点的高度 } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.right = new Node(6); tree.root.left.left.left = new Node(7); if (tree.isBalanced()) { System.out.println("The tree is balanced"); } else { System.out.println("The tree is not balanced"); } } }
在这个代码中,我们定义了一个
checkHeight函数来递归地计算每个节点的高度,并同时检查其左右子树的高度差是否超过1。如果任何节点的左右子树高度差超过1,或者某个子树本身是不平衡的,则整个树被认为是不平衡的。3计算二叉树的最大深度。
要计算二叉树的最大深度,我们可以使用递归方法。最大深度是从根节点到最远叶子节点的最长路径上的节点数。
下面是用Java实现的一个方法来计算二叉树的最大深度:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; int maxDepth() { return maxDepthRec(root); } int maxDepthRec(Node node) { if (node == null) { return 0; } else { // 计算左子树和右子树的最大深度 int leftDepth = maxDepthRec(node.left); int rightDepth = maxDepthRec(node.right); // 使用较大的一个并加1(当前节点) return Math.max(leftDepth, rightDepth) + 1; } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.right = new Node(6); tree.root.left.left.left = new Node(7); System.out.println("The maximum depth of the binary tree is: " + tree.maxDepth()); } }
在这个代码中,我们定义了一个
maxDepthRec函数来递归地计算每个节点的最大深度。对于每个节点,我们分别计算其左右子树的最大深度,然后取两者中的较大值并加1(代表当前节点)。如果节点为空,则返回0。这样,我们就可以得到整棵树的最大深度。4计算二叉树的最小深度。
要计算二叉树的最小深度,我们可以使用递归方法。最小深度是从根节点到最近的叶子节点的最短路径上的节点数。
下面是用Java实现的一个方法来计算二叉树的最小深度:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; int minDepth() { return minDepthRec(root); } int minDepthRec(Node node) { if (node == null) { return 0; } // 如果左子树为空,则递归计算右子树的最小深度 if (node.left == null) { return minDepthRec(node.right) + 1; } // 如果右子树为空,则递归计算左子树的最小深度 if (node.right == null) { return minDepthRec(node.left) + 1; } // 如果左右子树都不为空,则取两者中的较小值并加1(当前节点) return Math.min(minDepthRec(node.left), minDepthRec(node.right)) + 1; } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.right = new Node(6); tree.root.left.left.left = new Node(7); System.out.println("The minimum depth of the binary tree is: " + tree.minDepth()); } }
在这个代码中,我们定义了一个
minDepthRec函数来递归地计算每个节点的最小深度。对于每个节点,我们分别计算其左右子树的最小深度,然后取两者中的较小值并加1(代表当前节点)。如果节点为空,则返回0。这样,我们就可以得到整棵树的最小深度。5计算二叉树的直径(即最长路径)。
要计算二叉树的直径(即最长路径),我们可以使用递归方法。直径是从任意一个节点到另一个节点的最长路径,这条路径可能经过也可能不经过根节点。
下面是用Java实现的一个方法来计算二叉树的直径:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; // 用于存储最大直径 int diameter = 0; int calculateDiameter() { height(root); return diameter; } int height(Node node) { if (node == null) { return 0; } // 递归计算左子树和右子树的高度 int leftHeight = height(node.left); int rightHeight = height(node.right); // 更新直径 diameter = Math.max(diameter, leftHeight + rightHeight + 1); // 返回当前节点的高度 return Math.max(leftHeight, rightHeight) + 1; } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.right = new Node(6); tree.root.left.left.left = new Node(7); System.out.println("The diameter of the binary tree is: " + tree.calculateDiameter()); } }
在这个代码中,我们定义了一个
height函数来递归地计算每个节点的高度,并同时更新直径。对于每个节点,我们分别计算其左右子树的高度,然后取两者中的较大值并加1(代表当前节点)。在计算高度的同时,我们更新直径为左右子树高度之和加1(如果这个值大于当前的直径)。这样,我们就可以得到整棵树的直径。6判断两个二叉树是否相同或镜像对称。
要判断两个二叉树是否相同或镜像对称,我们可以分别实现两个方法:一个用于检查两棵树是否完全相同,另一个用于检查两棵树是否是镜像对称的。
下面是用Java实现这两个方法的代码:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; boolean isSameTree(Node p, Node q) { // 如果两个节点都为空,则它们相同 if (p == null && q == null) return true; // 如果其中一个节点为空,或者它们的值不同,则它们不相同 if (p == null || q == null || p.key != q.key) return false; // 递归检查左子树和右子树 return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); } boolean isMirror(Node p, Node q) { // 如果两个节点都为空,则它们是镜像对称的 if (p == null && q == null) return true; // 如果其中一个节点为空,或者它们的值不同,则它们不是镜像对称的 if (p == null || q == null || p.key != q.key) return false; // 递归检查左子树和右子树,注意这里是交叉比较 return isMirror(p.left, q.right) && isMirror(p.right, q.left); } } public class Main { public static void main(String[] args) { BinaryTree tree1 = new BinaryTree(); tree1.root = new Node(1); tree1.root.left = new Node(2); tree1.root.right = new Node(3); tree1.root.left.left = new Node(4); tree1.root.left.right = new Node(5); BinaryTree tree2 = new BinaryTree(); tree2.root = new Node(1); tree2.root.left = new Node(3); tree2.root.right = new Node(2); tree2.root.right.left = new Node(5); tree2.root.right.right = new Node(4); System.out.println("Are the two trees same? " + tree1.isSameTree(tree1.root, tree2.root)); System.out.println("Are the two trees mirrors of each other? " + tree1.isMirror(tree1.root, tree2.root)); } }
在这个代码中,我们定义了
isSameTree和isMirror两个方法。isSameTree方法递归地检查两个树的每个对应节点是否具有相同的值。isMirror方法递归地检查两个树的每个对应节点是否互为镜像(即左子树与右子树交换位置后相等)。在主函数中,我们创建了两棵示例树并调用这些方法来检查它们是否相同或镜像对称。7实现二叉树的前序、中序和后序遍历。
在Java中,我们可以使用递归方法来实现二叉树的前序、中序和后序遍历。下面是实现这三种遍历的代码:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void printPreorder(Node node) { if (node == null) { return; } System.out.print(node.key + " "); // 访问根节点 printPreorder(node.left); // 递归遍历左子树 printPreorder(node.right); // 递归遍历右子树 } void printInorder(Node node) { if (node == null) { return; } printInorder(node.left); // 递归遍历左子树 System.out.print(node.key + " "); // 访问根节点 printInorder(node.right); // 递归遍历右子树 } void printPostorder(Node node) { if (node == null) { return; } printPostorder(node.left); // 递归遍历左子树 printPostorder(node.right); // 递归遍历右子树 System.out.print(node.key + " "); // 访问根节点 } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); System.out.println("Preorder traversal of binary tree is:"); tree.printPreorder(tree.root); System.out.println("\nInorder traversal of binary tree is:"); tree.printInorder(tree.root); System.out.println("\nPostorder traversal of binary tree is:"); tree.printPostorder(tree.root); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了三个方法:printPreorder、printInorder和printPostorder,分别用于前序、中序和后序遍历二叉树。在主函数中,我们创建了一棵示例二叉树并调用这些方法来打印不同顺序的遍历结果。
8实现二叉树的层序遍历。
在Java中,我们可以使用队列来实现二叉树的层序遍历。下面是实现层序遍历的代码:
import java.util.LinkedList; import java.util.Queue; class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void printLevelOrder() { Queue<Node> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { // 从队列中移除当前节点并打印它的值 Node tempNode = queue.poll(); System.out.print(tempNode.key + " "); // 如果左子节点不为空,则将其添加到队列中 if (tempNode.left != null) { queue.add(tempNode.left); } // 如果右子节点不为空,则将其添加到队列中 if (tempNode.right != null) { queue.add(tempNode.right); } } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); System.out.println("Level order traversal of binary tree is:"); tree.printLevelOrder(); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了一个方法printLevelOrder,用于层序遍历二叉树。在
printLevelOrder方法中,我们使用了一个队列来存储待访问的节点。我们从根节点开始,将其添加到队列中,然后进入一个循环,直到队列为空。在每次循环中,我们从队列中移除一个节点,打印它的值,并将其左右子节点(如果存在)添加到队列中。这样,我们就可以按照层序顺序访问二叉树的所有节点。9实现二叉树的垂直遍历。
在Java中,我们可以使用哈希表和队列来实现二叉树的垂直遍历。下面是实现垂直遍历的代码:
import java.util.*; class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void verticalOrderTraversal() { // 创建一个映射来存储水平距离和对应的节点列表 Map<Integer, List<Integer>> map = new TreeMap<>(); // 创建一个队列来存储节点及其水平距离 Queue<Pair> queue = new LinkedList<>(); queue.add(new Pair(root, 0)); // 根节点的水平距离为0 while (!queue.isEmpty()) { Pair temp = queue.poll(); Node node = temp.node; int hd = temp.hd; // 将当前节点的值添加到对应水平距离的列表中 if (!map.containsKey(hd)) { map.put(hd, new ArrayList<>()); } map.get(hd).add(node.key); // 如果左子节点存在,将其添加到队列中,水平距离减1 if (node.left != null) { queue.add(new Pair(node.left, hd - 1)); } // 如果右子节点存在,将其添加到队列中,水平距离加1 if (node.right != null) { queue.add(new Pair(node.right, hd + 1)); } } // 打印所有水平距离及其对应的节点列表 for (Map.Entry<Integer, List<Integer>> entry : map.entrySet()) { System.out.println("Horizontal distance " + entry.getKey() + ": " + entry.getValue()); } } class Pair { Node node; int hd; public Pair(Node node, int hd) { this.node = node; this.hd = hd; } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.left = new Node(6); tree.root.right.right = new Node(7); System.out.println("Vertical order traversal of binary tree is:"); tree.verticalOrderTraversal(); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了一个方法verticalOrderTraversal,用于垂直遍历二叉树。在
verticalOrderTraversal方法中,我们使用了一个映射(Map)来存储水平距离和对应的节点列表,以及一个队列(Queue)来存储节点及其水平距离。我们从根节点开始,将其添加到队列中,然后进入一个循环,直到队列为空。在每次循环中,我们从队列中移除一个节点,并将其值添加到对应水平距离的列表中。如果左子节点或右子节点存在,则将其添加到队列中,并更新其水平距离。最后,我们打印所有水平距离及其对应的节点列表。10实现二叉树的螺旋遍历。
在Java中,我们可以使用两个栈来实现二叉树的螺旋遍历。下面是实现螺旋遍历的代码:
import java.util.Stack; class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void spiralOrderTraversal() { if (root == null) return; Stack<Node> s1 = new Stack<>(); // 用于存储奇数层的节点 Stack<Node> s2 = new Stack<>(); // 用于存储偶数层的节点 s1.push(root); while (!s1.isEmpty() || !s2.isEmpty()) { while (!s1.isEmpty()) { Node temp = s1.pop(); System.out.print(temp.key + " "); if (temp.right != null) s2.push(temp.right); if (temp.left != null) s2.push(temp.left); } while (!s2.isEmpty()) { Node temp = s2.pop(); System.out.print(temp.key + " "); if (temp.left != null) s1.push(temp.left); if (temp.right != null) s1.push(temp.right); } } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.left = new Node(6); tree.root.right.right = new Node(7); System.out.println("Spiral order traversal of binary tree is:"); tree.spiralOrderTraversal(); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了一个方法spiralOrderTraversal,用于螺旋遍历二叉树。在
spiralOrderTraversal方法中,我们使用了两个栈s1和s2来分别存储奇数层和偶数层的节点。我们从根节点开始,将其添加到s1中。然后进入一个循环,直到两个栈都为空。在每次循环中,我们先处理s1中的节点(即奇数层),将它们的值打印出来,并将它们的右子节点和左子节点(如果存在)添加到s2中。接着处理s2中的节点(即偶数层),将它们的值打印出来,并将它们的左子节点和右子节点(如果存在)添加到s1中。这样,我们就可以按照螺旋顺序访问二叉树的所有节点。11实现二叉树的之字形遍历。
在Java中,我们可以使用两个栈来实现二叉树的之字形遍历(也称为锯齿形遍历)。下面是实现之字形遍历的代码:
import java.util.Stack; class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void zigzagOrderTraversal() { if (root == null) return; Stack<Node> currentLevel = new Stack<>(); Stack<Node> nextLevel = new Stack<>(); boolean leftToRight = true; currentLevel.push(root); while (!currentLevel.isEmpty()) { Node temp = currentLevel.pop(); System.out.print(temp.key + " "); if (leftToRight) { if (temp.left != null) nextLevel.push(temp.left); if (temp.right != null) nextLevel.push(temp.right); } else { if (temp.right != null) nextLevel.push(temp.right); if (temp.left != null) nextLevel.push(temp.left); } if (currentLevel.isEmpty()) { leftToRight = !leftToRight; Stack<Node> tempStack = currentLevel; currentLevel = nextLevel; nextLevel = tempStack; } } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.left = new Node(6); tree.root.right.right = new Node(7); System.out.println("Zigzag order traversal of binary tree is:"); tree.zigzagOrderTraversal(); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了一个方法zigzagOrderTraversal,用于之字形遍历二叉树。在
zigzagOrderTraversal方法中,我们使用了两个栈currentLevel和nextLevel来分别存储当前层和下一层的节点。我们还使用了一个布尔变量leftToRight来指示当前的遍历方向。我们从根节点开始,将其添加到currentLevel中。然后进入一个循环,直到currentLevel为空。在每次循环中,我们从currentLevel中弹出一个节点,并将其值打印出来。根据当前的遍历方向,我们将该节点的左子节点和右子节点(如果存在)添加到nextLevel中。如果currentLevel为空,则交换currentLevel和nextLevel,并切换遍历方向。这样,我们就可以按照之字形顺序访问二叉树的所有节点。12如何实现二叉树的锯齿形遍历?
在Java中,实现二叉树的锯齿形遍历(Zigzag Level Order Traversal)可以通过使用两个栈来实现。下面是具体的实现代码:
import java.util.Stack; class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinaryTree { Node root; void zigzagOrderTraversal() { if (root == null) return; Stack<Node> currentLevel = new Stack<>(); Stack<Node> nextLevel = new Stack<>(); boolean leftToRight = true; currentLevel.push(root); while (!currentLevel.isEmpty()) { Node temp = currentLevel.pop(); System.out.print(temp.key + " "); if (leftToRight) { if (temp.left != null) nextLevel.push(temp.left); if (temp.right != null) nextLevel.push(temp.right); } else { if (temp.right != null) nextLevel.push(temp.right); if (temp.left != null) nextLevel.push(temp.left); } if (currentLevel.isEmpty()) { leftToRight = !leftToRight; Stack<Node> tempStack = currentLevel; currentLevel = nextLevel; nextLevel = tempStack; } } } } public class Main { public static void main(String[] args) { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.left = new Node(6); tree.root.right.right = new Node(7); System.out.println("Zigzag order traversal of binary tree is:"); tree.zigzagOrderTraversal(); } }
在这个代码中,我们定义了一个
Node类来表示二叉树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinaryTree类,其中包含了一个方法zigzagOrderTraversal,用于锯齿形遍历二叉树。在
zigzagOrderTraversal方法中,我们使用了两个栈currentLevel和nextLevel来分别存储当前层和下一层的节点。我们还使用了一个布尔变量leftToRight来指示当前的遍历方向。我们从根节点开始,将其添加到currentLevel中。然后进入一个循环,直到currentLevel为空。在每次循环中,我们从currentLevel中弹出一个节点,并将其值打印出来。根据当前的遍历方向,我们将该节点的左子节点和右子节点(如果存在)添加到nextLevel中。如果currentLevel为空,则交换currentLevel和nextLevel,并切换遍历方向。这样,我们就可以按照锯齿形顺序访问二叉树的所有节点。13实现二叉搜索树(BST)的插入和删除操作。
在Java中,我们可以定义一个
Node类来表示二叉搜索树(BST)的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinarySearchTree类,其中包含了插入和删除操作的方法。下面是实现这些操作的代码:class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinarySearchTree { Node root; // 插入操作 void insert(int key) { root = insertRec(root, key); } Node insertRec(Node root, int key) { if (root == null) { root = new Node(key); return root; } if (key < root.key) root.left = insertRec(root.left, key); else if (key > root.key) root.right = insertRec(root.right, key); return root; } // 删除操作 void deleteKey(int key) { root = deleteRec(root, key); } Node deleteRec(Node root, int key) { if (root == null) return root; if (key < root.key) root.left = deleteRec(root.left, key); else if (key > root.key) root.right = deleteRec(root.right, key); else { if (root.left == null) return root.right; else if (root.right == null) return root.left; root.key = minValue(root.right); root.right = deleteRec(root.right, root.key); } return root; } int minValue(Node root) { int minv = root.key; while (root.left != null) { minv = root.left.key; root = root.left; } return minv; } // 中序遍历打印BST void inorder() { inorderRec(root); } void inorderRec(Node root) { if (root != null) { inorderRec(root.left); System.out.print(root.key + " "); inorderRec(root.right); } } } public class Main { public static void main(String[] args) { BinarySearchTree tree = new BinarySearchTree(); tree.insert(50); tree.insert(30); tree.insert(20); tree.insert(40); tree.insert(70); tree.insert(60); tree.insert(80); System.out.println("Inorder traversal of the given tree:"); tree.inorder(); System.out.println("\n\nDelete 20:"); tree.deleteKey(20); tree.inorder(); System.out.println("\n\nDelete 30:"); tree.deleteKey(30); tree.inorder(); System.out.println("\n\nDelete 50:"); tree.deleteKey(50); tree.inorder(); } }
在这个代码中,我们定义了一个
Node类来表示二叉搜索树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinarySearchTree类,其中包含了插入和删除操作的方法。在
BinarySearchTree类中,insert方法用于插入一个新的键到BST中,它调用了递归的insertRec方法。deleteKey方法用于从BST中删除一个键,它调用了递归的deleteRec方法。minValue方法用于找到以给定节点为根的子树中的最小值。inorder方法用于中序遍历BST并打印其元素。14判断一个二叉搜索树是否有效。
在Java中,要判断一个二叉搜索树(BST)是否有效,我们需要确保对于每个节点,其左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。此外,整个树还需要满足BST的性质。
下面是一个实现这个功能的Java代码示例:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinarySearchTree { Node root; // 检查BST是否有效的函数 boolean isValidBST() { return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); } // 辅助递归函数,使用min和max值来限制当前节点的合法范围 boolean isValidBST(Node node, long min, long max) { if (node == null) { return true; } if (node.key <= min || node.key >= max) { return false; } return isValidBST(node.left, min, node.key) && isValidBST(node.right, node.key, max); } // 插入操作 void insert(int key) { root = insertRec(root, key); } Node insertRec(Node root, int key) { if (root == null) { root = new Node(key); return root; } if (key < root.key) root.left = insertRec(root.left, key); else if (key > root.key) root.right = insertRec(root.right, key); return root; } } public class Main { public static void main(String[] args) { BinarySearchTree tree = new BinarySearchTree(); tree.insert(50); tree.insert(30); tree.insert(20); tree.insert(40); tree.insert(70); tree.insert(60); tree.insert(80); System.out.println("Is the BST valid? " + tree.isValidBST()); } }
在这个代码中,我们定义了一个
Node类来表示二叉搜索树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinarySearchTree类,其中包含了一个方法isValidBST来判断BST是否有效。这个方法使用了递归来检查每个节点是否满足BST的性质。在
isValidBST方法中,我们调用了一个辅助递归函数isValidBST(Node node, long min, long max),它接受当前节点以及允许的最小值和最大值作为参数。如果当前节点为空,则返回true;如果当前节点的值不在允许的范围内,则返回false。否则,递归地检查左子树和右子树。15在二叉搜索树中找到第K个不在树中的正数。
在Java中,要在二叉搜索树(BST)中找到第K个不在树中的正数,我们可以使用递归的方法来遍历树并记录缺失的正整数。下面是一个实现这个功能的代码示例:
class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } class BinarySearchTree { Node root; // 找到第K个不在树中的正数 int findKthMissingPositive(int k) { return findKthMissingPositive(root, k); } // 辅助递归函数 int findKthMissingPositive(Node node, int k) { if (node == null) { return k; // 如果节点为空,返回k } // 计算左子树中缺失的正整数数量 int leftCount = countMissing(node.left); if (leftCount >= k) { // 如果左子树中缺失的正整数数量大于等于k,则在左子树中继续查找 return findKthMissingPositive(node.left, k); } else if (leftCount + 1 == k) { // 如果左子树中缺失的正整数数量加1等于k,则当前节点的值就是第k个缺失的正整数 return node.key; } else { // 否则,在右子树中查找,更新k值 return findKthMissingPositive(node.right, k - leftCount - 1); } } // 计算以给定节点为根的子树中缺失的正整数数量 int countMissing(Node node) { if (node == null) { return 0; } int leftCount = countMissing(node.left); int rightCount = countMissing(node.right); return leftCount + rightCount + 1; } // 插入操作 void insert(int key) { root = insertRec(root, key); } Node insertRec(Node root, int key) { if (root == null) { root = new Node(key); return root; } if (key < root.key) root.left = insertRec(root.left, key); else if (key > root.key) root.right = insertRec(root.right, key); return root; } } public class Main { public static void main(String[] args) { BinarySearchTree tree = new BinarySearchTree(); tree.insert(5); tree.insert(3); tree.insert(6); tree.insert(2); tree.insert(4); tree.insert(7); int k = 3; System.out.println("The " + k + "rd missing positive number is: " + tree.findKthMissingPositive(k)); } }
在这个代码中,我们定义了一个
Node类来表示二叉搜索树的节点,每个节点包含一个整数值key以及指向左右子节点的引用。我们还定义了一个BinarySearchTree类,其中包含了一个方法findKthMissingPositive来找到第K个不在树中的正数。这个方法使用了递归来遍历树并记录缺失的正整数。在
findKthMissingPositive方法中,我们调用了一个辅助递归函数findKthMissingPositive(Node node, int k),它接受当前节点和需要找到的第K个缺失的正整数作为参数。如果当前节点为空,则返回k;否则,计算左子树中缺失的正整数数量,并根据这个数量决定是继续在左子树中查找还是在右子树中查找。16实现AVL树的插入和删除操作。
AVL树是一种自平衡二叉搜索树,它通过在插入和删除操作后进行旋转来保持树的平衡。下面是一个实现AVL树插入和删除操作的Java代码示例:
class Node { int key, height; Node left, right; public Node(int d) { key = d; height = 1; } } class AVLTree { Node root; // 获取节点的高度 int height(Node N) { if (N == null) return 0; return N.height; } // 右旋操作 Node rightRotate(Node y) { Node x = y.left; Node T2 = x.right; x.right = y; y.left = T2; y.height = Math.max(height(y.left), height(y.right)) + 1; x.height = Math.max(height(x.left), height(x.right)) + 1; return x; } // 左旋操作 Node leftRotate(Node x) { Node y = x.right; Node T2 = y.left; y.left = x; x.right = T2; x.height = Math.max(height(x.left), height(x.right)) + 1; y.height = Math.max(height(y.left), height(y.right)) + 1; return y; } // 获取平衡因子 int getBalance(Node N) { if (N == null) return 0; return height(N.left) - height(N.right); } // 插入操作 Node insert(Node node, int key) { if (node == null) return (new Node(key)); if (key < node.key) node.left = insert(node.left, key); else if (key > node.key) node.right = insert(node.right, key); else // 不允许重复键 return node; node.height = 1 + Math.max(height(node.left), height(node.right)); int balance = getBalance(node); if (balance > 1 && key < node.left.key) return rightRotate(node); if (balance < -1 && key > node.right.key) return leftRotate(node); if (balance > 1 && key > node.left.key) { node.left = leftRotate(node.left); return rightRotate(node); } if (balance < -1 && key < node.right.key) { node.right = rightRotate(node.right); return leftRotate(node); } return node; } // 删除操作 Node deleteNode(Node root, int key) { if (root == null) return root; if (key < root.key) root.left = deleteNode(root.left, key); else if (key > root.key) root.right = deleteNode(root.right, key); else { if ((root.left == null) || (root.right == null)) { Node temp = null; if (temp == root.left) temp = root.right; else temp = root.left; if (temp == null) { temp = root; root = null; } else root = temp; } else { Node temp = minValueNode(root.right); root.key = temp.key; root.right = deleteNode(root.right, temp.key); } } if (root == null) return root; root.height = Math.max(height(root.left), height(root.right)) + 1; int balance = getBalance(root); if (balance > 1 && getBalance(root.left) >= 0) return rightRotate(root); if (balance > 1 && getBalance(root.left) < 0) { root.left = leftRotate(root.left); return rightRotate(root); } if (balance < -1 && getBalance(root.right) <= 0) return leftRotate(root); if (balance < -1 && getBalance(root.right) > 0) { root.right = rightRotate(root.right); return leftRotate(root); } return root; } // 找到最小值节点 Node minValueNode(Node node) { Node current = node; while (current.left != null) current = current.left; return current; } // 中序遍历打印树 void inOrder(Node node) { if (node != null) { inOrder(node.left); System.out.print(node.key + " "); inOrder(node.right); } } } public class Main { public static void main(String[] args) { AVLTree tree = new AVLTree(); tree.root = tree.insert(tree.root, 10); tree.root = tree.insert(tree.root, 20); tree.root = tree.insert(tree.root, 30); tree.root = tree.insert(tree.root, 40); tree.root = tree.insert(tree.root, 50); tree.root = tree.insert(tree.root, 25); System.out.println("Inorder traversal of the constructed AVL tree is:"); tree.inOrder(tree.root); tree.root = tree.deleteNode(tree.root, 20); System.out.println("\nInorder traversal after deletion of 20:"); tree.inOrder(tree.root); } }
在这个代码中,我们定义了一个
Node类来表示AVL树的节点,每个节点包含一个整数值key、高度height以及指向左右子节点的引用。我们还定义了一个AVLTree类,其中包含了方法来执行插入和删除操作,并保持树的平衡。17实现红黑树的插入和删除操作。
红黑树是一种自平衡二叉搜索树,它通过在插入和删除操作后进行颜色调整和旋转来保持树的平衡。下面是一个实现红黑树插入和删除操作的Java代码示例:
class RedBlackTree { private static final boolean RED = true; private static final boolean BLACK = false; class Node { int data; Node left, right, parent; boolean color; public Node(int data) { this.data = data; this.left = null; this.right = null; this.parent = null; this.color = RED; } } private Node root; // 左旋操作 private void rotateLeft(Node node) { Node rightChild = node.right; node.right = rightChild.left; if (node.right != null) { node.right.parent = node; } rightChild.parent = node.parent; if (node.parent == null) { root = rightChild; } else if (node == node.parent.left) { node.parent.left = rightChild; } else { node.parent.right = rightChild; } rightChild.left = node; node.parent = rightChild; } // 右旋操作 private void rotateRight(Node node) { Node leftChild = node.left; node.left = leftChild.right; if (node.left != null) { node.left.parent = node; } leftChild.parent = node.parent; if (node.parent == null) { root = leftChild; } else if (node == node.parent.left) { node.parent.left = leftChild; } else { node.parent.right = leftChild; } leftChild.right = node; node.parent = leftChild; } // 插入操作 public void insert(int data) { Node newNode = new Node(data); if (root == null) { root = newNode; root.color = BLACK; return; } Node current = root; Node parent = null; while (current != null) { parent = current; if (newNode.data < current.data) { current = current.left; } else { current = current.right; } } newNode.parent = parent; if (newNode.data < parent.data) { parent.left = newNode; } else { parent.right = newNode; } fixInsert(newNode); } // 修复插入后的红黑树性质 private void fixInsert(Node node) { Node parent = null; Node grandParent = null; while (node != root && node.color == RED && node.parent.color == RED) { parent = node.parent; grandParent = parent.parent; if (parent == grandParent.left) { Node uncle = grandParent.right; if (uncle != null && uncle.color == RED) { grandParent.color = RED; parent.color = BLACK; uncle.color = BLACK; node = grandParent; } else { if (node == parent.right) { rotateLeft(parent); node = parent; parent = node.parent; } rotateRight(grandParent); boolean tempColor = parent.color; parent.color = grandParent.color; grandParent.color = tempColor; node = parent; } } else { Node uncle = grandParent.left; if (uncle != null && uncle.color == RED) { grandParent.color = RED; parent.color = BLACK; uncle.color = BLACK; node = grandParent; } else { if (node == parent.left) { rotateRight(parent); node = parent; parent = node.parent; } rotateLeft(grandParent); boolean tempColor = parent.color; parent.color = grandParent.color; grandParent.color = tempColor; node = parent; } } } root.color = BLACK; } // 删除操作(省略具体实现) public void delete(int data) { // 删除操作的具体实现略去,通常包括找到节点、处理三种情况(无子节点、一个子节点、两个子节点)以及修复红黑树性质。 } }
18实现Trie树(前缀树)的插入和查询操作。
Trie树(前缀树)是一种用于快速检索字符串数据集中的键的树形数据结构。它常用于实现自动补全和拼写检查等功能。下面是用Java实现Trie树的插入和查询操作的代码示例:
class TrieNode { // 每个节点包含一个长度为26的数组,表示26个字母 TrieNode[] children = new TrieNode[26]; // 标记该节点是否是一个单词的结尾 boolean isEndOfWord; public TrieNode() { isEndOfWord = false; for (int i = 0; i < 26; i++) { children[i] = null; } } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } // 插入一个单词到Trie树中 public void insert(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { int index = word.charAt(i) - 'a'; if (node.children[index] == null) { node.children[index] = new TrieNode(); } node = node.children[index]; } node.isEndOfWord = true; // 标记单词结束 } // 查询一个单词是否存在于Trie树中 public boolean search(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { int index = word.charAt(i) - 'a'; if (node.children[index] == null) { return false; // 如果路径不存在,则返回false } node = node.children[index]; } return node != null && node.isEndOfWord; // 检查是否到达单词的结尾 } // 查询是否有以给定前缀开头的任何单词存在于Trie树中 public boolean startsWith(String prefix) { TrieNode node = root; for (int i = 0; i < prefix.length(); i++) { int index = prefix.charAt(i) - 'a'; if (node.children[index] == null) { return false; // 如果路径不存在,则返回false } node = node.children[index]; } return true; // 只要前缀存在,就返回true } public static void main(String[] args) { Trie trie = new Trie(); trie.insert("apple"); System.out.println(trie.search("apple")); // 返回 true System.out.println(trie.search("app")); // 返回 false System.out.println(trie.startsWith("app")); // 返回 true trie.insert("app"); System.out.println(trie.search("app")); // 返回 true } }
18.1代码说明:
- TrieNode类: 定义了Trie树的节点,每个节点包含一个长度为26的数组
children,用于存储子节点,以及一个布尔值isEndOfWord,用于标记是否是一个单词的结尾。 - Trie类: 包含根节点
root,并实现了插入、查询和前缀匹配的方法。 - insert方法: 将一个单词插入到Trie树中。遍历单词的每个字符,如果对应位置没有子节点,则创建一个新的子节点。最后将最后一个字符对应的节点标记为单词的结尾。
- search方法: 查询一个单词是否存在于Trie树中。遍历单词的每个字符,如果路径不存在,则返回
false。如果遍历完所有字符且最后一个字符对应的节点标记为单词的结尾,则返回true。 - startsWith方法: 查询是否有以给定前缀开头的任何单词存在于Trie树中。遍历前缀的每个字符,如果路径不存在,则返回
false。如果遍历完所有字符,则返回true。
通过这些方法,可以高效地在Trie树中进行插入和查询操作。
19实现Trie树的前缀匹配功能。
前缀匹配功能是指查找所有以给定前缀开头的单词。在Trie树中,实现这一功能可以通过深度优先搜索(DFS)来完成。下面是用Java实现Trie树的前缀匹配功能的代码示例:
import java.util.ArrayList; import java.util.List; class TrieNode { // 每个节点包含一个长度为26的数组,表示26个字母 TrieNode[] children = new TrieNode[26]; // 标记该节点是否是一个单词的结尾 boolean isEndOfWord; public TrieNode() { isEndOfWord = false; for (int i = 0; i < 26; i++) { children[i] = null; } } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } // 插入一个单词到Trie树中 public void insert(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { int index = word.charAt(i) - 'a'; if (node.children[index] == null) { node.children[index] = new TrieNode(); } node = node.children[index]; } node.isEndOfWord = true; // 标记单词结束 } // 查询是否有以给定前缀开头的任何单词存在于Trie树中 public boolean startsWith(String prefix) { TrieNode node = root; for (int i = 0; i < prefix.length(); i++) { int index = prefix.charAt(i) - 'a'; if (node.children[index] == null) { return false; // 如果路径不存在,则返回false } node = node.children[index]; } return true; // 只要前缀存在,就返回true } // 查找所有以给定前缀开头的单词 public List<String> findWordsWithPrefix(String prefix) { List<String> result = new ArrayList<>(); TrieNode node = root; for (int i = 0; i < prefix.length(); i++) { int index = prefix.charAt(i) - 'a'; if (node.children[index] == null) { return result; // 如果路径不存在,则返回空列表 } node = node.children[index]; } // 从当前节点开始进行深度优先搜索 findAllWords(node, new StringBuilder(prefix), result); return result; } // 深度优先搜索辅助函数 private void findAllWords(TrieNode node, StringBuilder currentWord, List<String> result) { if (node.isEndOfWord) { result.add(currentWord.toString()); } for (int i = 0; i < 26; i++) { if (node.children[i] != null) { currentWord.append((char) (i + 'a')); findAllWords(node.children[i], currentWord, result); currentWord.deleteCharAt(currentWord.length() - 1); // 回溯 } } } public static void main(String[] args) { Trie trie = new Trie(); trie.insert("apple"); trie.insert("app"); trie.insert("application"); trie.insert("apply"); trie.insert("bat"); trie.insert("batch"); trie.insert("bath"); trie.insert("ball"); System.out.println(trie.findWordsWithPrefix("app")); // 输出 ["apple", "app", "application", "apply"] System.out.println(trie.findWordsWithPrefix("bat")); // 输出 ["bat", "batch", "bath"] System.out.println(trie.findWordsWithPrefix("ba")); // 输出 ["bat", "batch", "bath", "ball"] System.out.println(trie.findWordsWithPrefix("cat")); // 输出 [] } }
19.1代码说明:
- TrieNode类: 定义了Trie树的节点,每个节点包含一个长度为26的数组
children,用于存储子节点,以及一个布尔值isEndOfWord,用于标记是否是一个单词的结尾。 - Trie类: 包含根节点
root,并实现了插入、查询和前缀匹配的方法。 - insert方法: 将一个单词插入到Trie树中。遍历单词的每个字符,如果对应位置没有子节点,则创建一个新的子节点。最后将最后一个字符对应的节点标记为单词的结尾。
- startsWith方法: 查询是否有以给定前缀开头的任何单词存在于Trie树中。遍历前缀的每个字符,如果路径不存在,则返回
false。如果遍历完所有字符,则返回true。 - findWordsWithPrefix方法: 查找所有以给定前缀开头的单词。首先找到前缀对应的节点,然后从该节点开始进行深度优先搜索,收集所有符合条件的单词。
- findAllWords方法: 深度优先搜索辅助函数,用于递归地查找所有以当前节点为起点的单词,并将它们添加到结果列表中。使用
StringBuilder来构建当前单词,并在递归调用后进行回溯。
通过这些方法,可以高效地在Trie树中进行前缀匹配操作。
20实现字典树Trie的自动补全功能。
自动补全功能是指根据输入的前缀,查找所有以该前缀开头的单词。在Trie树中实现这一功能可以通过深度优先搜索(DFS)来完成。下面是用Java实现Trie树的自动补全功能的代码示例:
import java.util.ArrayList; import java.util.List; class TrieNode { // 每个节点包含一个长度为26的数组,表示26个字母 TrieNode[] children = new TrieNode[26]; // 标记该节点是否是一个单词的结尾 boolean isEndOfWord; public TrieNode() { isEndOfWord = false; for (int i = 0; i < 26; i++) { children[i] = null; } } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } // 插入一个单词到Trie树中 public void insert(String word) { TrieNode node = root; for (int i = 0; i < word.length(); i++) { int index = word.charAt(i) - 'a'; if (node.children[index] == null) { node.children[index] = new TrieNode(); } node = node.children[index]; } node.isEndOfWord = true; // 标记单词结束 } // 查找所有以给定前缀开头的单词 public List<String> autocomplete(String prefix) { List<String> result = new ArrayList<>(); TrieNode node = root; for (int i = 0; i < prefix.length(); i++) { int index = prefix.charAt(i) - 'a'; if (node.children[index] == null) { return result; // 如果路径不存在,则返回空列表 } node = node.children[index]; } // 从当前节点开始进行深度优先搜索 findAllWords(node, new StringBuilder(prefix), result); return result; } // 深度优先搜索辅助函数 private void findAllWords(TrieNode node, StringBuilder currentWord, List<String> result) { if (node.isEndOfWord) { result.add(currentWord.toString()); } for (int i = 0; i < 26; i++) { if (node.children[i] != null) { currentWord.append((char) (i + 'a')); findAllWords(node.children[i], currentWord, result); currentWord.deleteCharAt(currentWord.length() - 1); // 回溯 } } } public static void main(String[] args) { Trie trie = new Trie(); trie.insert("apple"); trie.insert("app"); trie.insert("application"); trie.insert("apply"); trie.insert("bat"); trie.insert("batch"); trie.insert("bath"); trie.insert("ball"); System.out.println(trie.autocomplete("app")); // 输出 ["apple", "app", "application", "apply"] System.out.println(trie.autocomplete("bat")); // 输出 ["bat", "batch", "bath"] System.out.println(trie.autocomplete("ba")); // 输出 ["bat", "batch", "bath", "ball"] System.out.println(trie.autocomplete("cat")); // 输出 [] } }
20.1代码说明:
- TrieNode类: 定义了Trie树的节点,每个节点包含一个长度为26的数组
children,用于存储子节点,以及一个布尔值isEndOfWord,用于标记是否是一个单词的结尾。 - Trie类: 包含根节点
root,并实现了插入和自动补全的方法。 - insert方法: 将一个单词插入到Trie树中。遍历单词的每个字符,如果对应位置没有子节点,则创建一个新的子节点。最后将最后一个字符对应的节点标记为单词的结尾。
- autocomplete方法: 查找所有以给定前缀开头的单词。首先找到前缀对应的节点,然后从该节点开始进行深度优先搜索,收集所有符合条件的单词。
- findAllWords方法: 深度优先搜索辅助函数,用于递归地查找所有以当前节点为起点的单词,并将它们添加到结果列表中。使用
StringBuilder来构建当前单词,并在递归调用后进行回溯。
通过这些方法,可以高效地在Trie树中实现自动补全功能。
21实现Segment Tree(线段树)的构建和查询操作。
public class SegmentTree { private int[] tree; private int n; // Constructor to initialize the segment tree with given array public SegmentTree(int[] arr) { n = arr.length; tree = new int[4 * n]; buildTree(arr, 0, 0, n - 1); } // Function to build the segment tree private void buildTree(int[] arr, int node, int start, int end) { if (start == end) { tree[node] = arr[start]; } else { int mid = (start + end) / 2; int leftChild = 2 * node + 1; int rightChild = 2 * node + 2; buildTree(arr, leftChild, start, mid); buildTree(arr, rightChild, mid + 1, end); tree[node] = tree[leftChild] + tree[rightChild]; // Example operation: sum } } // Function to query the sum in a given range [l, r] public int query(int l, int r) { return query(0, 0, n - 1, l, r); } // Helper function for querying the segment tree private int query(int node, int start, int end, int l, int r) { if (r < start || l > end) { return 0; // Out of range } if (l <= start && end <= r) { return tree[node]; // Complete overlap } int mid = (start + end) / 2; int leftChild = 2 * node + 1; int rightChild = 2 * node + 2; int leftSum = query(leftChild, start, mid, l, r); int rightSum = query(rightChild, mid + 1, end, l, r); return leftSum + rightSum; // Example operation: sum } // Main method for testing public static void main(String[] args) { int[] arr = {1, 3, 5, 7, 9, 11}; SegmentTree segTree = new SegmentTree(arr); System.out.println("Sum of values in range [1, 3]: " + segTree.query(1, 3)); // Output should be 15 (3+5+7) System.out.println("Sum of values in range [0, 2]: " + segTree.query(0, 2)); // Output should be 9 (1+3+5) } }
这个解决方案展示了如何构建一个线段树,并实现查询操作。代码中包括了构建线段树的函数和查询特定范围的函数。
22实现Sparse Table(稀疏表)的构建和RMQ(范围最值查询)操作。
public class SparseTable { private int[][] st; private int[] log; // Constructor to initialize the sparse table with given array public SparseTable(int[] arr) { int n = arr.length; log = new int[n + 1]; for (int i = 2; i <= n; i++) { log[i] = log[i / 2] + 1; } st = new int[n][log[n] + 1]; for (int i = 0; i < n; i++) { st[i][0] = arr[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 0; (i + (1 << j) - 1) < n; i++) { st[i][j] = Math.min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); // Example operation: minimum } } } // Function to query the minimum in a given range [l, r] public int query(int l, int r) { int j = log[r - l + 1]; return Math.min(st[l][j], st[r - (1 << j) + 1][j]); // Example operation: minimum } // Main method for testing public static void main(String[] args) { int[] arr = {1, 3, 2, 7, 9, 11}; SparseTable sparseTable = new SparseTable(arr); System.out.println("Minimum of values in range [1, 4]: " + sparseTable.query(1, 4)); // Output should be 2 System.out.println("Minimum of values in range [0, 5]: " + sparseTable.query(0, 5)); // Output should be 1 } }
这个解决方案展示了如何构建一个稀疏表,并实现范围最值查询操作。代码中包括了稀疏表的构建函数和查询特定范围的函数。
23实现Fenwick Tree(二进制索引树)的更新和查询操作。
public class FenwickTree { private int[] tree; private int n; // Constructor to initialize the Fenwick Tree with given size public FenwickTree(int size) { n = size; tree = new int[n + 1]; } // Function to update the Fenwick Tree at a specific index public void update(int index, int delta) { index++; // Fenwick Tree is 1-based indexed while (index <= n) { tree[index] += delta; index += index & -index; // Move to the next index } } // Function to query the prefix sum up to a specific index public int query(int index) { index++; // Fenwick Tree is 1-based indexed int sum = 0; while (index > 0) { sum += tree[index]; index -= index & -index; // Move to the parent index } return sum; } // Function to get the range sum from l to r public int rangeQuery(int l, int r) { return query(r) - query(l - 1); } // Main method for testing public static void main(String[] args) { FenwickTree fenwickTree = new FenwickTree(10); fenwickTree.update(0, 5); fenwickTree.update(1, 3); fenwickTree.update(2, 7); fenwickTree.update(3, 6); fenwickTree.update(4, 1); fenwickTree.update(5, 8); fenwickTree.update(6, 9); fenwickTree.update(7, 2); fenwickTree.update(8, 4); fenwickTree.update(9, 10); System.out.println("Sum of values in range [0, 4]: " + fenwickTree.rangeQuery(0, 4)); // Output should be 19 (5+3+7+6) System.out.println("Sum of values in range [5, 9]: " + fenwickTree.rangeQuery(5, 9)); // Output should be 34 (8+9+2+4+10) } }
这个解决方案展示了如何构建一个Fenwick Tree,并实现更新和查询操作。代码中包括了更新特定索引的函数、查询前缀和的函数以及获取范围和的函数。
24实现Treap(带优先级的二叉搜索树)的插入和删除操作。
import java.util.Random; class TreapNode { int key, priority; TreapNode left, right; public TreapNode(int key) { this.key = key; this.priority = new Random().nextInt(); this.left = null; this.right = null; } } public class Treap { private TreapNode root; // Function to perform a right rotation private TreapNode rightRotate(TreapNode y) { TreapNode x = y.left; y.left = x.right; x.right = y; return x; } // Function to perform a left rotation private TreapNode leftRotate(TreapNode x) { TreapNode y = x.right; x.right = y.left; y.left = x; return y; } // Function to insert a node with given key in the Treap public void insert(int key) { root = insert(root, key); } private TreapNode insert(TreapNode root, int key) { if (root == null) { return new TreapNode(key); } if (key < root.key) { root.left = insert(root.left, key); if (root.left.priority > root.priority) { root = rightRotate(root); } } else { root.right = insert(root.right, key); if (root.right.priority > root.priority) { root = leftRotate(root); } } return root; } // Function to delete a node with given key from the Treap public void delete(int key) { root = delete(root, key); } private TreapNode delete(TreapNode root, int key) { if (root == null) { return null; } if (key < root.key) { root.left = delete(root.left, key); } else if (key > root.key) { root.right = delete(root.right, key); } else { if (root.left == null && root.right == null) { return null; } else if (root.left != null && root.right != null) { if (root.left.priority < root.right.priority) { root = leftRotate(root); root.left = delete(root.left, key); } else { root = rightRotate(root); root.right = delete(root.right, key); } } else if (root.left != null) { root = rightRotate(root); root.right = delete(root.right, key); } else { root = leftRotate(root); root.left = delete(root.left, key); } } return root; } // In-order traversal of the Treap public void inOrder() { inOrder(root); System.out.println(); } private void inOrder(TreapNode root) { if (root != null) { inOrder(root.left); System.out.print(root.key + " "); inOrder(root.right); } } // Main method for testing public static void main(String[] args) { Treap treap = new Treap(); treap.insert(50); treap.insert(30); treap.insert(20); treap.insert(40); treap.insert(70); treap.insert(60); treap.insert(80); System.out.println("In-order traversal of the Treap:"); treap.inOrder(); // Output should be sorted order of inserted elements treap.delete(20); System.out.println("In-order traversal after deleting 20:"); treap.inOrder(); // Output should reflect deletion of element 20 } }
这个解决方案展示了如何构建一个带优先级的二叉搜索树(Treap),并实现插入和删除操作。代码中包括了旋转操作、插入函数、删除函数以及中序遍历函数。
25实现Splay Tree(伸展树)的旋转操作。
class SplayTreeNode { int key; SplayTreeNode left, right, parent; public SplayTreeNode(int key) { this.key = key; this.left = null; this.right = null; this.parent = null; } } public class SplayTree { private SplayTreeNode root; // Function to perform a right rotation on the given node private void rightRotate(SplayTreeNode x) { SplayTreeNode y = x.left; x.left = y.right; if (y.right != null) { y.right.parent = x; } y.parent = x.parent; if (x.parent == null) { this.root = y; } else if (x == x.parent.right) { x.parent.right = y; } else { x.parent.left = y; } y.right = x; x.parent = y; } // Function to perform a left rotation on the given node private void leftRotate(SplayTreeNode x) { SplayTreeNode y = x.right; x.right = y.left; if (y.left != null) { y.left.parent = x; } y.parent = x.parent; if (x.parent == null) { this.root = y; } else if (x == x.parent.left) { x.parent.left = y; } else { x.parent.right = y; } y.left = x; x.parent = y; } // Function to splay a given node to the root of the tree public void splay(SplayTreeNode node) { while (node.parent != null) { if (node.parent.parent == null) { // Zig step if (node.parent.left == node) { rightRotate(node.parent); } else { leftRotate(node.parent); } } else if (node.parent.left == node && node.parent.parent.left == node.parent) { // Zig-Zig step rightRotate(node.parent.parent); rightRotate(node.parent); } else if (node.parent.right == node && node.parent.parent.right == node.parent) { // Zig-Zig step leftRotate(node.parent.parent); leftRotate(node.parent); } else if (node.parent.left == node && node.parent.parent.right == node.parent) { // Zig-Zag step rightRotate(node.parent); leftRotate(node.parent); } else { // Zag-Zig step leftRotate(node.parent); rightRotate(node.parent); } } } // Main method for testing public static void main(String[] args) { SplayTree tree = new SplayTree(); tree.root = new SplayTreeNode(10); tree.root.left = new SplayTreeNode(5); tree.root.right = new SplayTreeNode(20); tree.root.left.parent = tree.root; tree.root.right.parent = tree.root; tree.root.left.left = new SplayTreeNode(3); tree.root.left.right = new SplayTreeNode(7); tree.root.left.left.parent = tree.root.left; tree.root.left.right.parent = tree.root.left; System.out.println("Original root: " + tree.root.key); // Output should be 10 tree.splay(tree.root.left.left); // Splaying node with key 3 to the root System.out.println("New root after splaying 3: " + tree.root.key); // Output should be 3 } }
这个解决方案展示了如何实现伸展树(Splay Tree)的旋转操作,包括右旋和左旋。代码中还包括了伸展操作,将指定节点旋转到树的根位置。主方法用于测试这些功能。
26实现KD-Tree(k维树)的构建和最近邻搜索操作。
import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; class KDTree { private static class Node { double[] point; Node left, right; public Node(double[] point) { this.point = point; this.left = null; this.right = null; } } private Node root; private int k; // Number of dimensions public KDTree(int k) { this.k = k; this.root = null; } public void insert(double[] point) { root = insertRec(root, point, 0); } private Node insertRec(Node root, double[] point, int depth) { if (root == null) { return new Node(point); } int cd = depth % k; if (point[cd] < root.point[cd]) { root.left = insertRec(root.left, point, depth + 1); } else { root.right = insertRec(root.right, point, depth + 1); } return root; } public double[] nearestNeighbor(double[] target) { return nearestNeighborRec(root, target, 0, Double.MAX_VALUE, null); } private double[] nearestNeighborRec(Node root, double[] target, int depth, double bestDist, double[] bestPoint) { if (root == null) { return bestPoint; } double d = distanceSquared(target, root.point); if (d < bestDist) { bestDist = d; bestPoint = root.point; } int cd = depth % k; Node goodSide = target[cd] < root.point[cd] ? root.left : root.right; Node badSide = target[cd] < root.point[cd] ? root.right : root.left; bestPoint = nearestNeighborRec(goodSide, target, depth + 1, bestDist, bestPoint); if (Math.abs(target[cd] - root.point[cd]) < bestDist) { bestPoint = nearestNeighborRec(badSide, target, depth + 1, bestDist, bestPoint); } return bestPoint; } private double distanceSquared(double[] a, double[] b) { double dist = 0; for (int i = 0; i < a.length; i++) { double diff = a[i] - b[i]; dist += diff * diff; } return dist; } public static void main(String[] args) { KDTree tree = new KDTree(2); // 2-dimensional tree tree.insert(new double[]{3, 6}); tree.insert(new double[]{17, 15}); tree.insert(new double[]{13, 15}); tree.insert(new double[]{6, 12}); tree.insert(new double[]{9, 1}); tree.insert(new double[]{2, 7}); tree.insert(new double[]{10, 19}); double[] target = {10, 19}; double[] nearest = tree.nearestNeighbor(target); System.out.println("Nearest neighbor to " + arrayToString(target) + " is " + arrayToString(nearest)); } private static String arrayToString(double[] arr) { StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < arr.length; i++) { sb.append(arr[i]); if (i < arr.length - 1) { sb.append(", "); } } sb.append("]"); return sb.toString(); } }
这个解决方案实现了一个KD-Tree,并提供了插入和最近邻搜索操作。
KDTree类包含内部节点类Node,用于存储点和子节点。insert方法用于插入新点,nearestNeighbor方法用于查找最近的邻居。27实现Quad-Tree(四叉树)的构建和查询操作。
class Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class Rectangle { public int x, y, width, height; public Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } public boolean contains(Point p) { return p.x >= x && p.x <= x + width && p.y >= y && p.y <= y + height; } public boolean intersects(Rectangle range) { return !(range.x > x + width || range.x + range.width < x || range.y > y + height || range.y + range.height < y); } } class QuadTree { private static final int CAPACITY = 4; private Rectangle boundary; private List<Point> points; private boolean divided; private QuadTree northwest, northeast, southwest, southeast; public QuadTree(Rectangle boundary) { this.boundary = boundary; this.points = new ArrayList<>(); this.divided = false; } public void insert(Point p) { if (!boundary.contains(p)) { return; } if (points.size() < CAPACITY) { points.add(p); } else { if (!divided) { subdivide(); } northwest.insert(p); northeast.insert(p); southwest.insert(p); southeast.insert(p); } } private void subdivide() { int x = boundary.x; int y = boundary.y; int halfWidth = boundary.width / 2; int halfHeight = boundary.height / 2; northwest = new QuadTree(new Rectangle(x, y, halfWidth, halfHeight)); northeast = new QuadTree(new Rectangle(x + halfWidth, y, halfWidth, halfHeight)); southwest = new QuadTree(new Rectangle(x, y + halfHeight, halfWidth, halfHeight)); southeast = new QuadTree(new Rectangle(x + halfWidth, y + halfHeight, halfWidth, halfHeight)); divided = true; } public List<Point> query(Rectangle range, List<Point> found) { if (!boundary.intersects(range)) { return found; } else { for (Point p : points) { if (range.contains(p)) { found.add(p); } } if (divided) { northwest.query(range, found); northeast.query(range, found); southwest.query(range, found); southeast.query(range, found); } } return found; } } public class Main { public static void main(String[] args) { Rectangle boundary = new Rectangle(0, 0, 100, 100); QuadTree qt = new QuadTree(boundary); qt.insert(new Point(10, 10)); qt.insert(new Point(20, 20)); qt.insert(new Point(30, 30)); qt.insert(new Point(40, 40)); qt.insert(new Point(50, 50)); qt.insert(new Point(60, 60)); qt.insert(new Point(70, 70)); qt.insert(new Point(80, 80)); qt.insert(new Point(90, 90)); qt.insert(new Point(100, 100)); Rectangle searchRange = new Rectangle(50, 50, 50, 50); List<Point> foundPoints = qt.query(searchRange, new ArrayList<>()); System.out.println("Found points:"); for (Point p : foundPoints) { System.out.println("(" + p.x + ", " + p.y + ")"); } } }
这个解决方案实现了一个四叉树,用于存储和查询二维空间中的点。
QuadTree类包含插入和查询操作,通过递归地将空间划分为四个象限来管理点。28实现B-Tree(B树)的插入和删除操作。
B-Tree是一种自平衡的树数据结构,广泛用于数据库和文件系统中。它保持数据有序,并允许搜索、顺序访问、插入和删除操作在对数时间内完成。
下面是一个简化版的Java实现,包括插入和删除操作:
class BTreeNode { int t; // Minimum degree (defines the range for number of keys) int n; // Current number of keys boolean leaf; // Is true when node is leaf. Otherwise false int[] keys; // An array of keys BTreeNode[] children; // An array of child pointers // Constructor public 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; } } class BTree { private BTreeNode root; private int t; // Minimum degree // Constructor (Initializes tree as empty) public BTree(int t) { this.root = null; this.t = t; } // Function to traverse the tree private void traverse(BTreeNode node) { int i; for (i = 0; i < node.n; i++) { if (!node.leaf) { traverse(node.children[i]); } System.out.print(node.keys[i] + " "); } if (!node.leaf) { traverse(node.children[i]); } } // A utility function to insert a new key in this node private void insertNonFull(BTreeNode node, int k) { int i = node.n - 1; if (node.leaf) { while (i >= 0 && node.keys[i] > k) { node.keys[i + 1] = node.keys[i]; i--; } node.keys[i + 1] = k; node.n++; } else { while (i >= 0 && node.keys[i] > k) { i--; } if (node.children[i + 1].n == 2 * t - 1) { splitChild(node, i + 1, node.children[i + 1]); if (node.keys[i + 1] < k) { i++; } } insertNonFull(node.children[i + 1], k); } } // A utility function to split the child y of this node. i is index of y in child array children[]. The child y must be full when this function is called. private void splitChild(BTreeNode parent, int i, BTreeNode y) { BTreeNode z = new BTreeNode(y.t, y.leaf); z.n = t - 1; for (int j = 0; j < t - 1; j++) { z.keys[j] = y.keys[j + t]; } if (!y.leaf) { for (int j = 0; j < t; j++) { z.children[j] = y.children[j + t]; } } y.n = t - 1; for (int j = parent.n; j >= i + 1; j--) { parent.children[j + 1] = parent.children[j]; } parent.children[i + 1] = z; for (int j = parent.n - 1; j >= i; j--) { parent.keys[j + 1] = parent.keys[j]; } parent.keys[i] = y.keys[t - 1]; parent.n++; } // Function to insert a new key in this B-Tree public void insert(int k) { if (root == null) { root = new BTreeNode(t, true); root.keys[0] = k; root.n = 1; } else { if (root.n == 2 * t - 1) { BTreeNode s = new BTreeNode(t, false); s.children[0] = root; splitChild(s, 0, root); int i = 0; if (s.keys[0] < k) { i++; } insertNonFull(s.children[i], k); root = s; } else { insertNonFull(root, k); } } } // Function to remove a key from this B-Tree public void delete(int k) { if (root == null) { System.out.println("The tree is empty"); return; } delete(root, k); if (root.n == 0) { if (root.leaf) { root = null; } else { root = root.children[0]; } } } private void delete(BTreeNode node, int k) { int idx = findKey(node, k); if (idx < node.n && node.keys[idx] == k) { if (node.leaf) { removeFromLeaf(node, idx); } else { removeFromNonLeaf(node, idx); } } else { if (node.leaf) { System.out.println("The key " + k + " is does not exist in the tree"); return; } boolean flag = (idx == node.n); if (node.children[idx].n < t) { fill(node, idx); } if (flag && idx > node.n) { delete(node.children[idx - 1], k); } else { delete(node.children[idx], k); } } } private int findKey(BTreeNode node, int k) { int idx = 0; while (idx < node.n && node.keys[idx] < k) { idx++; } return idx; } private void removeFromLeaf(BTreeNode node, int idx) { for (int i = idx + 1; i < node.n; i++) { node.keys[i - 1] = node.keys[i]; } node.n--; } private void removeFromNonLeaf(BTreeNode node, int idx) { int k = node.keys[idx]; if (node.children[idx].n >= t) { int pred = getPred(node, idx); node.keys[idx] = pred; delete(node.children[idx], pred); } else if (node.children[idx + 1].n >= t) { int succ = getSucc(node, idx); node.keys[idx] = succ; delete(node.children[idx + 1], succ); } else { merge(node, idx); delete(node.children[idx], k); } } private int getPred(BTreeNode node, int idx) { BTreeNode cur = node.children[idx]; while (!cur.leaf) { cur = cur.children[cur.n]; } return cur.keys[cur.n - 1]; } private int getSucc(BTreeNode node, int idx) { BTreeNode cur = node.children[idx + 1]; while (!cur.leaf) { cur = cur.children[0]; } return cur.keys[0]; } private void fill(BTreeNode node, int idx) { if (idx != 0 && node.children[idx - 1].n >= t) { borrowFromPrev(node, idx); } else if (idx != node.n && node.children[idx + 1].n >= t) { borrowFromNext(node, idx); } else { merge(node, idx); } } private void borrowFromPrev(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.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] = node.keys[idx - 1]; if (!child.leaf) { child.children[0] = sibling.children[sibling.n]; } node.keys[idx - 1] = sibling.keys[sibling.n - 1]; child.n++; sibling.n--; } private void borrowFromNext(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.children[idx + 1]; child.keys[child.n] = node.keys[idx]; if (!child.leaf) { child.children[child.n + 1] = sibling.children[0]; } node.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++; sibling.n--; } private void merge(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.children[idx + 1]; child.keys[t - 1] = node.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 < node.n; i++) { node.keys[i - 1] = node.keys[i]; } for (int i = idx + 2; i <= node.n; i++) { node.children[i - 1] = node.children[i]; } child.n += sibling.n + 1; node.n--; } }
29实现B+Tree(B+树)的插入和删除操作。
B-Tree是一种自平衡的树数据结构,广泛用于数据库和文件系统中。它保持数据有序,并允许搜索、顺序访问、插入和删除操作在对数时间内完成。
下面是一个简化版的Java实现,包括插入和删除操作:
class BTreeNode { int t; // Minimum degree (defines the range for number of keys) int n; // Current number of keys boolean leaf; // Is true when node is leaf. Otherwise false int[] keys; // An array of keys BTreeNode[] children; // An array of child pointers // Constructor public 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; } } class BTree { private BTreeNode root; private int t; // Minimum degree // Constructor (Initializes tree as empty) public BTree(int t) { this.root = null; this.t = t; } // Function to traverse the tree private void traverse(BTreeNode node) { int i; for (i = 0; i < node.n; i++) { if (!node.leaf) { traverse(node.children[i]); } System.out.print(node.keys[i] + " "); } if (!node.leaf) { traverse(node.children[i]); } } // A utility function to insert a new key in this node private void insertNonFull(BTreeNode node, int k) { int i = node.n - 1; if (node.leaf) { while (i >= 0 && node.keys[i] > k) { node.keys[i + 1] = node.keys[i]; i--; } node.keys[i + 1] = k; node.n++; } else { while (i >= 0 && node.keys[i] > k) { i--; } if (node.children[i + 1].n == 2 * t - 1) { splitChild(node, i + 1, node.children[i + 1]); if (node.keys[i + 1] < k) { i++; } } insertNonFull(node.children[i + 1], k); } } // A utility function to split the child y of this node. i is index of y in child array children[]. The child y must be full when this function is called. private void splitChild(BTreeNode parent, int i, BTreeNode y) { BTreeNode z = new BTreeNode(y.t, y.leaf); z.n = t - 1; for (int j = 0; j < t - 1; j++) { z.keys[j] = y.keys[j + t]; } if (!y.leaf) { for (int j = 0; j < t; j++) { z.children[j] = y.children[j + t]; } } y.n = t - 1; for (int j = parent.n; j >= i + 1; j--) { parent.children[j + 1] = parent.children[j]; } parent.children[i + 1] = z; for (int j = parent.n - 1; j >= i; j--) { parent.keys[j + 1] = parent.keys[j]; } parent.keys[i] = y.keys[t - 1]; parent.n++; } // Function to insert a new key in this B-Tree public void insert(int k) { if (root == null) { root = new BTreeNode(t, true); root.keys[0] = k; root.n = 1; } else { if (root.n == 2 * t - 1) { BTreeNode s = new BTreeNode(t, false); s.children[0] = root; splitChild(s, 0, root); int i = 0; if (s.keys[0] < k) { i++; } insertNonFull(s.children[i], k); root = s; } else { insertNonFull(root, k); } } } // Function to remove a key from this B-Tree public void delete(int k) { if (root == null) { System.out.println("The tree is empty"); return; } delete(root, k); if (root.n == 0) { if (root.leaf) { root = null; } else { root = root.children[0]; } } } private void delete(BTreeNode node, int k) { int idx = findKey(node, k); if (idx < node.n && node.keys[idx] == k) { if (node.leaf) { removeFromLeaf(node, idx); } else { removeFromNonLeaf(node, idx); } } else { if (node.leaf) { System.out.println("The key " + k + " is does not exist in the tree"); return; } boolean flag = (idx == node.n); if (node.children[idx].n < t) { fill(node, idx); } if (flag && idx > node.n) { delete(node.children[idx - 1], k); } else { delete(node.children[idx], k); } } } private int findKey(BTreeNode node, int k) { int idx = 0; while (idx < node.n && node.keys[idx] < k) { idx++; } return idx; } private void removeFromLeaf(BTreeNode node, int idx) { for (int i = idx + 1; i < node.n; i++) { node.keys[i - 1] = node.keys[i]; } node.n--; } private void removeFromNonLeaf(BTreeNode node, int idx) { int k = node.keys[idx]; if (node.children[idx].n >= t) { int pred = getPred(node, idx); node.keys[idx] = pred; delete(node.children[idx], pred); } else if (node.children[idx + 1].n >= t) { int succ = getSucc(node, idx); node.keys[idx] = succ; delete(node.children[idx + 1], succ); } else { merge(node, idx); delete(node.children[idx], k); } } private int getPred(BTreeNode node, int idx) { BTreeNode cur = node.children[idx]; while (!cur.leaf) { cur = cur.children[cur.n]; } return cur.keys[cur.n - 1]; } private int getSucc(BTreeNode node, int idx) { BTreeNode cur = node.children[idx + 1]; while (!cur.leaf) { cur = cur.children[0]; } return cur.keys[0]; } private void fill(BTreeNode node, int idx) { if (idx != 0 && node.children[idx - 1].n >= t) { borrowFromPrev(node, idx); } else if (idx != node.n && node.children[idx + 1].n >= t) { borrowFromNext(node, idx); } else { merge(node, idx); } } private void borrowFromPrev(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.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] = node.keys[idx - 1]; if (!child.leaf) { child.children[0] = sibling.children[sibling.n]; } node.keys[idx - 1] = sibling.keys[sibling.n - 1]; child.n++; sibling.n--; } private void borrowFromNext(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.children[idx + 1]; child.keys[child.n] = node.keys[idx]; if (!child.leaf) { child.children[child.n + 1] = sibling.children[0]; } node.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++; sibling.n--; } private void merge(BTreeNode node, int idx) { BTreeNode child = node.children[idx]; BTreeNode sibling = node.children[idx + 1]; child.keys[t - 1] = node.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 < node.n; i++) { node.keys[i - 1] = node.keys[i]; } for (int i = idx + 2; i <= node.n; i++) { node.children[i - 1] = node.children[i]; } child.n += sibling.n + 1; node.n--; } }
30实现R-Tree(R树)的构建和查询操作。
import java.util.ArrayList; import java.util.List; class Rectangle { double x1, y1, x2, y2; // Coordinates of the bottom-left and top-right corners public Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // Method to check if two rectangles overlap public boolean overlaps(Rectangle other) { return !(this.x2 < other.x1 || this.x1 > other.x2 || this.y2 < other.y1 || this.y1 > other.y2); } // Method to calculate the area of the rectangle public double area() { return (x2 - x1) * (y2 - y1); } // Method to calculate the margin of the rectangle public double margin() { return 2 * ((x2 - x1) + (y2 - y1)); } } class RTreeNode { Rectangle mbr; // Minimal bounding rectangle List<RTreeNode> children; boolean isLeaf; int maxChildren; public RTreeNode(boolean isLeaf, int maxChildren) { this.isLeaf = isLeaf; this.maxChildren = maxChildren; this.children = new ArrayList<>(); this.mbr = null; } // Method to insert a child node into the current node public void insert(RTreeNode child) { if (this.mbr == null) { this.mbr = child.mbr; } else { this.mbr = mergeMBR(this.mbr, child.mbr); } this.children.add(child); } // Method to merge two MBRs private Rectangle mergeMBR(Rectangle r1, Rectangle r2) { double x1 = Math.min(r1.x1, r2.x1); double y1 = Math.min(r1.y1, r2.y1); double x2 = Math.max(r1.x2, r2.x2); double y2 = Math.max(r1.y2, r2.y2); return new Rectangle(x1, y1, x2, y2); } } class RTree { RTreeNode root; int maxChildren; public RTree(int maxChildren) { this.root = new RTreeNode(true, maxChildren); this.maxChildren = maxChildren; } // Method to insert a rectangle into the R-Tree public void insert(Rectangle rect) { RTreeNode leaf = chooseLeaf(this.root, rect); if (leaf.children.size() < maxChildren) { leaf.insert(new RTreeNode(true, maxChildren)); } else { splitNode(leaf, rect); } } // Method to choose the best leaf node for insertion private RTreeNode chooseLeaf(RTreeNode node, Rectangle rect) { if (node.isLeaf) { return node; } RTreeNode bestChild = null; double minEnlargement = Double.MAX_VALUE; for (RTreeNode child : node.children) { double enlargement = calculateEnlargement(child.mbr, rect); if (enlargement < minEnlargement) { minEnlargement = enlargement; bestChild = child; } } return chooseLeaf(bestChild, rect); } // Method to calculate the enlargement of an MBR when adding a new rectangle private double calculateEnlargement(Rectangle mbr, Rectangle rect) { Rectangle merged = mergeMBR(mbr, rect); return merged.area() - mbr.area(); } // Method to split a node that has exceeded its maximum number of children private void splitNode(RTreeNode node, Rectangle rect) { // For simplicity, we will not implement the actual split logic here. // In a real implementation, you would need to handle the splitting of nodes properly. } // Method to search for rectangles that overlap with a given query rectangle public List<Rectangle> search(Rectangle query) { List<Rectangle> result = new ArrayList<>(); searchRecursive(root, query, result); return result; } // Recursive method to search for overlapping rectangles in the tree private void searchRecursive(RTreeNode node, Rectangle query, List<Rectangle> result) { if (node.isLeaf) { for (RTreeNode child : node.children) { if (query.overlaps(child.mbr)) { result.add(child.mbr); } } } else { for (RTreeNode child : node.children) { if (query.overlaps(child.mbr)) { searchRecursive(child, query, result); } } } } }
这个解决方案实现了一个基本的R树结构,包括插入和查询操作。请注意,
splitNode方法的具体实现被省略了,因为这是一个复杂的过程,需要根据具体的分裂策略来实现。31实现X-Tree(X树)的构建和查询操作。
X树(X-Tree)是一种平衡树结构,用于存储和检索多维数据。与R树类似,X树也使用最小边界矩形(MBR)来组织空间数据。以下是用Java实现X树的构建和查询操作的示例代码:
import java.util.ArrayList; import java.util.List; class Rectangle { double[] minCoords; // Coordinates of the minimum corner double[] maxCoords; // Coordinates of the maximum corner public Rectangle(double[] minCoords, double[] maxCoords) { this.minCoords = minCoords; this.maxCoords = maxCoords; } // Method to check if two rectangles overlap public boolean overlaps(Rectangle other) { for (int i = 0; i < minCoords.length; i++) { if (this.maxCoords[i] < other.minCoords[i] || this.minCoords[i] > other.maxCoords[i]) { return false; } } return true; } // Method to calculate the margin of the rectangle public double margin() { double margin = 0; for (int i = 0; i < minCoords.length; i++) { margin += (maxCoords[i] - minCoords[i]); } return margin; } } class XTreeNode { Rectangle mbr; // Minimal bounding rectangle List<XTreeNode> children; boolean isLeaf; int maxChildren; public XTreeNode(boolean isLeaf, int maxChildren) { this.isLeaf = isLeaf; this.maxChildren = maxChildren; this.children = new ArrayList<>(); this.mbr = null; } // Method to insert a child node into the current node public void insert(XTreeNode child) { if (this.mbr == null) { this.mbr = child.mbr; } else { this.mbr = mergeMBR(this.mbr, child.mbr); } this.children.add(child); } // Method to merge two MBRs private Rectangle mergeMBR(Rectangle r1, Rectangle r2) { double[] minCoords = new double[r1.minCoords.length]; double[] maxCoords = new double[r1.maxCoords.length]; for (int i = 0; i < r1.minCoords.length; i++) { minCoords[i] = Math.min(r1.minCoords[i], r2.minCoords[i]); maxCoords[i] = Math.max(r1.maxCoords[i], r2.maxCoords[i]); } return new Rectangle(minCoords, maxCoords); } } class XTree { XTreeNode root; int maxChildren; public XTree(int maxChildren) { this.root = new XTreeNode(true, maxChildren); this.maxChildren = maxChildren; } // Method to insert a rectangle into the X-Tree public void insert(Rectangle rect) { XTreeNode leaf = chooseLeaf(this.root, rect); if (leaf.children.size() < maxChildren) { leaf.insert(new XTreeNode(true, maxChildren)); } else { splitNode(leaf, rect); } } // Method to choose the best leaf node for insertion private XTreeNode chooseLeaf(XTreeNode node, Rectangle rect) { if (node.isLeaf) { return node; } XTreeNode bestChild = null; double minEnlargement = Double.MAX_VALUE; for (XTreeNode child : node.children) { double enlargement = calculateEnlargement(child.mbr, rect); if (enlargement < minEnlargement) { minEnlargement = enlargement; bestChild = child; } } return chooseLeaf(bestChild, rect); } // Method to calculate the enlargement of an MBR when adding a new rectangle private double calculateEnlargement(Rectangle mbr, Rectangle rect) { Rectangle merged = mergeMBR(mbr, rect); return merged.margin() - mbr.margin(); } // Method to split a node that has exceeded its maximum number of children private void splitNode(XTreeNode node, Rectangle rect) { // For simplicity, we will not implement the actual split logic here. // In a real implementation, you would need to handle the splitting of nodes properly. } // Method to search for rectangles that overlap with a given query rectangle public List<Rectangle> search(Rectangle query) { List<Rectangle> result = new ArrayList<>(); searchRecursive(root, query, result); return result; } // Recursive method to search for overlapping rectangles in the tree private void searchRecursive(XTreeNode node, Rectangle query, List<Rectangle> result) { if (node.isLeaf) { for (XTreeNode child : node.children) { if (query.overlaps(child.mbr)) { result.add(child.mbr); } } } else { for (XTreeNode child : node.children) { if (query.overlaps(child.mbr)) { searchRecursive(child, query, result); } } } } }
这个解决方案实现了一个基本的X树结构,包括插入和查询操作。请注意,
splitNode方法的具体实现被省略了,因为这是一个复杂的过程,需要根据具体的分裂策略来实现。