简体   繁体   中英

counting number of elements less than X in a BST

I had implemented a BST for a multiset using the C++ code below, whereas each node contains the number of occurrence num of each distinct number data , and I try to find the number of elements less than certain value x, using the order function below.

It works, however, inefficient in terms of execution time. Is there any method with better time complexity?

struct Node {
    int data;
    int height;
    int num;

    Node *left;
    Node *right;
};
int order(Node *n, int x) {
    int sum = 0;
    if (n != NULL) {
        if (n->data < x) {
            sum += n->num;
            sum += order(n->right, x);
        }
        sum += order(n->left, x);
    }
    return sum;
}

Unless you can change the BST structure or create a meta-structure around it, your best bet is to remove the recursion by flattening the algorithm, eg:

int order(Node *n, int x) {
    if (n == NULL) return 0;

    int sum = 0;

    if (n->data >= x) {

        // Search left until we find the first node < x
        while (n != NULL && n->data >= x) {
            n = n->left;
        }

        // Add numbers of all left nodes up to the leftmost one
        while (n != NULL) {
            sum += n->num;
            n = n->left;
        }

    }
    else {
        // n->data < x

        // Add numbers of all left nodes up to the leftmost one
        Node* m = n;
        while (m != NULL) {
            sum += m->num;
            m = m->right;
        }

        // Add numbers of all right nodes as long as they are < x
        n = n->right;
        while (n != NULL && n->data < x) {
            sum += n->num;
            n = n->right;
        }

    }

    return sum;
}

You can bring the algorithm down to O(logN) time by storing in each node the number of elements in the subtree of which it is the root. Then you'd only have to recurse on one of the two children of each node (go left if x < node->data , right if x > node->data ), which if the tree is balanced only takes logarithmic time.

struct Node {
    int data;
    int height;
    int num;
    int size; // numer of elements in the subtree starting at this node

    Node *left;
    Node *right;
};

int order(Node *n, int x) {
    if(n == NULL) return 0;

    // elements less than n->data make up the whole left subtree
    if (x == n->data) {
        return n->left ? n->left->size : 0;
    }
    // even smaller? just recurse left
    else if (x < n->data) {
        return order(n->left, x);
    }
    // bigger? take the whole left subtree and part of the right one
    else {
        return (n->left ? n->left->size : 0) + order(n->right, x);
    }
}

Of course, now you have to keep track of the size, but this can be done very efficiently when updating the tree: simply recalculate the size ( n->left->size + n->right->size + 1 ) of each modified node in an insertion or deletion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM