简体   繁体   中英

Replace the root in Tree structure

I have following tree lets call it Tree One .

        1
       / \
      /   \
     2     3
    /
   /
  4

Now I want to replace the root node with 2. Then above tree will become something like this. Lets call it Tree Two

        2
       / \
      /   \
     4     1
            \
             \
              3

How can I implement above for arrays input as described above?

UPDATE

I already tried with Linkedlist. Which is below however, it is not working for above inputs(ie array).

    function replaceRootNode(tree, x) {
      var y = x.left;
      x.left = y.right;
      if (y.right) {
        y.right.parent = x;
      }
      y.parent = x.parent;
      if (!x.parent) {
        tree.root = y;
      } else {
        if (x === x.parent.left) {
          x.parent.left = y;
        } else {
          x.parent.right = y;
        }
      }
      y.right = x;
      x.parent = y;
    }

UPDATE 2

My above solution using likedlist is based on Splay Tree. But I need it for arrays input.

Your definition was not complete so I added the following definitions:

  • New selected root does not have to be direct child of the current root (ie you can go in your example directly from state 1 to state 3).
  • If the new selected root had both left and right children, one of them is assigned to its previous parent.

What I did is to first transform it to a structured json and do the manipulation there. I used lodash to make code cleaner.

Note that my code has functions to convert from your format to json and back. When you look at the code, make sure you open the console, also, you have a few samples of more complex 'trees' commented out, you can try them as well.

 // var tree = [1, [2, [4], [5]], [3]]; var tree = [1, [2, [4]], [3] ]; // var tree = [1, [2, [4, [5, [6,[7]]]]], [3]]; var zipTree = _.partial(_.zipObject, ['root', 'left', 'right']); var toTree = _.flow(zipTree, _.partial(_.pick, _, _.identity)); function toTreeRecurse(data) { var result = toTree(data); if (_.isArray(result.left)) { result.left = toTreeRecurse(result.left); } if (_.isArray(result.right)) { result.right = toTreeRecurse(result.right); } return (result); } function toArrayRecurse(tree) { var result = [tree.root]; if (tree.left) { result.push(toArrayRecurse(tree.left)); } if (tree.right) { result.push(toArrayRecurse(tree.right)); } return (result); } function findValueParent(tree, value) { if (!tree) { return (null); } var result; if (_.get(tree, 'left.root') === value) { return (tree); } else if (_.get(tree, 'right.root') === value) { return (tree); } else if (result = findValueParent(tree.left, value)) { return (result); } else if (result = findValueParent(tree.right, value)) { return (result); } else { return (null); } } function setRoot(tree, value) { var rootParent = findValueParent(tree, value); var newRoot; if (rootParent) { var fromSide, toSide; if (findValueParent(tree.left, value) || (_.get(tree, 'left.root') === value)) { fromSide = 'left'; toSide = 'right'; } else if (findValueParent(tree.right, value) || (_.get(tree, 'right.root') === value)) { fromSide = 'right'; toSide = 'left'; } newRoot = _.get(rootParent, fromSide); if (_.get(newRoot, toSide)) { _.set(rootParent, fromSide, _.get(newRoot, toSide)); } else { delete rootParent[fromSide]; } _.set(newRoot, toSide, tree); return (newRoot); } else { console.log('value does not exist'); return (null); } } console.log('original: ', JSON.stringify(tree)); console.log('original as json tree: ', JSON.stringify(toTreeRecurse(tree))); console.log('setting root to 2: ', JSON.stringify(toArrayRecurse(setRoot(toTreeRecurse(tree), 2)))); console.log('setting root to 4: ', JSON.stringify(toArrayRecurse(setRoot(toTreeRecurse(tree), 4)))); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script> <div>Demo, make sure you open the devtools to see the console print</div> 

I don't believe there is a way to do this exclusively with arrays. Linked lists are definetly better suited for your issue.

So if you can only accept arrays as an input, you could do the following:

  1. Convert your input array into a linked list.
  2. Use the replaceRootNode function you provided to replace the node.
  3. Convert linked list back to array.

Here is a code example I made to convert the array:

var arr = [2, [1, [3]], [4]];
var tree = {
    root: null
};

arrayToList(arr, tree);
console.log(tree);

function arrayToList(array, tree){
    var node = {
        data: array[0],
        left: null,
        right: null
    };
    if (tree.root == null) {
        tree.root = node;
    }
    if(array[1] instanceof Array) {
        node.left = arrayToList(array[1], tree); 
    } else {
        node.left = array[1];
    }
    if(array[2] instanceof Array) {
        node.right = arrayToList(array[2], tree); 
    } else {
        node.right = array[2];
    }
    return node;
}

Fiddle (output in console).

I'm using a Postorder traversing of the tree, which is Left > Right > Root . So it will output your trees mirrored compared to your examples above.

If you prefer to visit the right node first, you can do this simple edit: Fiddle . Though you typically always visit Left before Right.

Anyway, then you can replace your root node with your function (haven't tried it).

Then, you can do a similar function to convert the Re-arranged tree back into an array.

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