1

I have the following problem: I have a line with numbers that I have to read. The first number from the line is the amount of operations I will have to perform on the rest of the sequence. There are two types of operations I will have to do:

  • Remove- we remove the number after the current one, then we move forward X steps in the sequence, where X=value of removed element)
  • Insert- we insert a new number after the current one with a value of (current element's value-1), then we move forward by X steps in the sequence where X = value of the current element (i.e not the new one)

We do "Remove" if the current number's value is even, and "Insert" if the value is odd. After the amount of operations we have to print the whole sequence, starting from the number we ended the operations.

Properly working example: Input: 3 1 2 3 Output:0 0 3 1

3 is the first number and it becomes the OperCount value.

  • First operation:

Sequence: 1 2 3, first element: 1

1 is odd, so we insert 0 (currNum's value-1)

We move forward by 1(currNum's value)

Output sequence: 1 0 2 3, current position: 0

  • Second operation:

0 is even so we remove the next value (2)

Move forward by the removed element's value(2):

  • From 0 to 3
  • From 3 to 1

Output sequence: 1 0 3, current position: 1

  • Third operation:

1 is even, so once again we insert new element with value of 0

Move by current element's value(1), onto the created 0.

Output sequence: 1 0 0 3, current position: first 0

Now here is the deal, we have reached the final condition and now we have to print whole sequence, but starting from the current position. Final Output: 0 0 3 1

I have the working version, but its using the linked list, and because of that, it doesn't pass all the tests. Linked list traversal is too long, thats why I need to use the binary tree, but I kinda don't know how to start with it. I would appreciate any help.

2
  • Is the data count so large that linked list doesn't work? Or are the data values so large that you circle the list more than once per step. Reducing a number mod the current list size may save a lot. Commented Dec 13, 2015 at 20:52
  • Last time the same requirement was asked on SO (and got just downvoted and no useful answers IIRC) I looked for the name of that kind of tree and couldn't find it. If you take the design of Red/Black or AVL or any other efficient tree, you could easily change some details to make it into this keyless tree. But I don't know a way to use an existing STD tree to simply do the job. Commented Dec 13, 2015 at 20:56

2 Answers 2

1

First redefine the operations to put most (but not all) the work into a container object: We want 4 operations supported by the container object:
1) Construct from a [first,limit) pair of input random access iterators
2) insert(K) finds the value X at position K, inserts a X-1 after it and returns X
3) remove(K) finds the value X at position K, deletes it and returns X
4) size() reports the size of the contents

The work outside the container would just keep track of incremental changes to K:
K += insert(K); K %= size(); or
K += remove(K); K %= size(); Notice the importance of a sequence point before reading size()

The container data is just a root pointing to a node.

struct node {
  unsigned weight;
  unsigned value;
  node* child[2];
  unsigned cweight(unsigned s)
    { return child[s] ? child[s]->weight : 0; }
};

The container member functions insert and remove would be wrappers around recursive static insert and remove functions that each take a node*& in addition to K.
The first thing each of either recursive insert or remove must do is:
if (K<cweight(0)) recurse passing (child[0], K);
else if ((K-=cweight(0))>0) recurse passing (child[1], K-1);
else do the basic operation (read the result, create or destroy a node)
After doing that, you fix the weight at each level up the recursive call stack (starting where you did the work for insert or the level above that for remove).

After incrementing or decrementing the weight at the current level, you may need to re-balance, remembering which side you recursively changed. Insert is simpler: If child[s]->weight*4 >= This->weight*3 you need to re-balance. The re-balance is one of the two basic tree rotations and you select which one based on whether child[s]->cweight(s)<child[s]->cweight(1-s). rebalance for remove is the same idea but different details.

This system does a lot more worst case re-balancing than a red-black or AVL tree. But still is entirely logN. Maybe there is a better algorithm for a weight-semi-balanced tree. But I couldn't find that with a few google searches, nor even the real name of nor other details about what I just arbitrarily called a "weight-semi-balanced tree".

Getting the nearly 2X speed up of strangely mixing the read operation into the insert and remove operations, means you will need yet another recursive version of insert that doesn't mix in the read, and is used for the portion of the path below the point you read from (so it does the same recursive weight changes and re-balancing but with different input and output).

Given random access input iterators, the construction is a more trivial recursive function. Grab the middle item from the range of iterators and make a node of it with the total weight of the whole range, then recursively pass the sub ranges before and after the middle one to the same recursive function to create child subtree.

I haven't tested any of this, but I think the following is all the code you need for remove as well as the rebalance needed for both insert and remove. Functions taking node*& are static member function of tree and those not taking node*& are non static.

unsigned tree::remove(unsigned K)
{
    node* removed = remove(root, K);
    unsigned result = removed->value;
    delete removed;
    return result;
}

// static
node* tree::remove( node*& There, unsigned K) // Find, unlink and return the K'th node
{
   node* result;
   node* This = There;
   unsigned s=0;  // Guess at child NOT removed from
   This->weight -= 1;
   if ( K < This->cweight(0) )
   {
      s = 1;
      result = remove( This->child[0], K );
   }
   else
   {
      K -= This->cweight(0);
      if ( K > 0 )
      {
         result = remove( This->child[1], K-1 );
      }
      else if ( ! This->child[1] )
      {
     // remove This replacing it with child[0]
         There = This->child[0];
         return This;  // Nothing here/below needs a re-balance check
      }
      else
      {
     // remove This replacing it with the leftmost descendent of child[1]
         result = This;
         There = This = remove( This->child[1], 0 );
         This->child[0] = Result->child[0];
         This->child[1] = Result->child[1];
         This->weight = Result->weight;
      }
   }
   rebalance( There, s );
   return result;
}

// static
void tree::rebalance( node*& There, unsigned s)
{
   node* This = There;
   node* c = This->child[s];
   if ( c && c->weight*4 >= This->weight*3 )
   {
       node* b = c->child[s];
       node* d = c->child[1-s];
       unsigned bweight = b ? b->weight : 0;
       if ( d && bweight < d->weight )
       {
        // inner rotate:  d becomes top of subtree
        This->child[s] = d->child[1-s];
        c->child[1-s] = d->child[s];
        There = d;
        d->child[s] = c;
        d->child[1-s] = This;
        d->weight = This->weight;
        c->weight = bweight + c->cweight(1-s) + 1;
        This->weight -= c->weight + 1;
       }
       else
       {
        // outer rotate:  c becomes top of subtree
        There = c;
        c->child[1-s] = This;
        c->weight = This->weight;
        This->child[s] = d;
        This->weight -= bweight+1;
       }
   }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This helped me alot with traversing the tree, as well as "updating" (rebalancing) it. Thanks a ton.
0

You can use std::set which is implemented as binary tree. It's constructor allows construction from the iterator, thus you shouldn't have problem transforming list to the set.

3 Comments

The max amount of operations may reach up to 10^7, as well as the length of the sequence. The max element value may reach 10^9, so I don't think iterating over each element will do the job. Thats why I need to do it with the tree, but then I would need to implement the insert/delete methods, which I don't know how to do...
I am not sure that I understand - set already has insert/delete operations, there is nothing to implement.
If you understand the stated problem (non trivial given the bad description) a std::set would be completely useless for this task.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.