交换二叉树中的节点

时间:2015-01-05 21:36:52

标签: c# binary-tree

我试图在 C#中编写一个函数,允许我交换二叉树的两个节点,但它不能按预期工作。

以下是带交换方法的

class Node
{
    public int value;
    public Node parent { get; set; }
    public Node left { get; set; }
    public Node right { get; set; }

    public Node addLeft(int value)
    {
        this.left = new Node(value);
        this.left.parent = this;
        this.left.left = null;
        this.left.right = null;
        return this.left;
    }

    public Node addRight(int value)
    {
        this.right = new Node(value);
        this.right.parent = this;
        this.right.left = null;
        this.right.right = null;
        return this.right;
    }

    public Node(int value)
    {
        this.value = value;
        this.parent = null;
        this.left = null;
        this.right = null;
    }

    public Node getRoot()
    {
        Node n = this;
        while(n.parent!=null)
        {
            n = n.parent;
        }
        return n;
    }

    public static void swap(ref Node n1,ref Node n2)
    {
        //updating references of n1 and n2 parents
        if(n1.Equals(n1.parent.left)) //n1 is a left child
        {
            n1.parent.left = n2;
        }
        else if(n1.Equals(n1.parent.right)) //n1 is a right child
        {
            n1.parent.right = n2;
        }
        else
        {
            throw new Exception("Something is wrong");
        }
        if (n2.Equals(n2.parent.left)) //n2 is a left child
        {
            n2.parent.left = n1;
        }
        else if (n2.Equals(n2.parent.right)) //n2 is a right child
        {
            n2.parent.right = n1;
        }
        else
        {
            throw new Exception("Something is wrong");
        }
        //updating references of n1 and n2 childs
        if(n1.left != null)
        {
            n1.left.parent = n2;
        }
        if (n1.right != null)
        {
            n1.right.parent = n2;
        }
        if (n2.left != null)
        {
            n2.left.parent = n1;
        }
        if (n2.right != null)
        {
            n2.right.parent = n1;
        }
        //Swapping n1 and n2 references
        Node t_p = n1.parent;
        Node t_l = n1.left;
        Node t_r = n1.right;
        n1.parent = n2.parent;
        n1.left = n2.left;
        n1.right = n2.right;
        n2.parent = t_p;
        n2.left = t_l;
        n2.right = t_r;

    }
}

这是我的主要功能:

    static void Main(string[] args)
    {
        Node root = new Node(10);
        Node a = root.addLeft(1);
        Node b = root.addRight(2);
        Node c = a.addLeft(3);
        Node d = a.addRight(4);
        Node e = b.addLeft(5);
        Node f = b.addRight(6);
        Node g = d.addLeft(7);
        Node h = d.addRight(8);
        Node.swap(ref a,ref d);
        Console.WriteLine("Value is: " + root.left.value);
        Console.WriteLine("Value is: " + root.left.right.value);
        Console.WriteLine("Root: " + a.getRoot().value);
        Console.WriteLine("Root: " + d.getRoot().value);
        Console.Read();
    }

上面代码的输出是:

Value is: 4
Value is: 1

它在第二个Console.WriteLine之后挂起,我不明白为什么。你能告诉我我做错了什么吗?


编辑:

如果我尝试多次交换节点,"有些东西是错的" 异常被抛出。

3 个答案:

答案 0 :(得分:1)

while (n.parent != null)

这种情况从未得到满足,因此您陷入了一段时间。

您的swap方法创建一个具有无限祖先树(父级)的节点。如果您向上走n中的当前节点.getRoot(),您将永远找不到空父。

