โคลนต้นไม้ไบนารีด้วยตัวชี้แบบสุ่ม


ระดับความยาก ยาก
ถามบ่อยใน แอคโคไลท์ อเมซอน ซิสโก้ ข้อเท็จจริง fanatics Google ไมโครซอฟท์ ทำงาน Snapchat
กัญชา ต้นไม้

คำชี้แจงปัญหา

คุณจะได้รับที่สมบูรณ์ ต้นไม้ไบนารี ด้วยตัวชี้แบบสุ่ม ตัวชี้แบบสุ่มหมายถึงโหนดที่ทุกโหนดชี้ไปที่อื่นที่ไม่ใช่ลูกทางซ้ายและขวา ดังนั้นสิ่งนี้ยังเปลี่ยนโครงสร้างมาตรฐานของโหนดในต้นไม้ไบนารีอย่างง่าย ตอนนี้โหนดของต้นไม้ไบนารีจะเก็บข้อมูลไว้ที่โหนดปัจจุบันและตัวชี้ไปทางซ้ายขวาและตัวชี้แบบสุ่ม ดังนั้นตอนนี้เราพร้อมที่จะไปกับสิ่งที่ปัญหาถาม? ปัญหา“ โคลนต้นไม้ไบนารีด้วยตัวชี้แบบสุ่ม” ขอให้สร้างต้นไม้ไบนารีใหม่ซึ่งเป็นสำเนาที่ถูกต้องของต้นไม้เริ่มต้นที่กำหนด ดังนั้นในทรีที่สร้างขึ้นใหม่หากเราเลือกโหนดทางซ้ายขวาและตัวชี้แบบสุ่มหมายถึงโหนดในทรีใหม่นี้ซึ่งสอดคล้องกับโหนดในทรีดั้งเดิม

ตัวอย่าง

อินพุต

โคลนต้นไม้ไบนารีด้วยตัวชี้แบบสุ่ม

Inorder traversal of original binary tree is [current node data, random pointer data]:
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5],

Inorder traversal of cloned binary tree is[current node data, random pointer data]:
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5],

คำอธิบาย

โหนดซ้ายและขวาของต้นไม้ไบนารีจะแสดงตามปกติทางซ้ายและขวาของแต่ละโหนด และตัวชี้อื่น ๆ ทั้งหมดตัวชี้ซ้ายและขวาคือตัวชี้แบบสุ่ม ขอบบางด้านมีลูกศรคู่ (ซึ่งชี้ไปทั้งสองทิศทาง) ทิศทางไปยังพาเรนต์ใด ๆ ของโหนดแสดงว่าโหนดมีตัวชี้สุ่มไปยังพาเรนต์ เอาต์พุตถูกกำหนดในรูปแบบ [ข้อมูลโหนดปัจจุบัน / ข้อมูลโหนดสุ่ม]

วิธีการแฮช

ดังที่เราทราบดีว่าเราจำเป็นต้องสร้างต้นไม้ใหม่ซึ่งเป็นสำเนาที่ถูกต้องของต้นไม้เริ่มต้น เราสามารถเรียกใช้การข้ามผ่านตามลำดับและสร้างสำเนาได้ แต่เราจะประสบปัญหากับพอยน์เตอร์แบบสุ่มเนื่องจากบางโหนดอาจชี้ไปยังโหนดที่ยังไม่ได้สร้าง เพื่อกำจัดข้อผิดพลาดนี้ ก่อนอื่นเราจะสร้างต้นไม้ใหม่ จากนั้นใช้ไฟล์ HashMap ซึ่งเก็บแอดเดรสของโหนดใหม่ที่สอดคล้องกับแต่ละโหนดในทรีดั้งเดิม ดังนั้นเราจึงเรียกใช้การข้ามผ่านตามลำดับอีกครั้งและสร้างพอยน์เตอร์แบบสุ่มไปยังโหนดของทรีใหม่ (ซึ่งเก็บเป็นค่าใน HashMap)

แนวทางที่มีประสิทธิภาพ

