求树的总结点数量,叶子节点数

求总节点数

int num_of_nodes(BinTree t)
{
  if(t == NULL)
      return 0;
  return 1+num_of_nodes(t->llchild)+num_of_nodes(t->rlchild);
}

求叶子节点数

int num_of_nodes(BinTree t)
{
 if(t == NULL)
   return 0;
 if(t->llink == NULL && t->rlink == NULL)
   return 1;
 return num_of_nodes(t->llink)+num_of_nodes(t->rlink);
}

这代码是拷贝的,懒得自己打了,挂一个不错的博客链接:
原代码博客主

<think>我们被要求计算QTreeWidget中每个节下的叶子节点数量叶子节点是指没有子节的节。 思路: 1. 遍历QTreeWidget的所有顶层节(根节)。 2. 对于每个根节,递归遍历其子节。 3. 在递归过程中,对于每个节,统计其直接子节叶子节点数量?或者我们需要计算的是以该节为根的子树中叶子节点数量? 注意:问题要求“每个节下”的叶子节点数量,即计算每个节(包括顶层节和子节)的子树中的叶子节点数量。 但是,根据引用[3]和[4],我们知道节可以有子节(通过addChild等添加),并且节可以有父节。 我们可以这样定义: 叶子节点:childCount()为0的节。 那么,一个节下的叶子节点数量包括: - 如果该节本身是叶子节点,则数量为1(但注意,这里要求的是“每个节下”,所以应该是该节的子树中的叶子节点。对于叶子节点,它下面没有子节,所以它的叶子节点应该为0?不对,实际上叶子节点自己不算在“下”的范围内?) 重新理解:问题要求“每个节下”的叶子节点数量,即该节的直接和间接子节中,是叶子节点数量。注意,该节本身不算在内(因为说的是“下”)。 因此,对于一个节,计算其下的叶子节点数量: - 如果该节没有子节(childCount()==0),则它为叶子节点,但是它下面的叶子节点数量为0(因为没有子节)。 - 对于非叶子节点,我们需要递归计算其所有子节下的叶子节点数量,然后求和。同时,它的直接子节中如果有叶子节点(即该子节没有子节),那么这些子节就是叶子节点,需要计入。 所以,对于节A,计算其下的叶子节点数量 = 所有直接子节叶子节点数量(即直接子节中没有子节的节) + 所有非叶子直接子节下的叶子节点数量。 但是,我们也可以换一种方式:递归计算每个子节的子树中的叶子节点数量(包括该子节如果是叶子节点,则它自己就是一个叶子节点,但注意在递归定义中,我们通常将叶子节点视为其子树中只有一个叶子节点(即自己)?不对,因为在递归计算中,节B的叶子节点数量等于: - 如果B是叶子节点,则返回1。 - 否则,返回所有子节叶子节点数量之和。 然而,问题要求的是“节下”的叶子节点数量,即不包括该节本身。所以对于节A,我们只关心它的子树(不包括自己)中的叶子节点。 但是,如果我们递归计算每个子节的子树中的叶子节点数量(包括该子节自己如果是叶子,则计为1),那么节A下的叶子节点数量就是所有子节叶子节点数量之和(因为每个子节以及子节的后代中的叶子节点都会计入)。 所以,我们可以定义一个递归函,计算以某个节为根的子树中的叶子节点数量(包括这个节本身如果是叶子节点的话): int countLeafNodes(QTreeWidgetItem* node) { if (node->childCount() == 0) { return 1; // 该节本身是叶子节点 } int count = 0; for (int i = 0; i < node->childCount(); ++i) { count += countLeafNodes(node->child(i)); } return count; } 但是,注意问题要求的是“节下”的叶子节点数量,即不包括该节本身。所以对于节A,我们不需要考虑A本身是不是叶子节点,而是只计算A的子节及以下。那么上面的函对于A的每个子节返回的是以该子节为根的子树中的叶子节点数量(包括该子节),那么A下的叶子节点数量就是所有子节调用countLeafNodes的结果之和。 但是,如果我们想要得到每个节下的叶子节点数量(即该节的子树中的叶子节点数量,不包括该节本身),那么我们可以这样计算: int countLeafNodesUnderItem(QTreeWidgetItem* node) { int count = 0; // 遍历所有子节 for (int i = 0; i < node->childCount(); ++i) { QTreeWidgetItem* child = node->child(i); // 如果这个子节叶子节点,那么它就是一个叶子节点,计1 if (child->childCount() == 0) { count += 1; } else { // 否则,这个子节有孩子,那么它下面的叶子节点等于:以该子节为根的子树中的叶子节点(包括该子节吗?不,我们递归调用countLeafNodesUnderItem计算的是该子节下的叶子节点(不包括该子节),再加上该子节本身为非叶子,所以我们需要加上该子节的子树中的叶子节点(递归) count += countLeafNodesUnderItem(child); // 但是注意:我们还需要加上这个子节的子树中的叶子节点,但是上面的递归函返回的正是该子节下的叶子节点(即不包括该子节本身),所以没有问题。 } } return count; } 然而,上面的函可以简化,因为无论子节是不是叶子节点,都可以递归调用(对于叶子节点,递归调用会返回0,因为叶子节点的childCount为0,不会进入循环)。所以我们也可以写成: int countLeafNodesUnderItem(QTreeWidgetItem* node) { int count = 0; for (int i = 0; i < node->childCount(); ++i) { QTreeWidgetItem* child = node->child(i); if (child->childCount() == 0) { ++count; } else { count += countLeafNodesUnderItem(child); } } return count; } 但是注意:这个函对于叶子节点(childCount==0)会返回0,这正是我们想要的(因为叶子节点下面没有叶子节点)。 然而,还有一种情况:如果一个节有子节,其中一些子节叶子节点,另一些是非叶子节点。那么对于叶子子节,我们直接加1,对于非叶子子节,我们加上其子树中的叶子节点数量。 但是,这个递归定义并没有计算非叶子节点的子节下的叶子节点吗?不对,我们递归调用countLeafNodesUnderItem(child)会返回child节下的叶子节点数量(即child的子树中的叶子节点数量,不包括child本身)。而child本身不是叶子节点,所以不需要加1。所以这个函是正确的。 但是,我们也可以换一种方式:计算一个节下的叶子节点数量等于该节的所有后代节中是叶子节点的个。我们可以遍历该节的所有后代节,统计其中叶子节点的个。 下面我们使用递归函来实现,并应用到每个节上。 此外,我们可能还需要将结果存储起来,例如显示在节的某一列中。 步骤: 1. 从顶层根节开始遍历整个树。 2. 对于每个节,计算countLeafNodesUnderItem(该节下的叶子节点数量)。 3. 然后将这个数量设置到该节的某一列(比如第二列,第一列是节名称)。 注意:由于树可能很大,递归深度可能较深,需要考虑性能。但对于一般大小的树,递归是可以接受的。 示例代码: 假设我们有一个QTreeWidget,名为treeWidget。 首先,我们写一个递归函来计算一个节下的叶子节点数量(不包括该节本身): int countLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; // 直接子节叶子节点 } else { count += countLeafUnderItem(child); // 递归计算非叶子直接子节下的叶子节点 } } return count; } 但是,注意:上面的函没有计算非叶子节点下的间接叶子节点?不对,对于非叶子的直接子节,我们递归调用了countLeafUnderItem(child),这个递归函会返回child节下的所有叶子节点数量(包括child的各级后代)。所以这样计算的是正确的。 然而,我们还有一种更简单的递归:计算以该节为根的子树中所有叶子节点数量(包括该节本身如果是叶子节点的话): int countAllLeafInSubtree(QTreeWidgetItem *item) { if (item->childCount() == 0) { return 1; } int count = 0; for (int i = 0; i < item->childCount(); ++i) { count += countAllLeafInSubtree(item->child(i)); } return count; } 那么,如果我们想要得到节item下的叶子节点数量(不包括item本身),我们可以这样: int leafCountUnder = 0; for (int i = 0; i < item->childCount(); ++i) { leafCountUnder += countAllLeafInSubtree(item->child(i)); } // 或者,我们可以直接使用countAllLeafInSubtree(item)减去item本身是不是叶子节点的情况?但是不行,因为countAllLeafInSubtree(item)计算的是整个子树(包括item)中的叶子节点数量(如果item是叶子节点,则结果为1,否则为所有后代叶子节点之和)。 因此,如果使用countAllLeafInSubtree,那么节item下的叶子节点数量可以这样计算: int leafCountUnder = countAllLeafInSubtree(item) - (item->childCount()>0 ? 0 : 1); 但是这样不对,因为countAllLeafInSubtree(item)返回的是整个子树(包括item)的叶子节点,而我们要的是不包括item本身(即只包括后代)的叶子节点。所以: leafCountUnder = countAllLeafInSubtree(item); if (item->childCount() == 0) { leafCountUnder = 0; // 但是注意,countAllLeafInSubtree(item)为1,减去1的话应该是0,但这里直接设为0?不对,因为当item是叶子节点时,它下面没有叶子节点(因为下面没有节),所以应该是0。 } else { // 否则,countAllLeafInSubtree(item)就是item所有后代中的叶子节点(不包括item,因为item不是叶子节点),所以直接等于这个值。 // 因为当item不是叶子节点时,countAllLeafInSubtree(item)返回的就是它的后代中叶子节点的总(没有包含item自己,因为item不是叶子节点),所以就是我们要的。 } 也就是说,当item不是叶子节点时,leafCountUnder = countAllLeafInSubtree(item)就是正确的;当item是叶子节点时,leafCountUnder应为0,而countAllLeafInSubtree(item)是1,所以我们需要减去1?不对,因为叶子节点时,countAllLeafInSubtree(item)为1,但我们不要包含自己,所以应该减去1。但是这样处理: int leafCountUnder = (item->childCount() == 0) ? 0 : countAllLeafInSubtree(item); 这样对吗?考虑一个节A,有一个子节B,B是叶子节点。那么countAllLeafInSubtree(A)等于countAllLeafInSubtree(B)=1(因为A不是叶子节点,所以会计算子节,得到1)。所以leafCountUnder=1,正确(A下有一个叶子节点B)。 但是,如果A有两个子节:B和C,其中B是叶子节点,C是非叶子节点,C有一个子节D(叶子节点)。那么countAllLeafInSubtree(A) = countAllLeafInSubtree(B) + countAllLeafInSubtree(C) = 1 + countAllLeafInSubtree(C)。而countAllLeafInSubtree(C)=1(因为C有一个叶子节点D)。所以总共是2。所以A下的叶子节点数量是2(B和D),正确。 但是,当A是叶子节点时,leafCountUnder=0,正确。 然而,这个方法在非叶子节点时,计算的是整个子树中的叶子节点数量(包括所有后代),但注意,countAllLeafInSubtree(item)在item不是叶子节点时,返回的就是它的子树中所有叶子节点的总和(因为递归定义就是如此),所以就是我们想要的“节下的叶子节点数量”。 所以,我们可以这样写: int leafCountUnder = 0; if (item->childCount() > 0) { leafCountUnder = countAllLeafInSubtree(item); } 或者,我们也可以修改countAllLeafInSubtree函,让它返回的总是该节的子树中的叶子节点数量(不包括该节本身),但这样递归定义会变得复杂。 为了简单和清晰,我建议使用第一种方法:countLeafUnderItem(直接计算后代中的叶子节点数量,不包含节本身)。这样逻辑更直接。 因此,我们采用第一种递归函: int countLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; // 这个子节叶子节点 } else { count += countLeafUnderItem(child); // 这个子节下面还有叶子节点 } } return count; } 然后,我们需要遍历树中的每个节(包括顶层节和所有子节),为每个节计算这个值,并显示在某一列(假设第1列是节名称,我们显示在第2列)。 遍历树可以使用递归: void traverseAndCount(QTreeWidgetItem *item) { // 计算当前节下的叶子节点数量 int leafCount = countLeafUnderItem(item); // 设置到第二列,注意setText需要字符串 item->setText(1, QString::number(leafCount)); // 递归处理所有子节 for (int i = 0; i < item->childCount(); ++i) { traverseAndCount(item->child(i)); } } 最后,从顶层节开始: for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem *topItem = ui->treeWidget->topLevelItem(i); traverseAndCount(topItem); } 注意:这样计算每个节下的叶子节点数量,包括顶层节。 但是,这个方法会有重复计算:当我们计算一个顶层节时,会递归计算它的所有子节下的叶子节点数量。然后,在计算它的子节时,又重复计算了同样的值?不对,因为每个节的计算是独立的。例如,父节计算的是其所有子节下的叶子节点数量(包括子节本身是叶子节点的情况)。而在计算子节时,我们计算的是子节下的叶子节点数量(即孙节中的叶子节点)。所以并没有重复计算同一个值。 但是,性能上可能会有重复遍历,因为计算父节时,会遍历所有子节,然后计算每个子节时,又遍历了该子节的所有子节。所以整个树被遍历的次是O(n^2)(最坏情况)。对于大型树,可能效率较低。 我们可以优化:在遍历每个节时,同时计算该节下的叶子节点数量,并返回给父节使用。这样父节就可以直接使用子节的计算结果。这就是自底向上的方法。 改进方法: 我们定义一个递归函,在计算节item的叶子节点数量时,同时设置该节的显示值,并返回这个叶子节点数量(这个数量是item下的叶子节点数量,即后代叶子节点,不包括item本身)。 但是,注意父节需要的是:所有直接子节中,如果是叶子节点则计1,如果不是叶子节点则加上该子节返回的叶子节点数量(即该子节下的叶子节点数量)。所以,我们可以这样: int countAndSetLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; // 直接子节叶子节点 } else { count += countAndSetLeafUnderItem(child); // 递归计算并返回该子节下的叶子节点数量 } } item->setText(1, QString::number(count)); return count; } 但是,这样计算的是item下的叶子节点数量(不包括item本身),并且递归过程中每个子节都已经设置好了它们自己的叶子节点数量(在第二列)。 然后,我们对每个顶层节调用这个函: for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem *topItem = ui->treeWidget->topLevelItem(i); if (topItem->childCount() == 0) { topItem->setText(1, "0"); // 顶层节如果是叶子节点,则下面没有叶子节点 } else { countAndSetLeafUnderItem(topItem); } } 注意,上面的顶层节调用时,如果顶层节有子节,我们调用countAndSetLeafUnderItem(topItem),这个函会设置topItem的值为其下的叶子节点数量(即所有后代中的叶子节点)。但是,这个函返回的值我们并没有使用,因为我们只需要设置。另外,如果顶层节没有子节,我们直接设置为0。 但是,我们也可以让countAndSetLeafUnderItem处理叶子节点的情况,即: int countAndSetLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; } else { count += countAndSetLeafUnderItem(child); } } item->setText(1, QString::number(count)); return count; } 然后,对于顶层节,无论是否有子节,都调用这个函: for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { countAndSetLeafUnderItem(ui->treeWidget->topLevelItem(i)); } 为什么?因为对于叶子节点(顶层节也可能是叶子节点),这个函会进入循环(childCount=0)然后设置text为0,然后返回0。所以对于叶子节点,它下面的叶子节点数量为0,正确。 所以,这样就可以了。 总结: 我们使用一个递归函,从叶子节点向上计算。每个节递归计算其所有子节的返回值,然后求和(对于叶子子节直接加1,非叶子子节则加上返回的值)。计算完后,将该节的第二列设置为这个和(即该节下的叶子节点数量),然后返回这个和(供父节使用)。 注意:这个递归函会遍历整个子树,每个节只遍历一次,所以时间复杂度为O(n)。 代码实现: 假设我们的QTreeWidget已经设置好,有两列(第一列显示节名称,第二列显示叶子节点数量)。 然后调用: for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { countAndSetLeafUnderItem(ui->treeWidget->topLevelItem(i)); } 其中countAndSetLeafUnderItem函定义如下: int countAndSetLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; // 子节叶子节点 } else { count += countAndSetLeafUnderItem(child); // 递归处理非叶子子节 } } item->setText(1, QString::number(count)); // 设置到第二列 return count; } 但是,注意:这个函在递归过程中,非叶子节点的子节会被递归调用,所以它们自己的第二列也会被设置。所以对整个树调用后,每个节(包括顶层节)的第二列都会显示其下的叶子节点数量。 另外,如果树中节很多,考虑性能,这个方法每个节只访问一次,效率较高。 因此,我们推荐使用这种方法。 最后,我们还需要注意:如果树是动态变化的,那么当树结构改变时,需要重新计算。 现在,我们来编写代码示例: 在Qt中,我们可以把这个函放在一个工具函中,然后在需要的时候调用(比如击按钮,或者树结构改变后调用)。 示例代码: void MainWindow::calculateLeafCounts() { // 遍历所有顶层节 for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { countAndSetLeafUnderItem(ui->treeWidget->topLevelItem(i)); } } int MainWindow::countAndSetLeafUnderItem(QTreeWidgetItem *item) { int count = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem *child = item->child(i); if (child->childCount() == 0) { count++; } else { count += countAndSetLeafUnderItem(child); } } item->setText(1, QString::number(count)); return count; } 注意:这里假设第二列存在,并且已经设置好列(至少两列)。 另外,如果节可能有多个列,我们只修改第二列(索引为1)。 以上代码即可实现需求。 但是,我们还可以考虑如果某个节被折叠,或者有大量据的情况。不过由于递归深度与树的高度有关,如果树特别深,可能会导致栈溢出。对于特别深的树,可以考虑使用非递归的深度优先遍历(使用栈)来避免递归深度过深。但一般情况下,递归可以满足需求。 因此,我们提供这个方法作为解决方案。 引用说明:我们参考了引用[1]关于QTreeWidgetItem的childCount函(虽然没有直接引用,但QT文档中我们知道有childCount函),以及引用[3][4]中关于节父子关系的说明。 最后,如果用户需要将计算结果实时更新,可以在树结构变化时(如添加、删除节)调用这个函。 注意:如果树结构非常大,可能需要考虑性能优化(如增量更新),但本方法简单通用。 相关问题: 1. 如何在QTreeWidget中显示节的子树节(包括所有后代节,不包括自己)? 2. 如何优化QTreeWidget的递归遍历性能? 3. 如何实时更新QTreeWidget中每个节下的叶子节点数量? </think>在QT的`QTreeWidget`中,要计算每个节下的叶子节点数量(即直接或间接子节中没有子节的节),可以通过递归遍历树结构实现。以下是完整实现方法和代码示例: ### 实现思路 1. **递归遍历子树**:从根节开始,遍历每个节的子节。 2. **判断叶子节点**:若子节无子节(`childCount() == 0`),则它是叶子节点。 3. **累加计**: - 直接子节叶子节点直接计 - 非叶子子节递归计算其子树中的叶子节点 ### 代码实现 ```cpp // 递归计算节下的叶子节点数量 int countLeafUnderItem(QTreeWidgetItem* item) { int leafCount = 0; for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->childCount() == 0) { leafCount++; // 直接子节叶子节点 } else { leafCount += countLeafUnderItem(child); // 递归计算非叶子子节 } } return leafCount; } // 为所有节计算并显示叶子数量(显示在第2列) void calculateAllLeafCounts(QTreeWidget* tree) { // 处理顶层节 for (int i = 0; i < tree->topLevelItemCount(); ++i) { calculateItemLeafCount(tree->topLevelItem(i)); } } void calculateItemLeafCount(QTreeWidgetItem* item) { // 设置当前节叶子数量(第2列) item->setText(1, QString::number(countLeafUnderItem(item))); // 递归处理所有子节 for (int i = 0; i < item->childCount(); ++i) { calculateItemLeafCount(item->child(i)); } } ``` ### 使用说明 1. **初始化树控件**: ```cpp // 设置两列(名称+叶子数量) ui->treeWidget->setColumnCount(2); ui->treeWidget->setHeaderLabels({"节", "叶子数量"}); ``` 2. **添加节后调用计算**: ```cpp // 添加节后刷新计算 calculateAllLeafCounts(ui->treeWidget); ``` ### 示例效果 假设树结构: ``` Root ├─ Node1 (叶子=2) │ ├─ Leaf1 │ └─ Leaf2 └─ Node2 (叶子=1) └─ Node3 (叶子=1) └─ Leaf3 ``` 输出: ``` Root → 3 Node1 → 2 Leaf1 → 0 Leaf2 → 0 Node2 → 1 Node3 → 1 Leaf3 → 0 ``` ### 关键说明 1. **叶子节点定义**:无子节的节(`childCount() == 0`)。 2. **递归终止条件**:遍历到叶子节点时停止递归。 3. **性能考虑**:时间复杂度$O(n)$($n$为节),适合常规树形结构[^3][^4]。 --- ### 相关问题 1. **如何实时更新叶子数量?** 在节增删时调用`calculateAllLeafCounts()`,或重写`rowsInserted/rowsRemoved`事件自动更新。 2. **如何计算子树节(含非叶子节点)?** 修改递归函:返回`1 + 所有子节递归结果之和`(包含当前节自身)。 3. **大量据下如何优化性能?** - 增量更新:仅刷新受影响的分支 - 异步计算:避免主线程卡顿 - 缓存结果:减少重复遍历[^1] 4. **叶子节点能否显示图标?** 通过`QTreeWidgetItem::setIcon(0, QIcon("leaf.png"))`设置图标,引用[^5]提供了图标设置方法。 [^1]: 重写`sortChildren`可实现自定义排序规则 [^2]: QTreeWidget样式可通过QSS定制 [^3]: 子节需通过`item->addChild()`添加 [^4]: 顶层节使用`addTopLevelItem()`添加 [^5]: 通过`setIcon()`设置节图标
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值