以下是我们开始交换之前树的状态

             ((Root(10))
            /           \
          a(1)          b(2)
         /    \        /   \
      c(3)    d(4)   e(5)  f(6)
             /    \
           g(7)   h(8)

如果只交换子节点和& d,你最终得到父母的循环引用。

你的交换方法更像是这样的东西应该有效。为了清楚起见,我离开了这个冗长的。

  public static void swap(ref Node A, ref Node B)
    {
        var newA = new Node(B.value);
        var newB = new Node(A.value);

        newA.left = A.left;
        newA.right = A.right;
        newA.parent = A.parent;

        newB.left = B.left;
        newB.right = B.right;
        newB.parent = B.parent;

        // Fix up parent node for A
        if (A.parent.left == A)
        {
            // A is a left node
            A.parent.left = newA;
        }
        if (A.parent.right == A)
        {
            // A is a Right node
            A.parent.right = newA;
        }

        // Fix up parent node for B
        if (B.parent.left == B)
        {
            // B is a left node
            B.parent.left = newB;
        }
        if (B.parent.right == B)
        {
            // B is a right node
            B.parent.right = newB;
        }


        if (newA.right == B)
        {
            // If B was a right child of A, update reference to newB
            newA.right = newB;
        }
        if (newA.left == A)
        {
            // If B was a left child of A, update reference to newB
            newA.left = newB;
        }

        if (newB.right == A)
        {
            // If A was a right child of B, update reference to newA
            newB.right = newA;
        }
        if (newB.left == A)
        {
            // If A was a left child of B, update reference to newA
            newA.left = newB;
        }

        // Update child references to be orphaned to point to new parents for A
        A.left.parent = newA;
        A.right.parent = newA;

        // Update child references to be orphaned to point to new parents for A
        B.left.parent = newB;
        B.right.parent = newB;

        // Final Swap to update ref types
        A = newA;
        B = newB;

    }

交换后的所需状态

         ((Root(10))
        /           \
      d(4)          b(2)
     /    \        /   \
  c(3)    a(1)   e(5)  f(6)
         /    \
       g(7)   h(8)

这是一些快速&在控制台中运行的脏验证码。我没有检查所有可能的情况,但现在似乎更新了所有相关节点。

    static void Main(string[] args)
    {
        var root = new Node(10);
        var a = root.addLeft(1);
        var b = root.addRight(2);
        var c = a.addLeft(3);
        var d = a.addRight(4);
        var e = b.addLeft(5);
        var f = b.addRight(6);
        var g = d.addLeft(7);
        var h = d.addRight(8);
        Node.swap(ref a, ref d);

        if (root.left.value != 4) 
            throw new ApplicationException("New Root --> left --> value != 4 as expected");
        Console.WriteLine("New root --> left node has correct value of 4");

        if ((root.left.right.parent != root.left))
            throw new Exception("and root --> left --> right has incorrect parent");   
        Console.WriteLine("Root --> left --> right has the correct parent"); 

        if (root.left.right.value != 1)
            throw new ApplicationException("New Root --> left --> right --> value did not equal 1.");
        Console.WriteLine("New Root --> Left --> right has the correct value of 1");

        if (root.left.right.left.value != 7)
            throw new ApplicationException("New Root --> left --> right --> left --> value was not 7 as expected.");
        Console.WriteLine("New Root --> left --> right --> left.value had a value of 7 as expected");

        if (root.left.right.left.parent != root.left.right)
            throw new ApplicationException("New Root --> left --> right --> left --> parent was not root --> left --> right as expected");
        Console.WriteLine("New Root --> Left --> right --> left has the correct value of 7 and expected parent");


        Console.Read();
    }

答案 1 :(得分:0)

请看一下这一行:

if (n1.right != null)
{
    n1.right.parent = n2;
}

如果n2是n1的右子,就像在这种情况下,n1.rightn2,所以n1.right.parent = n2实际上n2.parent = n2会导致循环。

要解决此问题,您必须复制节点然后替换它们,或尝试以原子方式独立进行两次交换。

答案 2 :(得分:-1)

如果我理解你的交换函数应该做什么,它应该就这么简单:

    public static void swap(Node n1, Node n2)
    {
        Node left1 = n1.left;
        Node left2 = n2.left;
        Node right1 = n1.right;
        Node right2 = n2.right;
        Node parent1 = n1.parent;
        Node parent2 = n2.parent;

        n1.left = left2;
        n2.left = left1;

        n1.right = right2;
        n2.right = right1;

        n1.parent = parent2;
        n2.parent = parent1;
    }

我认为你需要做的是简单地交换n1的左右两边n2的左右。也不需要ref,因为它是引用类型而不是值类型。