实现B Tree的插入操作

时间:2018-06-13 09:52:58

标签: java b-tree

我正在尝试实施 B树insert操作。

以下是node的实现:

import java.util.ArrayList;
import java.util.List;

/**
 * Represent a node in a B Tree. The keys in a node have to be sortable.
 */
public class BTreeNode<K extends Comparable<K>, V> {

    /**
     * Array of keys (or key-value-pairs) in the node.
     */
    private List<KeyValuePair<K, V>> keyValPair = new ArrayList<>();

    /**
     * Array of references to child nodes.
     */
    private List<BTreeNode<K, V>> child;

    /**
     * parent node, null in case of root node
     */
    private BTreeNode<K, V> parent = null;

    /**
     * Construct a B-tree node initially with one element and two children.
     * @param element a key-value-pair
     */
    public BTreeNode(KeyValuePair<K, V> element) {
        keyValPair = new ArrayList<KeyValuePair<K, V>>();
        keyValPair.add(element);

        // 2 children
        child = new ArrayList<>();
        child.add(null);
        child.add(null);
    }

    /**
     * Check if a node is leaf node.
     * @return false, when the node has at least a non-null child, or else true
     */
    public boolean isLeaf() {
        for (BTreeNode<K, V> child : child) {
            if (child != null) {
                return false;
            }
        }
        return true;
    }

    /**
     * Get number of keys in the node.
     */
    public int size() {
        return keyValPair.size();
    }

    /**
     * Get number of children.
     */
    public int numOfChilds() {
        return child.size();
    }


    /**
     * Extract from current node the pairs that have index within left..(right-1)
     * and children index within left..right
     * 
     * @param left index lower bound
     * @param right index strict upper bound
     * @return subnode
     */
    public BTreeNode<K, V> subNode(int left, int right) {
        BTreeNode<K, V> res = new BTreeNode<>(null);
        for (int i = left; i < right; i++) {
            res.addKeyValPair(i - left, getKeyValPair(i));
            res.setChild(i - left, getChild(i));
        }
        res.setChild(right - left, getChild(right));

        return res;
    }

    /**
     * Insert an element (a key-value-pair) at a given index (before the key that was at that position).
     * Insert automatically a null child.
     */
    public void addKeyValPair(int index, KeyValuePair<K, V> element) {
        if (keyValPair.size() == 0) {
            child.add(null);
        }
        keyValPair.add(index, element);
        child.add(index, null);
    }

    /**
     * Get the key at a given position.
     */
    public KeyValuePair<K, V> getKeyValPair(int index) {
        return keyValPair.get(index);
    }

    /**
     * Get the parent node.
     */
    public BTreeNode<K, V> getParent() {
        return parent;
    }

    /**
     * Get a child node at a given position.
     */
    public BTreeNode<K, V> getChild(int index) {
        return child.get(index);
    }

    /**
     * Insert a new element, set 2 nodes as its children.
     */
    public void insertKeyChild(BTreeNode<K, V> leftNode, KeyValuePair<K, V> element,
        BTreeNode<K, V> rightNode) {
        // Finde index des Teiler
        int index = -1;
        if (element.getKey().compareTo(getKeyValPair(0).getKey()) < 0) {
            index = 0;
        } else if (element.getKey().compareTo(getKeyValPair(size() - 1).getKey()) > 0) {
            element = size() - 1;
        } else {
            for (int i = 0; i < size() - 1; i++) {
                if (element.getKey().compareTo(getKeyValPair(i + 1).getKey()) < 0) {
                    index = i + 1;
                    break;
                }
            }
        }
        addKeyValPair(index, element);
        setChild(index, leftNode);
        setChild(index + 1, rightNode);
    }

    /**
     * Replace a child node at given position. Index has to be available.
     */
    public void setChild(int index, BTreeNode<K, V> node) {
        child.set(index, node);
        if (node != null) {
            node.setParent(this);
        }
    }

    /**
     * Set a node as parent node
     */
    public void setParent(BTreeNode<K, V> node) {
        parent = node;
    }

    /**
     * Find the position of a key (to put) in a node
     * 
     * @param target a key that needs to be search
     * @return the index of the leftmost element in this node that is not less than target.
     */
    public int searchInNode(KeyValuePair<K, V> target) {
        int l = 0;
        int r = size() - 1;

        while (l <= r) {
            int m = (l + r) / 2;
            int comp = target.getKey().compareTo(getKeyValPair(m).getKey());
            if (comp == 0)
                return m;
            else if (comp < 0)
                r = m - 1;
            else
                l = m + 1;
        }
        return l;
    }

    @Override
    public String toString() {
        String ergebnis = "(" + ((getChild(0) != null) ? " " + getChild(0) + " " : "") + ")";
        for (int i = 0; i < keyValPair.size(); i++) {
            ergebnis += getKeyValPair(i).getKey() + "";       // NullPointerException
            ergebnis += "(" + ((getChild(i + 1) != null) ? " " + getChild(i + 1) + " " : "") + ")";
    }
        ergebnis += "";
        return ergebnis;
    }
}