ในแนวทางข้างต้นเราต้องเก็บไฟล์ HashMap ซึ่งเก็บที่อยู่โหนดโคลนที่สอดคล้องกับแต่ละโหนดในทรีดั้งเดิม ดังนั้นแทนที่จะทำอย่างนั้นเราจะทำบางอย่างเหมือนที่ทำเพื่อโคลนก รายการที่เชื่อมโยง ด้วยตัวชี้แบบสุ่ม เราจะผลักโหนดที่สร้างขึ้นใหม่ระหว่างชายด์ด้านซ้ายและโหนดที่เกี่ยวข้องในทรีดั้งเดิม จนถึงตอนนี้เราได้เปลี่ยนโครงสร้างของต้นไม้เริ่มต้น ในการตั้งค่าตัวชี้แบบสุ่มเรารู้ว่าโหนดใหม่ที่ตรงกับโหนดนั้นจะเป็นโหนดลูกทางซ้ายเสมอ ดังนั้นเราเพียงแค่ตั้งค่าตัวชี้แบบสุ่มไปที่ชายด์ด้านซ้ายของโหนดในแผนผังอินพุต แต่เรายังมีสิ่งหนึ่งที่ต้องดูแล เราจำเป็นต้องกู้คืนชายด์ด้านซ้ายของโหนดของต้นไม้เริ่มต้น & โคลน เมื่อเสร็จแล้วเราได้โคลนต้นไม้ไบนารีด้วยพอยน์เตอร์แบบสุ่ม

รหัส

รหัส C ++ เพื่อโคลนต้นไม้ไบนารีด้วยตัวชี้แบบสุ่ม

#include <iostream>
using namespace std;

struct node{
    int data;
  node *left, *right, *random;
};

node* create(int data){
  node* tmp = new node();
  tmp->data = data;
  tmp->left = tmp->right = tmp->random = NULL;
}

// print inorder traversal of the given tree in [node, random node] format
void inorder(node* root)
{
  if(root == NULL)
    return;
  // print inorder traversal of left tree
  inorder(root->left);
  // print in [current node, random node] format
  cout << "[" << root->data << " ";
  if (root->random == NULL)
    cout << "NULL], ";
  else
    cout << root->random->data << "], ";
  // print inorder traversal of right tree
  inorder(root->right);
}

// insert clone nodes between the original node and its left child
node* insertCloneNode(node* originalNode)
{
  if (originalNode == NULL)
    return NULL;

  node* left = originalNode->left;
  originalNode->left = create(originalNode->data);
  originalNode->left->left = left;
  if(left != NULL)
    left->left = insertCloneNode(left);

  originalNode->left->right = insertCloneNode(originalNode->right);
  return originalNode->left;
}

// sets the random pointers in clone tree
void setRandomNode(node* originalNode, node* cloneNode)
{
  if (originalNode == NULL)
    return;
  if(originalNode->random != NULL)
    cloneNode->random = originalNode->random->left;
  else
    cloneNode->random = NULL;

  if(originalNode->left != NULL && cloneNode->left != NULL)
    setRandomNode(originalNode->left->left, cloneNode->left->left);
  setRandomNode(originalNode->right, cloneNode->right);
}

// after all the work restore the left pointers in original and clone tree
void restoreTreeLeftNode(node* originalNode, node* cloneNode)
{
  if (originalNode == NULL)
    return;
  if (cloneNode->left != NULL)
  {
    node* cloneLeft = cloneNode->left->left;
    originalNode->left = originalNode->left->left;
    cloneNode->left = cloneLeft;
  }
  else
    originalNode->left = NULL;

  restoreTreeLeftNode(originalNode->left, cloneNode->left);
  restoreTreeLeftNode(originalNode->right, cloneNode->right);
}

// constructs the new clone tree
node* cloneTree(node* originalNode)
{
  if (originalNode == NULL)
    return NULL;
  node* cloneNode = insertCloneNode(originalNode);
  setRandomNode(originalNode, cloneNode);
  restoreTreeLeftNode(originalNode, cloneNode);
  return cloneNode;
}


int main()
{
  node *root = create(3);
  node* two = create(2);
  node* one = create(1);
  node* seven = create(7);
  node* five = create(5);
  node* nine = create(9);

  root->left = two;
  root->left->left = one;
  root->right = seven;
  root->right->left = five;
  root->right ->right = nine;

  root->random = nine;
  root->left->random = seven;
  root->left->left->random = two;
  root->right->random = one;
  root->right->left->random = one;
  root->right->right->random = five;

  cout << "Inorder traversal of original binary tree is [current node data, random pointer data]: \n";
  inorder(root);

  node *clone = cloneTree(root);

  cout << "\n\nInorder traversal of cloned binary tree is[current node data, random pointer data]: \n";
  inorder(clone);

  return 0;
}
Inorder traversal of original binary tree is [current node data, random pointer data]:
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5],

Inorder traversal of cloned binary tree is[current node data, random pointer data]:
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5],

