yjcdll
yjcdll

Reputation: 51

How to find a node's predecessor in a BST with each node having 3 attributes--succ,left and right?

A problem from CLRS ,3ed.

12.3-5
Suppose that instead of each node x keeping the attribute x.p, pointing to x’s parent, it keeps x.succ, pointing to x’s successor. Give pseudocode for SEARCH,INSERT, and DELETE on a binary search tree T using this representation. These procedures should operate in time O(h), where h is the height of the tree T . (Hint:You may wish to implement a subroutine that returns the parent of a node.)

I know how to implement a subroutine that returns the parent of a node in O(h) time.

To find the parent of node x, we should find the maximum key M in the subtree rooted at x first. Then, we go downward to the right from M.succ.left. When we reach x, the node we encounter before x is x's parent.

See the code.

typedef struct TREE{
  struct TREE* left,right,succ;
}*BST;

PARENT(BST x)
{
  if(x==root) return NULL;
  BST max=TREE_MAXIMUM(x);
  BST parent=max->succ,t;
  if(parent) t=parent->left;
  else t=root;
  while(t!=x){parent=t;t=t->right;}
  return parent;
}

When DELETE x, the succ of x's predecessor should be modified to point to x.succ, no longer x. So now comes the problem--how to find the predecessor of x in O(h) time?

When the left subtree of x is nonempty, it's the rightmost node in x's left subtree. However, when the left subtree of x is empty, the predecessor is x's ancestor, to find which calls O(h) times of PARENT. Does that needs O(h*h) time? Or should we search downward from the root?

Notice that the operation INSERT, also needs to find a node's predecessor.

There comes a question--what if all the keys share the same value? Then we cannot find x's predecessor using comparison, for the key a, which equals to key x, may appears in x's left subtree or right subtree.

Upvotes: 3

Views: 1724

Answers (3)

PleaseHelp
PleaseHelp

Reputation: 101

Note : O(h) is only possible if the root is directly available.(which should be the case)

Changing your data structure a bit.

typedef struct TREE{
 int key;
 struct TREE* left,right,succ;
}*BST;

If the left sub-tree is non-empty we use TREE_MAXIMUM, otherwise we search for x in the BST starting the search from root and maintain(in variable goal) the last(i.e latest) node encountered with a right child. At the end of the search, goal is the predecessor.

Predecessor function

 BST PRE(BST x)
    {
      if(x->left!=NULL)
          return TREE_MAXIMUM(x->left);
      if(x== root)
          return NULL;
      BST goal = NULL, y = root;
      while(1)
      {
        if(x->key <= y->key)
            y=y->left;
        else
        {
            goal=y;
            y=y->right;
        }
        if(x==y)
        {
            return goal;
        }
      }      
    }

Upvotes: 0

Jeff Y
Jeff Y

Reputation: 2456

If keys are discrete, the predecessor of x has key that is <= key(x)-1 right? And if you search the tree for key(x)-1 what happens? Don't you necessarily run into pred(x) during that traverse? You'd have to double-check me on that. And of course non-discrete keys (e.g. floating point) could be problematic (as to -1).

Upvotes: 0

yurib
yurib

Reputation: 8147

In case the left subtree of x is empty, the predecessor of x is not just its ancestor, it's the direct parent of x. So you only need a single call to the parent subroutine, making the over all runtime O(h)+O(h)=O(h).

P.S
Even if it wasn't the direct parent, you still don't have to call parent repeatedly to get all ancestors, you can just start searching for x from the root of the tree as you normally would, saving all of the nodes along the path to x in a single traversal of length O(h). All of the nodes you pass through when searching for x, and only those nodes, are by definition the ancestors of x.

Upvotes: 1

Related Questions