2016-04-22 11 views
5

मैं एक "बिट" ग में नीचे की तरह एक द्विआधारी पेड़ प्रिंट करने का प्रयास कर रहा हूँ खो ++ का उपयोग करते हुए:प्रिंट द्विआधारी पेड़ C++

  8 
     /\ 
     / \ 
     / \ 
     5  10 
    /\ /\ 
     2 6 9 11 

मैं कैसे पेड़ और संख्या की ऊंचाई प्राप्त करने के लिए पता प्रत्येक स्तर पर नोड्स का, लेकिन मैं यह नहीं समझ पाया कि रूट और दूसरे स्तर के बीच रिक्त स्थान की सही संख्या कैसे सेट करें (3 स्तरों के लिए रूट के नीचे 3 पंक्तियां हैं लेकिन मुझे विश्वास है कि यह हर बार नहीं है, मैंने सोचा यह अधिक पेड़ के लिए 3 गुना ऊंचाई हो सकता है)।

मैं पंक्तियों के बीच पंक्तियों और पंक्तियों की संख्या में इन रिक्त स्थानों को मुद्रित करने में कुछ सहायता करना चाहता हूं। धन्यवाद।

मैं सी में कोडिंग कर रहा हूँ ++

Get height 

int tree::getHeight(No *node) { 
    if (node == NULL) return 0; 
    return 1 + max(getHeight(node->esq), getHeight(node->dir)); 
} 

Get number of nodes per line 

void tree::getLine(const No *root, int depth, vector<int>& vals){ 
    int placeholder = 10; 
    if (depth <= 0 && root != nullptr) { 
     vals.push_back(root->chave); 
     return; 
    } 
    if (root->esq != nullptr) 
     getLine(root->esq, depth-1, vals); 
    else if (depth-1 <= 0) 
     vals.push_back(placeholder); 
    if (root->dir != nullptr) 
     getLine(root->dir, depth-1, vals); 
    else if (depth-1 <= 0) 
     vals.push_back(placeholder); 
} 
+2

एक द्विआधारी पेड़ कई अलग अलग तरीकों मुद्रित किया जा सकता, देख https://en.wikipedia.org/wiki/Tree_traversal। आपका वांछित आउटपुट क्या है? – Arun

+0

क्या आप टर्मिनल पर बाइनरी पेड़ प्रिंट करने की कोशिश कर रहे हैं? – orbitcowboy

+0

मैं इसे स्तर-क्रम में प्रिंट करना चाहता हूं, लेकिन मैं पोस्ट में उल्लिखित की तरह प्रिंट करना चाहता हूं, "/", कूदने वाली लाइन और इतने पर ... –

उत्तर

6

यहाँ एक द्विआधारी पेड़ की एक पाठ आधारित प्रतिनिधित्व बनाने कोड का एक उदाहरण है। यह प्रदर्शन उदाहरण के आकार को फलने से बचने के लिए, एक छोटे से पदचिह्न के साथ, कम से कम उपयोगी बाइनरी पेड़ वर्ग (बिनट्री) का उपयोग करता है।

कक्षा के अन्य हिस्सों में पाए जाने वाले पुनरावृत्ति के बजाए पुनरावृत्ति के बजाय इसका टेक्स्ट-रेंडरिंग सदस्य फ़ंक्शन अधिक गंभीर हैं।

यह तीन चरणों में अपना काम करता है, पहले स्ट्रिंग मानों की पंक्तियों का एक वेक्टर एक साथ रखा जाता है।

फिर इसका उपयोग पेड़ का प्रतिनिधित्व करने वाले पाठ तारों की रेखाओं को प्रारूपित करने के लिए किया जाता है।

तब तार साफ हो जाते हैं और कोउट में डंप किए जाते हैं।

एक अतिरिक्त बोनस के रूप में, डेमो में नॉनस्टॉप मनोरंजन के घंटों के लिए "यादृच्छिक पेड़" सुविधा शामिल है।

#include <iostream> 
#include <vector> 
#include <string> 
#include <sstream> 
#include <algorithm> 
#include <random> 

using std::vector; 
using std::string; 
using std::cout; 