รหัส Java เพื่อโคลนต้นไม้ไบนารีด้วยตัวชี้แบบสุ่ม

import java.util.*;
// Class that denotes a node of the tree
class node
{ 
    int data; 
    node left, right, random; 
 
    public node(int data) 
    { 
        this.data = data;
        left = right = random = null; 
    } 
}
 
class Tree 
{ 
    static node root;
  static node create(int data) {
    node tmp = new node(data);
    return tmp;
  }
  // print inorder traversal of the given tree in [node, random node] format
  static void inorder(node root){
    if(root != null){
      // print inorder traversal of left tree
      inorder(root.left);
      // print in [current node, random node] format
      System.out.print("[" + root.data + " ");
      if(root.random == null)
        System.out.print("null], ");
      else
        System.out.print(root.random.data +"], ");
      // print inorder traversal of right tree
      inorder(root.right);
    }
  }
 
  // insert clone nodes between the original node and its left child
  static node insertCloneNode(node originalNode) 
  { 
    if (originalNode == null) 
      return null; 
 
    node left = originalNode.left; 
    originalNode.left = create(originalNode.data); 
    originalNode.left.left = left; 
    if(left != null) 
      left.left = insertCloneNode(left); 
 
    originalNode.left.right = insertCloneNode(originalNode.right); 
    return originalNode.left; 
  } 
 
  // sets the random pointers in clone tree
  static void setRandomNode(node originalNode, node cloneNode) 
  { 
    if (originalNode != null){
      if(originalNode.random != null) 
        cloneNode.random = originalNode.random.left; 
      else
        cloneNode.random = null; 
 
      if(originalNode.left != null && cloneNode.left != null) 
        setRandomNode(originalNode.left.left, cloneNode.left.left); 
      setRandomNode(originalNode.right, cloneNode.right); 
    }
  } 
 
  // after all the work restore the left pointers in original and clone tree
  static void restoreTreeLeftNode(node originalNode, node cloneNode) 
  { 
    if (originalNode != null) {
      if (cloneNode.left != null) 
      { 
        node cloneLeft = cloneNode.left.left; 
        originalNode.left = originalNode.left.left; 
        cloneNode.left = cloneLeft; 
      } 
      else
        originalNode.left = null; 
 
      restoreTreeLeftNode(originalNode.left, cloneNode.left); 
      restoreTreeLeftNode(originalNode.right, cloneNode.right); 
    }
  } 
 
  // constructs the new clone tree
  static node cloneTree(node originalNode) 
  { 
    if (originalNode == null)
      return null;
    node cloneNode = insertCloneNode(originalNode); 
    setRandomNode(originalNode, cloneNode); 
    restoreTreeLeftNode(originalNode, cloneNode); 
    return cloneNode; 
  } 
 
  public static void main(String[] args) {
    node root = create(3);
    node two = create(2);
    node one = create(1);
    node seven = create(7);
    node five = create(5);
    node nine = create(9);
 
    root.left = two;
    root.left.left = one;
    root.right = seven;
    root.right.left = five;
    root.right .right = nine;
 
    root.random = nine;
    root.left.random = seven;
    root.left.left.random = two;
    root.right.random = one;
    root.right.left.random = one;
    root.right.right.random = five;
 
    System.out.print("Inorder traversal of original binary tree is[current node data, random pointer data]: \n");
    inorder(root);
 
    node clone = cloneTree(root);
 
    System.out.print("\n\nInorder traversal of cloned binary tree is[current node data, random pointer data]: \n");
    inorder(clone);
  }
}
Inorder traversal of original binary tree is[current node data, random pointer data]: 
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5], 

Inorder traversal of cloned binary tree is[current node data, random pointer data]: 
[1 2], [2 7], [3 9], [5 1], [7 1], [9 5],

การวิเคราะห์ความซับซ้อนเพื่อโคลนทรีไบนารีด้วยตัวชี้แบบสุ่ม

ความซับซ้อนของเวลา 

บน), เราเพิ่งข้ามโหนดในต้นไม้ไบนารีและเนื่องจากมีโหนด N ในต้นไม้ไบนารี ดังนั้นความซับซ้อนของเวลาจึงเป็นเชิงเส้น

ความซับซ้อนของอวกาศ

O (1), เนื่องจากเราไม่ได้จัดเก็บข้อมูลใด ๆ ในอาร์เรย์หรือแผนที่ ดังนั้นความซับซ้อนของพื้นที่จึงคงที่