2

I know how to convert an array to a cartesian tree in O(n) time

  1. http://en.wikipedia.org/wiki/Cartesian_tree#Efficient_construction and
  2. http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#From RMQ to LCA

However, the amount of memory required is too high (constants) since I need to associate a left and right pointer at least with every node in the cartesian tree.

Can anyone link me to work done to reduce these constants (hopefully to 1)?

dhruvbird
  • 5,396
  • 5
  • 31
  • 37
  • Can you tell us any scenario where you're trying to implement this DS. Based on your scenario if some other DS is apt then we may suggest you to use that – asifsid88 Nov 12 '13 at 06:14
  • @asifsid88 See http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#From RMQ to LCA - that is the scenario. – dhruvbird Nov 12 '13 at 16:14
  • 3
    If you want to build a tree, you either need some *explicit* way to point to the left and right nodes, or you need some *implicit* way (for example, the implicit tree structure in an array-based binary heap). I don't see an implicit way to represent your potentially unbalanced cartesian tree, you're left with explicit pointers. If you can limit the size of the tree, then those pointers could be array indexes, possibly bitmasks. So, for example, if you know you'll never have more than 64K nodes, the pointers could be unsigned shorts. – Jim Mischel Nov 12 '13 at 23:03
  • @JimMischel I could use a succinct representation to store the final cartesian tree, but how can I use it while the tree is being built? The O(n) time algorithm uses a stack of nodes and manipulates the lef/right points at will. – dhruvbird Nov 14 '13 at 05:24

3 Answers3

2

You do not need to keep the right and left pointers associated with your cartesian tree nodes. You just need to keep the parent of each node and by the definition of cartesian tree (A Cartesian Tree of an array A[0, N - 1] is a binary tree C(A) whose root is a minimum element of A, labeled with the position i of this minimum. The left child of the root is the Cartesian Tree of A[0, i - 1] if i > 0, otherwise there's no child. The right child is defined similary for A[i + 1, N - 1].), you can just traverse through this array and if the parent of the node has lower index than the node itself than the node will be the right son of its parent and similarly if the parent of the node has higher index than the node will be left son of its parent.

Hope this helps.

Vipul Jain
  • 376
  • 2
  • 14
  • This is a bit misleading: what you're saying here is how to get the *left-most* or *right-most* child of a sub-tree, not necessarily the actual left or right child of a particular node. – Charles Salvia Mar 06 '17 at 19:19
0

It is possible to construct a Cartesian tree with only extra space for child-to-parent references (by index): so besides the input array, you would need an array of equal size, holding index values that relate to the first array. If we call that extra array parentOf, then array[parentOf[i]] will be the parent of array[i], except when array[i] is the root. In that case parentOf[i] should be like a NIL pointer (or, for example, -1).

The Wikipedia article on Cartesian trees, gives a simple construction method:

One method is to simply process the sequence values in left-to-right order [...] in a structure that allows both upwards and downwards traversal of the tree

This may give the impression that it is necessary for that algorithm to maintain both upwards and downwards links in the tree, but this is not the case. It can be done with only maintaining links from child to parent.

During the construction, a new value is injected into the path that ends in the rightmost node (having the value that was most recently added). Any child in that path is by necessity a right child of its parent.

While walking up that path in the opposite direction, from the leaf, keep track of a parent and its right child (where you came from). Once you find the insertion point, that child will get the new node as parent, and the new child will get the "old" parent as its parent.

At no instance in this process do you need to store pointers to children.

Here is the algorithm written in JavaScript. As example, the tree is populated from the input array [9,3,7,1,8,12,10,20,15,18,5]. For verification only, both the input array and the parent references are printed:

class CartesianTree {
    constructor() {
        this.values = [];
        this.parentOf = [];
    }
    extend(values) {
        for (let value of values) this.push(value);
    }
    push(value) {
        let added = this.values.length; // index of the new value
        let parent = added - 1; // index of the most recently added value
        let child = -1; // a NIL pointer
        this.values.push(value);
        while (parent >= 0 && this.values[parent] > value) {
            child = parent;
            parent = this.parentOf[parent]; // move up
        }
        // inject the new node between child and parent
        this.parentOf[added] = parent;
        if (child >= 0) this.parentOf[child] = added;
    }
}

let tree = new CartesianTree;
tree.extend([9,3,7,1,8,12,10,20,15,18,5]);

printArray("indexes:", tree.values.keys());
printArray(" values:", tree.values);
printArray("parents:", tree.parentOf);

function printArray(label, arr) {
    console.log(label, Array.from(arr, value => (""+value).padStart(3)).join(" "));
}
trincot
  • 211,288
  • 25
  • 175
  • 211
-1

You can use a heap to store your tree, essentially it is an array where the first element int he array is the root, the second is the left child of the root the third the right, etc.. it is much cheaper but requires a little more care when programming it.

http://en.wikipedia.org/wiki/Binary_heap

Rob
  • 2,447
  • 2
  • 18
  • 27