我试图在 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之后挂起,我不明白为什么。你能告诉我我做错了什么吗?
编辑:
如果我尝试多次交换节点,"有些东西是错的" 异常被抛出。
答案 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.right
是n2
,所以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
,因为它是引用类型而不是值类型。