这是我对B Tree的实现:

import java.util.ArrayList;
import java.util.List;

public abstract class BTree<K extends Comparable<K>, V> {
    /**
     * root node
     */
    protected BTreeKnoten<K, V> root = null;

    /**
     * Order (degree) of the tree
     */
    protected int deg;

    public BTree(int deg) {
        this.deg = deg;
    }

    /**
     * maximum number of key-value-pairs in a node
     */
    public int maxNodeSize() {
        return 2 * deg - 2;
    }

    @Override
    public String toString() {
        return root.toString();
    }

    /**
     * Get the value from a key in the tree. Give null if the key is not found.
     * @param key 
     */
    public V getValFromKey(K key) {
        if (root == null) {
            return null;
        }

        return getValFromKey(key, root);
    }

    /**
     * Get the value from a key in a subtree. Give null if the key is not found.
     * @param key 
     * @param node root node of the subtree
     */
    private V getValFromKey(K key, BTreeNode<K, V> node) {
        if (node == null) {
            return null;
        }

        for (int i = 0; i < node.size(); i++) {
            KeyValuePair<K, V> key_val = node.getKeyValPair(i);

            if (key_val.getKey().compareTo(key) == 0) {
                return key_val.getValue();
            } else if (key_val.getKey().compareTo(key) > 0) {
                return getValFromKey(key, node.getChild(i));
            }
        }

        return getValFromKey(key, node.getChild(node.numOfChilds() - 1));
    }

    /**
     * Get number of keys in the tree.
     */
    public int numOfKeys() {
        return numOfKeys(root);
    }

    /**
     * Get number of keys in a subtree
     * @param node root of subtree
     */
    private int numOfKeys(BTreeKnoten<K, V> node) {
        if (node == null) {
            return 0;
        }
        int res = node.size();
        for (int i = 0; i < node.numOfChilds(); i++) {
            res += numOfKeys(node.getChild(i));
        }
        return res;
    }

    /**
     * Insert a new element (key+value) into the tree.
     * @param key 
     * @param value
     * @return new root node of the tree
     */
    public BTreeNode<K, V> insert(K key, V value) {
        KeyValuePair<K, V> insertPair = new KeyValuePair<K, V>(key, value);

        // if tree is empty, simply insert new
        if (root == null) {
            root = new BTreeKnoten<>(insertPair);
            return root;
        }

        BTreeNode<K, V> curr = root;
        for (;;) {
            // position of insertPair in current node
            int currPos = curr.searchInNode(insertPair);

            // if position of insertion is in a leaf -> insert
            if (curr.isLeaf()) {
                curr.addKeyValPair(currPos, insertPair);

                // if current node becomes overflowed
                if (curr.size() > maxNodeSize()) {
                    splitNode(curr);
                }
                return root;
            } else {
                // traverse down to a child (until reaching a leaf)
                curr = curr.getChild(currPos);
            }
        }
    }

    /**
     * Split the current node, push the middle element into its parent node
     * @param node
     */
    public void splitNode(BTreeKnoten<K, V> node) {
        // seperate current node into 3 parts
        int medPos = node.size() / 2;
        KeyValuePair<K, V> med = node.getKeyValPair(medPos);
        BTreeKnoten<K, V> leftSubNode = node.subNode(0, medPos);
        BTreeKnoten<K, V> rightSubNode = node.subNode(medPos + 1, node.size());

        // if root node is to be split
        if (node == root) {
            // set med as new root
            root = new BTreeKnoten<>(med);

            // set leftSubNode and rightSubNode as its two children
            root.setChild(0, leftSubNode);
            root.setChild(1, rightSubNode);
        } else {
            BTreeNode<K, V> parent = node.getParent();

            // push med into parent and make leftSubNode and rightSubNode its children
            parent.insertKeyChild(leftSubNode, med, rightSubNode);

            // if parent becomes overflowed, split again
            if (parent.size() > maxNodeSize()) {
                splitNode(parent);
            }
        }
    }

    public static void main(String[] args) {
        BTree<Integer, String> baum = new BTree<Integer, String>(2) {
        };

        baum.insert(23, "23");
        System.out.println(baum); 
        System.out.println(baum.numOfKeys());  // 1

        baum.insert(42, "42");
        System.out.println(baum);
        System.out.println(baum.numOfKeys());  // 2

        baum.insert(12, "12");
        System.out.println(baum);
        System.out.println(baum.numOfKeys());  // 5 (?!)
    }
}

当我尝试insert 12进入树{NullPointerException toString() BTreeNode)时,程序会出现错误

那时我们应该split节点。

numOfKeys()insert 12的结果也是假的。所以我的猜测是splitNode()可能存在一些问题,使程序对密钥和子项的数量感到困惑。

我找不到确切的问题所在。有人可以帮帮我吗?

0 个答案:

没有答案