示例:
我有下面的例子,说明了锦标赛的时间表:
1 2 3 4 5 6 7 8
| | | |
9 10 11 12
| |
13 14
|
15
规则:
数字表示节点(匹配(的值。价值从上到下,因为根是冠军的最后一场比赛
树将始终保持平衡,并且可以有N个顶部位置,其中N={2,4,8,16,32,64128256…}
问题:
我需要找到一个数学函数,它将返回节点父节点的值,并传递其值。这个数学函数必须可以用C#表示。
GetNodeParent(9); // must return 13
GetNodeParent(10); // must return 13
GetNodeParent(4); // must return 10
GetNodeParent(15); // must return null
功能应该如何?
在我看来,处理这一问题的最简单方法是以另一种方式存储树。通常,平衡树可以表示为一个数组,根在第一个位置,叶在最后一个n/2
位置,如下所示:
1
|
2 3
| |
4 5 6 7
| | | |
8 9 10 11 12 13 14 15
在内存中是这样的:
1|2|3|4|5|6|7|8|9|10|11|12|13|14|15
现在,这个构造的一个有趣的属性是,父对象在Math.Floor(index/2)
处总是(所以子对象在n*2
和n*2+1
处(
例如,13
的父级是13/2 = 6.5 =floored= 6
。它确实简化了操作。
如前所述,所有叶节点都位于最后n/2个位置。这是另一个有趣的脂肪;如果你正在寻找第一轮比赛,只需砍下你的阵列并获得Math.Ceiling(n/2)
最后一项:
1|2|3|4|5|6|7|8|9|10|11|12|13|14|15
对于第二轮,移除上一轮,并在Math.Ceiling(n/2)
:处再次切碎
1|2|3|4|5|6|7|
因此,知道了这一点,GetParent
可以看起来如下:
public static int? GetParent(int childIndex)
{
int parentIndex = (int) Math.Floor((double)childIndex/2);
return parentIndex == 0 ? (int?)null : parentIndex;
}
如果不需要空值,则直接使用return (int)Math.Floor((double)index/2);
。
这可以通过这个简单的单元测试来验证,该测试是根据您的示例值的位置进行的:
[TestMethod]
public void TestMethod1()
{
Assert.AreEqual(2, Stuff.GetParent(4));
Assert.AreEqual(2, Stuff.GetParent(5));
Assert.AreEqual(6, Stuff.GetParent(13));
Assert.AreEqual(null, Stuff.GetParent(1));
}
另外,别忘了使用索引而不是值,你不能直接应用你的例子中的9和13,现在是4和2,因为这是它们在树中的索引,根在第一个位置,它的结构是这样的。你提供的例子是这样构建的:
15|13|14|9|10|11|12|1|2|3|4|5|6|7|8
如果需要按值工作,可以使用IndexOf
。由于您是从1开始的,而不是从0开始的,因此还需要在每个索引中添加1。看起来是这样的:
[TestMethod]
public void TestMethod1()
{
List<int> values = new List<int>{15, 13, 14, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8};
Assert.AreEqual(values.IndexOf(13) + 1, Stuff.GetParent(values.IndexOf(9) + 1));
Assert.AreEqual(values.IndexOf(13) + 1, Stuff.GetParent(values.IndexOf(10) + 1));
Assert.AreEqual(values.IndexOf(10) + 1, Stuff.GetParent(values.IndexOf(4) + 1));
Assert.AreEqual(null, Stuff.GetParent(values.IndexOf(15)));
}
选项1:构建一个相反的新树,这样您就可以使用二进制树。
选项2:
打电话给我的list_name.Count()
,我已经为你的使用了15
GetNodeParent(9,15); // return 13
GetNodeParent(10,15);// return 13
GetNodeParent(4,15); // return 10
这是有效的,但你必须检查最终情况(在你的情况下是15(:
int GetNodeParent(int aNodeValue, int aListSize) {
aNodeValue.Dump("Look for the parent of");
var depth = (int)(Math.Log(aListSize,2) ); // this rounds down
var level = Math.Pow(2,depth);
while (aNodeValue > level) {
depth--;
level += Math.Pow(2,depth);
}
var target = level+1; // this will be our accumulator
var search = level; // for our "binary search"
var search_width = Math.Pow(2,depth).Dump("width");
// This runs a "binary search", adding 1 to our result
// if we are on the right side.
while (search_width!=0){
if (aNodeValue > level)
target++;
search -= search_width / 2;
search_width /=2;
}
return (int)target;
}
经过深思熟虑,在为null
调用该方法之前添加一个if (aNodeValue == list_name.Count())
,或者将签名更改为int?
并在该方法中进行检查,使其返回null
你试过这个简单的公式吗?
GetNodeParent(k) = (k + 1) div 2 + N
(if >=2N then null)
其中div是整数除法(7div 2=3(