【问题标题】:Ostream Operator for AVL Trees C++AVL 树 C++ 的 Ostream 运算符
【发布时间】:2017-12-28 14:29:51
【问题描述】:

议程是使用 ostream 运算符打印 AVL 树的内容。内容必须以特定格式打印。

树是使用模板实现的。 一个简单的主要实现。

AVLTree<int, float> tree;
for(int i = 0; i < 10; i++)
   tree.insert(i, i+0.1);
cout << tree;

Ostream 运算符

friend ostream&  operator<<(ostream& out, const AVLTree& v)
{   
    out << "{";
    v.print(out, v.root);
    out << "}";
    return out;
}


void print(AVLnode<KEY,INFO>* curr)const
    {
        if(curr)
        {
            print(curr->left);
            print(curr->right);
        }
    }

void print(ostream& out, AVLnode<KEY, INFO>* curr)const
    {
        if(curr)
        {
            print(out, curr->left);
            out << curr->Key_ << ": " << curr->Info_<<", ";
            print(out, curr->right);
        }
    }

我有两个用于打印的辅助函数。

我得到的输出是

{1:1.1, 2:2.1. 3:3.1, 4:4.1, 5:5.1, 6:6.1, 7:7.1, 8:8.1, 9:9.1, }

需要的输出是

{1:1.1, 2:2.1. 3:3.1, 4:4.1, 5:5.1, 6:6.1, 7:7.1, 8:8.1, 9:9.1}

“,”不应该被打印,你如何检测树的最后一个元素?我无法理解这种情况。很简单,但我看不到。

【问题讨论】:

  • 逗号似乎出现在左右打印之间。也许最后右边没有打印任何东西?
  • 您也可以采用稍微不同的方法,尝试在输出中打印逗号 first,而不是最后一个。这样就不会出现尾随逗号。检测一个节点是否是第一次打印,IMO,比尝试检测它是否是最后一个打印节点更容易掌握。

标签: c++ templates recursion avl-tree ostream


【解决方案1】:

考虑这个问题的另一种方法是打印逗号first,而不是最后一个。这样,您将永远不会得到尾随逗号,因为它将是第一个打印的项目。

这可以通过在辅助函数中引入bool 引用变量来实现(未测试):

friend ostream&  operator<<(ostream& out, const AVLTree& v)
{   
    bool firstTime = true;
    out << "{";
    v.print(out, v.root, firstTime);
    out << "}";
    return out;
}


void print(ostream& out, AVLnode<KEY, INFO>* curr, bool& firstTime) const
{
    if (curr)
    {
       print(out, curr->left, firstTime);
       out << (firstTime?"":", ") << curr->Key_ << ": " << curr->Info_;
       firstTime = false;
       print(out, curr->right, firstTime);
    }
}

firstTime 跟踪它是否是第一次打印。如果是这种情况,则不打印逗号,否则打印逗号。

【讨论】:

  • 嘿,我试过了。无效的输出 {1: 1.1, 2: 2.23: 3.3, 4: 4.4, 5: 5.5, 6: 6.6, 7: 7.7, 8: 8.8} 应该是 { 1: 1.1, 2: 2.2, 3: 3.3, 4: 4.4, 5: 5.5, 6: 6.6, 7: 7.7, 8: 8.8}.. 区别非常细微,但由于某种原因,它会打印“没有空格"。
  • 鉴于我给出的答案中的代码,该输出似乎不太可能。 operator &lt;&lt; 调用两个参数print 函数,并且firstTime 变量在第一次输出任何东西时立即设置为false。您在原始帖子中应该做的是一个minimal reproducible example,它显示如何您在上面的代码中调用print 函数。显然,您的真实代码正在做一些不同的事情,而不是简单地调用 print(param1, param2).. 您还可以调试您的代码,看看为什么将布尔值设置为 false 的明显设置不成立。
  • 此外,代码假定您正在尝试打印在operator &lt;&lt; 中给定AVLTree 的树。你原来的帖子中的那个参数print 函数——它没有显示任何用法,它甚至没有被operator &lt;&lt; 使用。那么单参数print函数的作用是什么?
