/* * Copyright (C) 2011 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.BstSide.LEFT; import static com.google.common.collect.BstSide.RIGHT; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import javax.annotation.Nullable; /** * Tools to perform single-key queries and mutations in binary search trees. * * @author Louis Wasserman */ @GwtCompatible final class BstOperations { private BstOperations() {} /** * Returns the node with key {@code key} in {@code tree}, if any. */ @Nullable public static > N seek( Comparator comparator, @Nullable N tree, @Nullable K key) { checkNotNull(comparator); if (tree == null) { return null; } int cmp = comparator.compare(key, tree.getKey()); if (cmp == 0) { return tree; } else { BstSide side = (cmp < 0) ? LEFT : RIGHT; return seek(comparator, tree.childOrNull(side), key); } } /** * Returns the result of performing the mutation specified by {@code mutationRule} in {@code * tree} at the location with key {@code key}. * * */ public static > BstMutationResult mutate( Comparator comparator, BstMutationRule mutationRule, @Nullable N tree, @Nullable K key) { checkNotNull(comparator); checkNotNull(mutationRule); if (tree != null) { int cmp = comparator.compare(key, tree.getKey()); if (cmp != 0) { BstSide side = (cmp < 0) ? LEFT : RIGHT; BstMutationResult mutation = mutate(comparator, mutationRule, tree.childOrNull(side), key); return mutation.lift( tree, side, mutationRule.getNodeFactory(), mutationRule.getBalancePolicy()); } } return modify(tree, key, mutationRule); } /** * Perform the local mutation at the tip of the specified path. */ public static > BstMutationResult mutate( BstInOrderPath path, BstMutationRule mutationRule) { checkNotNull(path); checkNotNull(mutationRule); BstBalancePolicy balancePolicy = mutationRule.getBalancePolicy(); BstNodeFactory nodeFactory = mutationRule.getNodeFactory(); BstModifier modifier = mutationRule.getModifier(); N target = path.getTip(); K key = target.getKey(); BstMutationResult result = modify(target, key, mutationRule); while (path.hasPrefix()) { BstInOrderPath prefix = path.getPrefix(); result = result.lift(prefix.getTip(), path.getSideOfExtension(), nodeFactory, balancePolicy); path = prefix; } return result; } /** * Perform the local mutation right here, at the specified node. */ private static > BstMutationResult modify( @Nullable N tree, K key, BstMutationRule mutationRule) { BstBalancePolicy rebalancePolicy = mutationRule.getBalancePolicy(); BstNodeFactory nodeFactory = mutationRule.getNodeFactory(); BstModifier modifier = mutationRule.getModifier(); N originalRoot = tree; N changedRoot; N originalTarget = (tree == null) ? null : nodeFactory.createLeaf(tree); BstModificationResult modResult = modifier.modify(key, originalTarget); N originalLeft = null; N originalRight = null; if (tree != null) { originalLeft = tree.childOrNull(LEFT); originalRight = tree.childOrNull(RIGHT); } switch (modResult.getType()) { case IDENTITY: changedRoot = tree; break; case REBUILDING_CHANGE: if (modResult.getChangedTarget() != null) { changedRoot = nodeFactory.createNode(modResult.getChangedTarget(), originalLeft, originalRight); } else if (tree == null) { changedRoot = null; } else { throw new AssertionError( "Modification result is a REBUILDING_CHANGE, but rebalancing required"); } break; case REBALANCING_CHANGE: if (modResult.getChangedTarget() != null) { changedRoot = rebalancePolicy.balance( nodeFactory, modResult.getChangedTarget(), originalLeft, originalRight); } else if (tree != null) { changedRoot = rebalancePolicy.combine(nodeFactory, originalLeft, originalRight); } else { changedRoot = null; } break; default: throw new AssertionError(); } return BstMutationResult.mutationResult(key, originalRoot, changedRoot, modResult); } /** * Returns the result of removing the minimum element from the specified subtree. */ public static > BstMutationResult extractMin( N root, BstNodeFactory nodeFactory, BstBalancePolicy balancePolicy) { checkNotNull(root); checkNotNull(nodeFactory); checkNotNull(balancePolicy); if (root.hasChild(LEFT)) { BstMutationResult subResult = extractMin(root.getChild(LEFT), nodeFactory, balancePolicy); return subResult.lift(root, LEFT, nodeFactory, balancePolicy); } return BstMutationResult.mutationResult( root.getKey(), root, root.childOrNull(RIGHT), BstModificationResult.rebalancingChange(root, null)); } /** * Returns the result of removing the maximum element from the specified subtree. */ public static > BstMutationResult extractMax( N root, BstNodeFactory nodeFactory, BstBalancePolicy balancePolicy) { checkNotNull(root); checkNotNull(nodeFactory); checkNotNull(balancePolicy); if (root.hasChild(RIGHT)) { BstMutationResult subResult = extractMax(root.getChild(RIGHT), nodeFactory, balancePolicy); return subResult.lift(root, RIGHT, nodeFactory, balancePolicy); } return BstMutationResult.mutationResult(root.getKey(), root, root.childOrNull(LEFT), BstModificationResult.rebalancingChange(root, null)); } /** * Inserts the specified entry into the tree as the minimum entry. Assumes that {@code * entry.getKey()} is less than the key of all nodes in the subtree {@code root}. */ public static > N insertMin(@Nullable N root, N entry, BstNodeFactory nodeFactory, BstBalancePolicy balancePolicy) { checkNotNull(entry); checkNotNull(nodeFactory); checkNotNull(balancePolicy); if (root == null) { return nodeFactory.createLeaf(entry); } else { return balancePolicy.balance(nodeFactory, root, insertMin(root.childOrNull(LEFT), entry, nodeFactory, balancePolicy), root.childOrNull(RIGHT)); } } /** * Inserts the specified entry into the tree as the maximum entry. Assumes that {@code * entry.getKey()} is greater than the key of all nodes in the subtree {@code root}. */ public static > N insertMax(@Nullable N root, N entry, BstNodeFactory nodeFactory, BstBalancePolicy balancePolicy) { checkNotNull(entry); checkNotNull(nodeFactory); checkNotNull(balancePolicy); if (root == null) { return nodeFactory.createLeaf(entry); } else { return balancePolicy.balance(nodeFactory, root, root.childOrNull(LEFT), insertMax(root.childOrNull(RIGHT), entry, nodeFactory, balancePolicy)); } } }