我正在尝试编写一个递归函数,用于定位Huffman树中的特定叶子,然后用零和1打印其最短路径(零是向左遍历,一个是向右遍历)。我理解我需要做的事情的逻辑,但我没有成功地实现它。我相信我在这里有一个很好的骨架,但是我缺少的部分是一些更复杂的逻辑来告诉我什么时候应该实际运行printf以及什么时候不应该(因为这个目前只打印每一个零和一个)。此外,我知道除此之外的其余逻辑工作正常,因为如果你进行正常遍历,你不必绘制最短路径,我找到的每个元素都可以找到。
我尝试在线查看相当多的资源,但我找不到解决方案,或者至少我无法正确识别解决方案。我可能已经重写了50次或更多次。让我知道你的想法!
void traverse(struct tree *curr, struct tree *cmp)
{
if (curr == NULL)
{
return;
}
if (getLeft(curr) == NULL && getRight(curr) == NULL)
{
if (curr == cmp)
{
return;
}
}
if (getLeft(curr) != NULL)
{
printf("0");
traverse(getLeft(curr), cmp);
}
if (getRight(curr) != NULL)
{
printf("1");
traverse(getRight(curr), cmp);
}
}
对于上下文:cmp
是我们要查找的节点,getLeft()
和getRight()
分别返回节点的左右子节点,curr
作为霍夫曼树本身的根。此外,这个printf工作的原因是因为我循环遍历所有已知的叶子,打印有关叶子的其他信息,然后调用此遍历方法,然后换行。
答案 0 :(得分:2)
有几种解决方案。
首先,您可以像往常一样遍历整个树,并构建一个代码表。然后使用表,而不是树。然后你不会浪费时间在整个树上搜索每个代码。当你遍历树时,你会建立一个0和1的字符串,当你到达一个叶子时,你将构建的字符串和符号保存在表格的叶子中。然后扔掉树。这是推荐的方法。
其次,您的链接可以是双向的。由于你有一个指向叶子的指针,你可以简单地从叶子开始并按照你的方式返回根目录,反过来构造0和1的字符串。
第三,你可以通过让遍历函数返回true或false来坚持对每个叶子进行痛苦的树搜索。如果它到达所需的叶子,或者其中一个遍历调用返回true,它将返回true。然后,根据哪个遍历调用返回true,您将打印或保存零或一个。这将反向打印路径。如果以相反顺序将它们保存在字符串中而不是打印,则可以在第一次遍历调用返回时打印字符串。
答案 1 :(得分:1)
一个可行的解决方案是给每个节点一个parent
指针。这样,一旦找到了叶子,就可以从该叶子开始递归遍历树,并在从递归调用返回时打印相应的位。
在此函数中,首先检查节点是否有父节点(换句话说,如果我们是否在根节点),如果是,则以节点的形式递归调用该函数父母;如果没有,请返回。
在递归调用函数的情况下,在递归调用之后,检查当前节点是否是其父节点的正确子节点。如果是这样,打印1;如果没有,请打印0。
无需担心在此实现中反转字符串。
另一种可能的解决方案是构建字符串并将其传递给递归调用。对于此解决方案,您需要知道树的高度,或者至少知道树可以编码的符号数,以便传入至少为该大小的char数组,以及一个用于null终止的字符数组。 / p>
在伪代码中,这看起来像:
func traverse (cur, cmp, str)
if cur == null, return
if cur == cmp
print str
if cur.left != null
traverse(cur.left, cmp, str + "0")
if cur.right != null
traverse(cur.right, cmp, str + "1")
这样,您就可以构建字符串,只有在找到相关的叶子后才会打印它。请注意,我将cur == cmp
检查移到了if语句之外,因为对于Huffman代码树中的内部节点,它永远不会成立。但是,这种方法对于查找一个字符的代码来说效率非常低,因为它在整个树上执行DFS。