【解决方案2】:

我认为有两种方法可以做到这一点:

1.

生成字符串,存储在变量中,然后删除最后 2 或 3 个字符(取决于间距),然后附加 } 以关闭格式,然后输出到 ostream。

2.

进入第一个打印函数时,设置一个计数器为0,每处理一个节点递增一次。这将为您提供打印的项目总数。然后,您可以将其与 AVL 树中的项目总数进行比较。您还必须跟踪某处的项目总数。

我会使用选项一,因为它现在更容易实现。此外,AVL 树不应该负责知道其中有多少项目。此外,您的树越大,打印速度就越慢,因为会有更多的 if 语句和 count 增量。这两个操作是最小的,但如果你得到数千或数百万个项目,它就会加起来。

【讨论】:

    【解决方案3】:

    引入一个简单的if statement条件:

    out << curr->Key_ << ": " << curr->Info_;
    if (some_condition_of_yours) {
        out << ", ";
    } 
    else {
        out << " ";
    }
    

    用你的内部逻辑替换条件。

    【讨论】:

    • 我更新了问题,我确实需要条件,条件的逻辑应该是检测树的末端。
    • 我不得不重新构建问题,目的是使格式正确,即不应打印“,”。但我需要条件来检查是否相同。我明白我们需要一个“if 条件”,但我的问题集中在“some_condition_of_yours”上,我该如何得到它?
    【解决方案4】:

    我想你需要在print() 中添加一个额外的参数来知道右边是否有东西。

    举例(注意:未测试)

    friend ostream&  operator<<(ostream& out, const AVLTree& v)
    {   
        out << "{";
        v.print(out, v.root, false);
        out << "}";
        return out;
    }
    
    
    void print (ostream & out, AVLnode<KEY, INFO> * curr, bool proComma) const
        {
            if (curr)
            {
                bool proC2 = proComma || (NULL != curr->right);
    
                print(out, curr->left, proC2);
    
                out << curr->Key_ << ": " << curr->Info_;
    
                if ( proC2 )
                   out << ", ";
    
                print(out, proComma);
            }
        }
    

    【讨论】:

      【解决方案5】:

      所以过了一会儿,我弄清楚了问题所在并且能够想出一个简单的解决方案,所以我决定在我忘记这样做之前发布一个答案。

      很难检测到树中的最后一个节点,因为树的左侧和右侧都有一个 NULL 结尾。所以打印出整个树并删除多余的字符就很好了。

      template<typename KEY, typename INFO>
      void AVLTree<KEY, INFO>:: print(ostream& out, AVLnode<KEY,INFO>* curr) const{
              //sstream operator to calc the output and delete (n-1) of the output.        
              out << "{";
              std::stringstream ss;
              printHelp(ss, curr);
              std::string output = ss.str();
              if (output.length() > 1)
                  output.erase(output.end()-2, output.end());
      
              out << output;
              out << "}";
      }
      
      template<typename KEY, typename INFO>
      void AVLTree<KEY, INFO>::printHelp(std::stringstream& ss, AVLnode<KEY, INFO>* curr) const{
          if(curr){
                  printHelp(ss, curr->left);  
                  ss << curr->Key_<< ": " << curr->Info_ << ", ";
                  printHelp(ss, curr->right);
              }
      }
      

      然后是ostream运算符,它使用以根节点为参数的打印函数和ostream对象。

      friend ostream& operator<<(ostream& out, const AVLTree& v){
              v.print(out, v.root);
              return out;
          }
      

      还有另一种选择,将它们推入堆栈并弹出最后一个字符并完全打印堆栈,这非常接近我的需要,但效率非常低,因为树上有接近 15,000 个节点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-31
        • 1970-01-01
        • 2020-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-24
        相关资源
        最近更新 更多