LoginSignup
0
0

More than 1 year has passed since last update.

AVL木辞書(1つのキーに複数の値を含めることができます)

Posted at

AVL木辞書とは

AVL木辞書はAVL木の拡張であり、1つのインデックスに複数の値を格納することができます.

データ構造

1つノードのデータ構造,1つノードに複数の値を格納することができます.

class N<T> {
    key: number;
    values = new Array<T>();
    height = 1;
    left: N<T> | undefined;
    right: N<T> | undefined;
}

全部のコード

import * as assert from "typed-assert";

class N<T> {
    //NOTE Key will be set when remove node
    key: number;
    values = new Array<T>();
    height = 1;
    left: N<T> | undefined;
    right: N<T> | undefined;

    get value(): T {
        assert.isNotUndefined(this.values[0]);
        return this.values[0];
    }

    get count() {
        return this.values.length;
    }

    constructor(key: number, value: T) {
        this.key = key;
        this.values.push(value);
    }

    addRepeat(value: T) {
        this.values.push(value);
    }

    removeRepeat(value: T) {
        const index = this.values.indexOf(value);
        if (index > -1) {
            this.values.splice(index, 1);
        }
    }
}

function max(a: number, b: number): number {
    return a > b ? a : b;
}

export class AvlTree<T> {

    private _root: N<T> | undefined;

    private getHeight(node: N<T> | undefined): number {
        if (node === undefined) {
            return 0;
        }
        return node.height;
    }

    private rightRotate(y: N<T>): N<T> {

        assert.isNotUndefined(y.left);
        const x = y.left;
        const t2 = x.right;

        x.right = y;
        y.left = t2;

        y.height = max(
            this.getHeight(y.left),
            this.getHeight(y.right)) +
            1;
        x.height = max(
            this.getHeight(x.left),
            this.getHeight(x.right)) +
            1;

        return x;
    }

    private leftRotate(x: N<T>): N<T> {
        assert.isNotUndefined(x.right);
        const y = x.right;
        const t2 = y.left;

        y.left = x;
        x.right = t2;

        x.height = max(
            this.getHeight(x.left),
            this.getHeight(x.right)) +
            1;
        y.height = max(
            this.getHeight(y.left),
            this.getHeight(y.right)) +
            1;

        return y;
    }

    private getBalance(node: N<T> | undefined): number {
        if (node === undefined) {
            return 0;
        }
        return this.getHeight(node.left) - this.getHeight(node.right);
    }


    private insertInternal(node: N<T> | undefined, key: number, value: T): N<T> {
        if (node === undefined)
            return (new N(key, value));

        if (key < node.key) {
            node.left = this.insertInternal(node.left, key, value);
        } else if (key > node.key) {
            node.right = this.insertInternal(node.right, key, value);
        } else {
            //NOTE The same key contains different values
            node.addRepeat(value);
            return node;
        }
        node.height = 1 +
            max(
                this.getHeight(node.left),
                this.getHeight(node.right));
        const balance = this.getBalance(node);

        //LL
        if (balance > 1
            && node.left !== undefined
            && key < node.left.key) {
            return this.rightRotate(node);
        }

        //RR
        if (balance < -1
            && node.right !== undefined
            && key > node.right.key) {
            return this.leftRotate(node);
        }

        //LR
        if (balance > 1
            && node.left !== undefined
            && key > node.left.key) {
            node.left = this.leftRotate(node.left);
            return this.rightRotate(node);
        }

        //RL
        if (balance < -1
            && node.right !== undefined
            && key < node.right.key) {
            node.right = this.rightRotate(node.right);
            return this.leftRotate(node);
        }

        return node;
    }

    private getRangeKeysInternal(node: N<T> | undefined, low: number, high: number, array: Array<number>) {
        if (node === undefined) {
            return;
        }

        if (node.key > low && node.key < high) {
            array.push(node.key);
            this.getRangeKeysInternal(node.left, low, high, array);
            this.getRangeKeysInternal(node.right, low, high, array);
        } else if (node.key < low) {
            this.getRangeKeysInternal(node.right, low, high, array);
        } else if (node.key > high) {
            this.getRangeKeysInternal(node.left, low, high, array);
        }
    }

