我有以下示例,说明了锦标赛时间表:
1 2 3 4 5 6 7 8
| | | |
9 10 11 12
| |
13 14
|
15
数字代表节点的值(匹配)。值从顶部开始到根,因为根是冠军的最终匹配。
树将始终平衡,并且可以有N个顶部位置,其中N = {2,4,8,16,32,64,128,256 ...} < / p>
我需要找到一个数学函数,它将返回节点parent的值,并传递其值。此数学函数必须可以用C#表示。
GetNodeParent(9); // must return 13
GetNodeParent(10); // must return 13
GetNodeParent(4); // must return 10
GetNodeParent(15); // must return null
该功能应该如何?
答案 0 :(得分:2)
在我看来,处理这个问题的最简单方法是以相反的方式存储树。通常情况下,平衡树可以在数组中表示,其中根位于第一个位置,而叶子位于最后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);
如果您不需要空值。
这可以通过这个简单的单元测试来验证,该单元测试来自您的示例值&#39;位置:
[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因为那个&#39;它们在树中的索引,根位于第一个位置,构造如下。您提供的示例将构造如下:
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 :(得分:0)
选项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;
}
作为后来的想法,在调用方法之前为if (aNodeValue == list_name.Count())
添加null
,或者将签名更改为int?
并在方法中进行检查以便&#39 ; ll返回null
答案 2 :(得分:0)
你试过这个简单的公式吗?
GetNodeParent(k) = (k + 1) div 2 + N
(if >=2N then null)
其中 div 是整数除法(7 div 2 = 3)