C# 二叉树中的节点交换
我正试图在C#中编写一个函数,它允许我交换二叉树的两个节点,但它不能按预期工作 以下是使用交换方法的类:C# 二叉树中的节点交换,c#,binary-tree,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
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
它挂在第二个控制台后。WriteLine和我不明白为什么。你能告诉我我做错了什么吗
编辑: 如果我多次尝试交换节点,就会抛出异常“有问题”
while (n.parent != null)
这个条件永远不会满足,所以你会陷入一个很长的循环中
swap方法创建一个具有无限祖先树(父)的节点。如果在.getRoot()
中沿着当前节点n
前进,则永远不会找到空的父节点
这是开始交换前树的状态
((Root(10))
/ \
a(1) b(2)
/ \ / \
c(3) d(4) e(5) f(6)
/ \
g(7) h(8)
如果仅将子节点交换为a&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();
}
如果我理解您的交换函数应该做什么,它应该简单如下:
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
,因为它是引用类型而不是值类型。请看这一行:
if (n1.right != null)
{
n1.right.parent = n2;
}
如果n2是n1的右子级,如本例所示,n1.right
是n2
,因此n1.right.parent=n2
实际上是导致循环的n2.parent=n2
要解决此问题,您必须复制节点并替换它们,或者尝试以原子方式独立地进行这两种交换。为什么要将带有
ref
的节点传递给swap
?它可能挂起,只是因为getRoot没有返回,这意味着您可能会把更改父节点的操作弄糟。我的建议,在分配任何内容之前,请先提取所需的所有信息,然后将所有内容分配给它应该是的内容。您遇到了这样一个问题:更改某些值,然后在确实需要旧值时将更改的值分配给其他值。如果你把所有东西都安排得井井有条,你实际上可以不储存那么多东西就离开,但是如果你没有完全正确地安排好,你就会遇到这样的错误。@lared当两个代码都不是另一个的父代码时,代码工作得很好。这就是他的错误的原因。这是不是一个实际的要求,即不创建新的节点来进行交换?此外,节点是否必须交换?就我所知,如果没有使用子树,您就不能交换值而不是整个节点吗?这只会导致一个问题,为什么父节点没有设置为正确的值,从而导致循环。这并不能回答这个问题。或者至少,答案是如此模糊,以至于完全没有帮助。如果你说“因为你有一个bug”,那么从技术上讲你是对的,同样没有帮助。@ChrisBallance:你还没有真正回答这个问题。您刚刚重述了症状。您最近的编辑仍然是错误的。它不会更新所有子节点的父引用或父节点的子引用。仅供参考:如果与叶节点交换,则会失败。例如,swap(d,e)
父节点也需要交换。也可以通过简单地交换值来完成,但在这种情况下,必须确保代码中其他地方没有对树节点的引用。