    private getRangeValuesInternal(node: N<T> | undefined, low: number, high: number, array: Array<T>) {
        if (node === undefined) {
            return;
        }
        if (node.key > low && node.key < high) {
            for (let i = 0; i < node.values.length; ++i) {
                array.push(node.values[i]);
            }
            this.getRangeValuesInternal(node.left, low, high, array);
            this.getRangeValuesInternal(node.right, low, high, array);
        } else if (node.key < low) {
            this.getRangeValuesInternal(node.right, low, high, array);
        } else if (node.key > high) {
            this.getRangeValuesInternal(node.left, low, high, array);
        }
    }


    private getMaxNodeInternal(n: N<T>): N<T> {
        while (n.right !== undefined) {
            n = n.right;
        }
        return n;
    }

    private getMinNodeInternal(n: N<T>): N<T> {
        while (n.left !== undefined) {
            n = n.left;
        }
        return n;
    }

    private removeInternal(node: N<T> | undefined, key: number): N<T> | undefined {
        if (node === undefined)
            return node;
        if (key < node.key)
            node.left = this.removeInternal(node.left, key);
        else if (key > node.key)
            node.right = this.removeInternal(node.right, key);
        else {

            if ((node.left === undefined)
                || (node.right === undefined)) {

                let temp: N<T> | undefined;

                if (temp === node.left)
                    temp = node.right;
                else
                    temp = node.left;

                //NOTE No child case 
                if (temp === undefined) {
                    node = undefined;
                } else
                    node = temp;
            } else {
                // Node with two children: Get the inorder 
                // successor (smallest in the right subtree) 
                const temp = this.getMinNodeInternal(node.right);
                // Copy the inorder successor's data to this node 
                node.key = temp.key;
                // Delete the inorder successor 
                node.right = this.removeInternal(node.right, temp.key);
            }
        }

        if (node === undefined)
            return node;

        node.height = max(
            this.getHeight(node.left),
            this.getHeight(node.right)) +
            1;

        const balance = this.getBalance(node);

        //LL
        if (balance > 1 && this.getBalance(node.left) >= 0)
            return this.rightRotate(node);

        //LR
        if (balance > 1 && this.getBalance(node.left) < 0) {
            assert.isNotUndefined(node.left);
            node.left = this.leftRotate(node.left);
            return this.rightRotate(node);
        }

        //RR
        if (balance < -1 && this.getBalance(node.right) <= 0)
            return this.leftRotate(node);

        //RL
        if (balance < -1 && this.getBalance(node.right) > 0) {
            assert.isNotUndefined(node.right);
            node.right = this.rightRotate(node.right);
            return this.leftRotate(node);
        }

        return node;
    }

    insert(key: number, value: T) {
        this._root = this.insertInternal(this._root, key, value);
    }

    getMin(): number {
        return this.getMinNode().key;
    }


    getMinNode(): N<T> {
        assert.isNotUndefined(this._root);
        return this.getMinNodeInternal(this._root);
    }

    getMaxNode(): N<T> {
        assert.isNotUndefined(this._root);
        return this.getMaxNodeInternal(this._root);
    }

    getMax(): number {
        return this.getMaxNode().key;
    }

    getRangeKeys(low: number, high: number): Array<number> {
        const array = new Array<number>();
        this.getRangeKeysInternal(this._root, low, high, array);
        return array;
    }

    getRangeValues(low: number, high: number): Array<T> {
        const array = new Array<T>();
        this.getRangeValuesInternal(this._root, low, high, array);
        return array;
    }

    remove(key: number) {
        this._root = this.removeInternal(this._root, key);
    }


    removeByValue(target: T) {

        if (this._root === undefined) {
            return;
        }

        this.removeByValueInternal(target, this._root);
    }

    private removeByValueInternal(target: T, node: N<T> | undefined) {
        if (node === undefined) {
            return;
        }

        const values = node.values;
        if (values.indexOf(target) !== -1) {

            if (values.length > 1) {
                node.removeRepeat(target);
            } else {
                this.remove(node.key);
                //NOTE Only remove once
                return;
            }

        } else {
            this.removeByValueInternal(target, node.left);
            this.removeByValueInternal(target, node.right);
        }
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0