template <typename T> 
class BinTree { 
    struct Node { 
     T value; 
     Node *left,*right; 
     Node() : left(nullptr),right(nullptr) {} 
     Node(const T& value) :value(value),left(nullptr),right(nullptr) {} 
     // stack-abusing recursion everywhere, for small code 
     ~Node() { delete left; delete right; } 
     int max_depth() const { 
      const int left_depth = left ? left->max_depth() : 0; 
      const int right_depth = right ? right->max_depth() : 0; 
      return (left_depth > right_depth ? left_depth : right_depth) + 1; 
     } 
    }; 

    Node *root; 

public: 
    BinTree() : root(nullptr) {} 
    ~BinTree() { delete root; } 

    int get_max_depth() const { return root ? root->max_depth() : 0; } 
    void clear() { delete root; root = nullptr; } 
    void insert() {} 
    template <typename ...Args> 
    void insert(const T& value, Args...more) { 
     if(!root) { 
      root = new Node(value); 
     } else { 
      Node* p = root; 
      for(;;) { 
       if(value == p->value) return; 
       Node* &pchild = value < p->value ? p->left : p->right; 
       if(!pchild) { 
        pchild = new Node(value); 
        break; 
       } 
       p = pchild; 
      } 
     } 
     insert(more...); 
    } 

    struct cell_display { 
     string valstr; 
     bool  present; 
     cell_display() : present(false) {} 
     cell_display(std::string valstr) : valstr(valstr), present(true) {} 
    }; 

    using display_rows = vector< vector<cell_display> >; 

    // The text tree generation code below is all iterative, to avoid stack faults. 

    // get_row_display builds a vector of vectors of cell_display structs 
    // each vector of cell_display structs represents one row, starting at the root 
    display_rows get_row_display() const { 
     // start off by traversing the tree to 
     // build a vector of vectors of Node pointers 
     vector<Node*> traversal_stack; 
     vector< std::vector<Node*> > rows; 
     if(!root) return display_rows(); 

     Node *p = root; 
     const int max_depth = root->max_depth(); 
     rows.resize(max_depth); 
     int depth = 0; 
     for(;;) { 
      // Max-depth Nodes are always a leaf or null 
      // This special case blocks deeper traversal 
      if(depth == max_depth-1) { 
       rows[depth].push_back(p); 
       if(depth == 0) break; 
       --depth; 
       continue; 
      } 

      // First visit to node? Go to left child. 
      if(traversal_stack.size() == depth) { 
       rows[depth].push_back(p); 
       traversal_stack.push_back(p); 
       if(p) p = p->left; 
       ++depth; 
       continue; 
      } 

      // Odd child count? Go to right child. 
      if(rows[depth+1].size() % 2) { 
       p = traversal_stack.back(); 
       if(p) p = p->right; 
       ++depth; 
       continue; 
      } 

      // Time to leave if we get here 

      // Exit loop if this is the root 
      if(depth == 0) break; 

      traversal_stack.pop_back(); 
      p = traversal_stack.back(); 
      --depth; 
     } 

     // Use rows of Node pointers to populate rows of cell_display structs. 
     // All possible slots in the tree get a cell_display struct, 
     // so if there is no actual Node at a struct's location, 
     // its boolean "present" field is set to false. 
     // The struct also contains a string representation of 
     // its Node's value, created using a std::stringstream object. 
     display_rows rows_disp; 
     std::stringstream ss; 
     for(const auto& row : rows) { 
      rows_disp.emplace_back(); 
      for(Node* pn : row) { 
       if(pn) { 
        ss << pn->value; 
        rows_disp.back().push_back(cell_display(ss.str())); 
        ss = std::stringstream(); 
       } else { 
        rows_disp.back().push_back(cell_display()); 
     } } } 
     return rows_disp; 
    } 

