Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2011 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
     11  * express or implied. See the License for the specific language governing permissions and
     12  * limitations under the License.
     13  */
     14 
     15 package com.google.common.collect;
     16 
     17 import static com.google.common.base.Preconditions.checkNotNull;
     18 import static com.google.common.collect.BstSide.LEFT;
     19 import static com.google.common.collect.BstSide.RIGHT;
     20 
     21 import com.google.common.annotations.GwtCompatible;
     22 
     23 import java.util.Comparator;
     24 
     25 import javax.annotation.Nullable;
     26 
     27 /**
     28  * Tools to perform single-key queries and mutations in binary search trees.
     29  *
     30  * @author Louis Wasserman
     31  */
     32 @GwtCompatible
     33 final class BstOperations {
     34   private BstOperations() {}
     35 
     36   /**
     37    * Returns the node with key {@code key} in {@code tree}, if any.
     38    */
     39   @Nullable
     40   public static <K, N extends BstNode<K, N>> N seek(
     41       Comparator<? super K> comparator, @Nullable N tree, @Nullable K key) {
     42     checkNotNull(comparator);
     43     if (tree == null) {
     44       return null;
     45     }
     46     int cmp = comparator.compare(key, tree.getKey());
     47     if (cmp == 0) {
     48       return tree;
     49     } else {
     50       BstSide side = (cmp < 0) ? LEFT : RIGHT;
     51       return seek(comparator, tree.childOrNull(side), key);
     52     }
     53   }
     54 
     55   /**
     56    * Returns the result of performing the mutation specified by {@code mutationRule} in {@code
     57    * tree} at the location with key {@code key}.
     58    *
     59    * <ul>
     60    * <li>If the returned {@link BstModificationResult} has type {@code IDENTITY}, the exact
     61    * original tree is returned.
     62    * <li>If the returned {@code BstModificationResult} has type {@code REBUILDING_CHANGE},
     63    * the tree will be rebuilt with the node factory of the mutation rule, but not rebalanced.
     64    * <li>If the returned {@code BstModificationResult} has type {@code REBALANCING_CHANGE},
     65    * the tree will be rebalanced using the balance policy of the mutation rule.
     66    * </ul>
     67    */
     68   public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate(
     69       Comparator<? super K> comparator, BstMutationRule<K, N> mutationRule, @Nullable N tree,
     70       @Nullable K key) {
     71     checkNotNull(comparator);
     72     checkNotNull(mutationRule);
     73 
     74     if (tree != null) {
     75       int cmp = comparator.compare(key, tree.getKey());
     76       if (cmp != 0) {
     77         BstSide side = (cmp < 0) ? LEFT : RIGHT;
     78         BstMutationResult<K, N> mutation =
     79             mutate(comparator, mutationRule, tree.childOrNull(side), key);
     80         return mutation.lift(
     81             tree, side, mutationRule.getNodeFactory(), mutationRule.getBalancePolicy());
     82       }
     83     }
     84     return modify(tree, key, mutationRule);
     85   }
     86 
     87   /**
     88    * Perform the local mutation at the tip of the specified path.
     89    */
     90   public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate(
     91       BstInOrderPath<N> path, BstMutationRule<K, N> mutationRule) {
     92     checkNotNull(path);
     93     checkNotNull(mutationRule);
     94     BstBalancePolicy<N> balancePolicy = mutationRule.getBalancePolicy();
     95     BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory();
     96     BstModifier<K, N> modifier = mutationRule.getModifier();
     97 
     98     N target = path.getTip();
     99     K key = target.getKey();
    100     BstMutationResult<K, N> result = modify(target, key, mutationRule);
    101     while (path.hasPrefix()) {
    102       BstInOrderPath<N> prefix = path.getPrefix();
    103       result = result.lift(prefix.getTip(), path.getSideOfExtension(), nodeFactory, balancePolicy);
    104       path = prefix;
    105     }
    106     return result;
    107   }
    108 
    109   /**
    110    * Perform the local mutation right here, at the specified node.
    111    */
    112   private static <K, N extends BstNode<K, N>> BstMutationResult<K, N> modify(
    113       @Nullable N tree, K key, BstMutationRule<K, N> mutationRule) {
    114     BstBalancePolicy<N> rebalancePolicy = mutationRule.getBalancePolicy();
    115     BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory();
    116     BstModifier<K, N> modifier = mutationRule.getModifier();
    117 
    118     N originalRoot = tree;
    119     N changedRoot;
    120     N originalTarget = (tree == null) ? null : nodeFactory.createLeaf(tree);
    121     BstModificationResult<N> modResult = modifier.modify(key, originalTarget);
    122     N originalLeft = null;
    123     N originalRight = null;
    124     if (tree != null) {
    125       originalLeft = tree.childOrNull(LEFT);
    126       originalRight = tree.childOrNull(RIGHT);
    127     }
    128     switch (modResult.getType()) {
    129       case IDENTITY:
    130         changedRoot = tree;
    131         break;
    132       case REBUILDING_CHANGE:
    133         if (modResult.getChangedTarget() != null) {
    134           changedRoot =
    135               nodeFactory.createNode(modResult.getChangedTarget(), originalLeft, originalRight);
    136         } else if (tree == null) {
    137           changedRoot = null;
    138         } else {
    139           throw new AssertionError(
    140               "Modification result is a REBUILDING_CHANGE, but rebalancing required");
    141         }
    142         break;
    143       case REBALANCING_CHANGE:
    144         if (modResult.getChangedTarget() != null) {
    145           changedRoot = rebalancePolicy.balance(
    146               nodeFactory, modResult.getChangedTarget(), originalLeft, originalRight);
    147         } else if (tree != null) {
    148           changedRoot = rebalancePolicy.combine(nodeFactory, originalLeft, originalRight);
    149         } else {
    150           changedRoot = null;
    151         }
    152         break;
    153       default:
    154         throw new AssertionError();
    155     }
    156     return BstMutationResult.mutationResult(key, originalRoot, changedRoot, modResult);
    157   }
    158 
    159   /**
    160    * Returns the result of removing the minimum element from the specified subtree.
    161    */
    162   public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMin(
    163       N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) {
    164     checkNotNull(root);
    165     checkNotNull(nodeFactory);
    166     checkNotNull(balancePolicy);
    167     if (root.hasChild(LEFT)) {
    168       BstMutationResult<K, N> subResult =
    169           extractMin(root.getChild(LEFT), nodeFactory, balancePolicy);
    170       return subResult.lift(root, LEFT, nodeFactory, balancePolicy);
    171     }
    172     return BstMutationResult.mutationResult(
    173         root.getKey(), root, root.childOrNull(RIGHT),
    174         BstModificationResult.rebalancingChange(root, null));
    175   }
    176 
    177   /**
    178    * Returns the result of removing the maximum element from the specified subtree.
    179    */
    180   public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMax(
    181       N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) {
    182     checkNotNull(root);
    183     checkNotNull(nodeFactory);
    184     checkNotNull(balancePolicy);
    185     if (root.hasChild(RIGHT)) {
    186       BstMutationResult<K, N> subResult =
    187           extractMax(root.getChild(RIGHT), nodeFactory, balancePolicy);
    188       return subResult.lift(root, RIGHT, nodeFactory, balancePolicy);
    189     }
    190     return BstMutationResult.mutationResult(root.getKey(), root, root.childOrNull(LEFT),
    191         BstModificationResult.rebalancingChange(root, null));
    192   }
    193 
    194   /**
    195    * Inserts the specified entry into the tree as the minimum entry. Assumes that {@code
    196    * entry.getKey()} is less than the key of all nodes in the subtree {@code root}.
    197    */
    198   public static <N extends BstNode<?, N>> N insertMin(@Nullable N root, N entry,
    199       BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) {
    200     checkNotNull(entry);
    201     checkNotNull(nodeFactory);
    202     checkNotNull(balancePolicy);
    203     if (root == null) {
    204       return nodeFactory.createLeaf(entry);
    205     } else {
    206       return balancePolicy.balance(nodeFactory, root,
    207           insertMin(root.childOrNull(LEFT), entry, nodeFactory, balancePolicy),
    208           root.childOrNull(RIGHT));
    209     }
    210   }
    211 
    212   /**
    213    * Inserts the specified entry into the tree as the maximum entry. Assumes that {@code
    214    * entry.getKey()} is greater than the key of all nodes in the subtree {@code root}.
    215    */
    216   public static <N extends BstNode<?, N>> N insertMax(@Nullable N root, N entry,
    217       BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) {
    218     checkNotNull(entry);
    219     checkNotNull(nodeFactory);
    220     checkNotNull(balancePolicy);
    221     if (root == null) {
    222       return nodeFactory.createLeaf(entry);
    223     } else {
    224       return balancePolicy.balance(nodeFactory, root, root.childOrNull(LEFT),
    225           insertMax(root.childOrNull(RIGHT), entry, nodeFactory, balancePolicy));
    226     }
    227   }
    228 }
    229