    // row_formatter takes the vector of rows of cell_display structs 
    // generated by get_row_display and formats it into a test representation 
    // as a vector of strings 
    vector<string> row_formatter(const display_rows& rows_disp) const { 
     using s_t = string::size_type; 

     // First find the maximum value string length and put it in cell_width 
     s_t cell_width = 0; 
     for(const auto& row_disp : rows_disp) { 
      for(const auto& cd : row_disp) { 
       if(cd.present && cd.valstr.length() > cell_width) { 
        cell_width = cd.valstr.length(); 
     } } } 

     // make sure the cell_width is an odd number 
     if(cell_width % 2 == 0) ++cell_width; 

     // formatted_rows will hold the results 
     vector<string> formatted_rows; 

     // some of these counting variables are related, 
     // so its should be possible to eliminate some of them. 
     s_t row_count = rows_disp.size(); 

     // this row's element count, a power of two 
     s_t row_elem_count = 1 << (row_count-1); 

     // left_pad holds the number of space charactes at the beginning of the bottom row 
     s_t left_pad = 0; 

     // Work from the level of maximum depth, up to the root 
     // ("formatted_rows" will need to be reversed when done) 
     for(s_t r=0; r<row_count; ++r) { 
      const auto& cd_row = rows_disp[row_count-r-1]; // r reverse-indexes the row 
      // "space" will be the number of rows of slashes needed to get 
      // from this row to the next. It is also used to determine other 
      // text offsets. 
      s_t space = (s_t(1) << r) * (cell_width + 1)/2 - 1; 
      // "row" holds the line of text currently being assembled 
      string row; 
      // iterate over each element in this row 
      for(s_t c=0; c<row_elem_count; ++c) { 
       // add padding, more when this is not the leftmost element 
       row += string(c ? left_pad*2+1 : left_pad, ' '); 
       if(cd_row[c].present) { 
        // This position corresponds to an existing Node 
        const string& valstr = cd_row[c].valstr; 
        // Try to pad the left and right sides of the value string 
        // with the same number of spaces. If padding requires an 
        // odd number of spaces, right-sided children get the longer 
        // padding on the right side, while left-sided children 
        // get it on the left side. 
        s_t long_padding = cell_width - valstr.length(); 
        s_t short_padding = long_padding/2; 
        long_padding -= short_padding; 
        row += string(c%2 ? short_padding : long_padding, ' '); 
        row += valstr; 
        row += string(c%2 ? long_padding : short_padding, ' '); 
       } else { 
        // This position is empty, Nodeless... 
        row += string(cell_width, ' '); 
       } 
      } 
      // A row of spaced-apart value strings is ready, add it to the result vector 
      formatted_rows.push_back(row); 

      // The root has been added, so this loop is finsished 
      if(row_elem_count == 1) break; 

      // Add rows of forward- and back- slash characters, spaced apart 
      // to "connect" two rows' Node value strings. 
      // The "space" variable counts the number of rows needed here. 
      s_t left_space = space + 1; 
      s_t right_space = space - 1; 
      for(s_t sr=0; sr<space; ++sr) { 
       string row; 
       for(s_t c=0; c<row_elem_count; ++c) { 
        if(c % 2 == 0) { 
         row += string(c ? left_space*2 + 1 : left_space, ' '); 
         row += cd_row[c].present ? '/' : ' '; 
         row += string(right_space + 1, ' '); 
        } else { 
         row += string(right_space, ' '); 
         row += cd_row[c].present ? '\\' : ' '; 
        } 
       } 
       formatted_rows.push_back(row); 
       ++left_space; 
       --right_space; 
      } 
      left_pad += space + 1; 
      row_elem_count /= 2; 
     } 

     // Reverse the result, placing the root node at the beginning (top) 
     std::reverse(formatted_rows.begin(), formatted_rows.end()); 

     return formatted_rows; 
    } 

    // Trims an equal number of space characters from 
    // the beginning of each string in the vector. 
    // At least one string in the vector will end up beginning 
    // with no space characters. 
    static void trim_rows_left(vector<string>& rows) { 
     if(!rows.size()) return; 
     auto min_space = rows.front().length(); 
     for(const auto& row : rows) { 
      auto i = row.find_first_not_of(' '); 
      if(i==string::npos) i = row.length(); 
      if(i == 0) return; 
      if(i < min_space) min_space = i; 
     } 
     for(auto& row : rows) { 
      row.erase(0, min_space); 
    } } 

    // Dumps a representation of the tree to cout 
    void Dump() const { 
     const int d = get_max_depth(); 

     // If this tree is empty, tell someone 
     if(d == 0) { 
      cout << " <empty tree>\n"; 
      return; 
     } 

     // This tree is not empty, so get a list of node values... 
     const auto rows_disp = get_row_display(); 
     // then format these into a text representation... 
     auto formatted_rows = row_formatter(rows_disp); 
     // then trim excess space characters from the left sides of the text... 
     trim_rows_left(formatted_rows); 
     // then dump the text to cout. 
     for(const auto& row : formatted_rows) { 
      std::cout << ' ' << row << '\n'; 
     } 
    } 
}; 


int main() { 
    BinTree<int> bt; 

    // Build OP's tree 
    bt.insert(8,5,2,6,10,9,11); 
    cout << "Tree from OP:\n\n"; 
    bt.Dump(); 
    cout << "\n\n"; 

    bt.clear(); 

    // Build a random tree 
    // This toy tree can't balance, so random 
    // trees often look more like linked lists. 
    // Just keep trying until a nice one shows up. 
    std::random_device rd; 
    std::mt19937 rng(rd()); 

    int MaxCount=20; 
    int MaxDepth=5; 
    const int Min=0, Max=1000; 

    std::uniform_int_distribution<int> dist(Min,Max); 

    while(MaxCount--) { 
     bt.insert(dist(rng)); 
     if(bt.get_max_depth() >= MaxDepth) break; 
    } 

    cout << "Randomly generated tree:\n\n"; 
    bt.Dump(); 
} 

उत्पादन का एक उदाहरण:

Tree from OP: 

     8 
    /\ 
    / \ 
    / \ 
    5  10 
/\ /\ 
2 6 9 11 


Randomly generated tree: 

         703 
         /\ 
        / \ 
        / \ 
        /  \ 
        /  \ 
       /   \ 
       /   \ 
       /    \ 
       /    \ 
      /     \ 
      /     \ 
      /      \ 
      /      \ 
     /       \ 
     /       \ 
     137        965 
     /\       /
    / \       /
    / \      /
    /  \      /
    /  \     /
/   \     /
/   \    /
41    387    786 
    \   /\   /\ 
    \   / \   / \ 
    \  / \  / \ 
    95  382  630  726  813 
             \ 
             841 
+0

क्रिस्टोफर धन्यवाद। मुझे लगता है कि इस समस्या के लिए कोई आसान तरीका नहीं है। मुझे कतारों का उपयोग करके लगभग एक त्वरित समाधान मिला लेकिन मैं आंतरिक दूरी को समझ नहीं पाया। –

+2

मुझे आश्चर्य हुआ कि यह कैसे शामिल हुआ, लेकिन मुझे इसे पूरा करने में दिलचस्पी थी क्योंकि मुझे अतीत में कई मौकों पर ऐसा कुछ चाहिए था। –

+0

@ क्रिस्टोफरऑनिक्स। क्या आप संकलन निर्भरताओं का उल्लेख कर सकते हैं? -std = C++ 11 ध्वज का उपयोग करके कई चेतावनियां फेंक रही हैं + मेरे विंडोज सिस्टम में त्रुटियां। – Debashish

0
  • 1 समारोह - स्तर से प्रिंट स्तर (रूट lv -> छोड़ देता है lv)
  • 2 समारोह - नई लाइन की शुरुआत से दूरी
  • तीसरा फ़ंक्शन - नोड्स प्रिंट करता है और दो प्रिंटों के बीच दूरी की गणना करता है;

void Tree::TREEPRINT() 
{ 
    int i = 0; 
    while (i <= treeHeight(getroot())){ 
     printlv(i); 
     i++; 
     cout << endl; 
    } 
} 

void Tree::printlv(int n){ 
    Node* temp = getroot(); 
    int val = pow(2, treeHeight(root) -n+2); 
    cout << setw(val) << ""; 
    prinlv(temp, n, val); 
} 

void Tree::dispLV(Node*p, int lv, int d) 
{ 
    int disp = 2 * d; 
    if (lv == 0){ 
     if (p == NULL){ 

      cout << " x "; 
      cout << setw(disp -3) << ""; 
      return; 
     } 
     else{ 
      int result = ((p->key <= 1) ? 1 : log10(p->key) + 1); 
      cout << " " << p->key << " "; 
      cout << setw(disp - result-2) << ""; 
     } 
    } 
    else 
    { 
     if (p == NULL&& lv >= 1){ 
      dispLV(NULL, lv - 1, d); 
      dispLV(NULL, lv - 1, d); 
     } 
     else{ 
      dispLV(p->left, lv - 1, d); 
      dispLV(p->right, lv - 1, d); 
     } 
    } 
} 

इनपुट:

50-28-19-30-29-17-42-200-160-170-180-240-44-26-27 

आउटपुट: https://i.stack.imgur.com/TtPXY.png

संबंधित मुद्दे