再帰的および非再帰的アルゴリズムは、バイナリ ツリーの前、中間、および後順トラバーサルを実装します

序章

二分木は非常に重要なデータ構造であり、他の多くのデータ構造は二分木に基づいて進化しています。バイナリ ツリーには、深さトラバーサルと幅トラバーサルがあります. 深さトラバーサルには、プリオーダー、インオーダー、ポストオーダーの 3 つのトラバーサル方法があります. 幅トラバーサルは、通常、階層トラバーサルと呼ばれるものです. ツリーの定義自体が再帰的な定義であるため、再帰的な方法を使用してツリーの 3 つのトラバーサルを実装することは理解しやすく、コードが非常に単純であるだけでなく、幅トラバーサルについては他のサポートが必要です。ヒープなどのデータ構造。

ここでは、主に二分木の 3 つの走査方法、つまり、順序順走査、順序順走査、および後順走査を紹介します。その中で、順序走査が最も重要です。

トラバーサルの主なアイデア:
トラバーサルの事前順序付け: ルート ノード -> 左のサブツリー -> 右のサブツリー

順序通りのトラバーサル: 左のサブツリー -> ルート ノード -> 右のサブツリー

ポストオーダー トラバーサル: 左のサブツリー —> 右のサブツリー —> ルート ノード

次の図を例にとります:
ここに画像の説明を挿入A: ルート ノード、B: 左ノード、C: 右ノード、プレオーダー オーダーは ABC、インオーダー オーダーは BAC、ポストオーダー オーダーは BCA です。

より複雑なツリーを見てください:
ここに画像の説明を挿入
事前順序トラバーサル: ABCDEFGHK; 中間順序トラバーサル: BDCAEHGKF; 後順序トラバーサル: DCBHKGFEA

再帰的な方法は、前後の順トラバーサルを実現します

まず、二分木の構造を定義しましょう。

typedef struct BinaryTreeNode
{
    
    
    TelemType data;
    struct BinaryTreeNode *Left;
    struct BinaryTreeNode *Right;
}Node;

ルート ノード -> 左サブツリー -> 右サブツリーの順序でバイナリ ツリーを作成します。

Node* createBinaryTree()
{
    
    
    Node *p;
    TelemType ch;
    cin>>ch;
    if(ch == 0)     //如果到了叶子节点,接下来的左、右子树分别赋值为0
    {
    
    
        p = NULL;
    }
    else
    {
    
    
        p = (Node*)malloc(sizeof(Node));
        p->data = ch;
        p->Left  = createBinaryTree();  //递归创建左子树
        p->Right = createBinaryTree();  //递归创建右子树
    }
    return p;
}

事前注文トラバーサル:

void preOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        cout<<root->data<<' ';
        preOrderTraverse(root->Left);
        preOrderTraverse(root->Right);
    }
}

順序通りのトラバーサル:

void inOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        inOrderTraverse(root->Left);
        cout<<root->data<<' ';
        inOrderTraverse(root->Right);
    }
}

ポストオーダー トラバーサル:

void lastOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        lastOrderTraverse(root->Left);
        lastOrderTraverse(root->Right);
        cout<<root->data<<' ';
    }
}

以下は、二分木のノードの総数、深さ、および葉ノードの数です。

//二叉树节点总数目
int Nodenum(Node* root)
{
    
    
    if(root == NULL)
    {
    
    
        return 0;
    }
    else
    {
    
    
        return 1+Nodenum(root->Left)+Nodenum(root->Right);
 
    }
}
 
//二叉树的深度
int DepthOfTree(Node* root)
{
    
    
    if(root)
    {
    
    
        return DepthOfTree(root->Left)>DepthOfTree(root->Right)?DepthOfTree(root->Left)+1:DepthOfTree(root->Right)+1;
    }
    if( root == NULL )
    {
    
    
        return 0;
    }
}
 
//二叉树叶子节点数
int Leafnum(Node* root)
{
    
    
    if(!root)
    {
    
    
        return 0;
    }
    else if(  (root->Left == NULL) && (root->Right == NULL) )
    {
    
    
        return 1;
    }
    else
    {
    
    
        return  (Leafnum(root->Left) + Leafnum(root->Right)) ;
    }
}

完全なコード:

#include <iostream>
#include<cstdio>
#include<cstdlib>
 
using namespace std;
 
typedef int TelemType;
 
typedef struct BinaryTreeNode
{
    
    
    TelemType data;
    struct BinaryTreeNode *Left;
    struct BinaryTreeNode *Right;
}Node;
 
 
//创建二叉树,顺序依次为中间节点->左子树->右子树
Node* createBinaryTree()
{
    
    
    Node *p;
    TelemType ch;
    cin>>ch;
    if(ch == 0)     //如果到了叶子节点,接下来的左、右子树分别赋值为0
    {
    
    
        p = NULL;
    }
    else
    {
    
    
        p = (Node*)malloc(sizeof(Node));
        p->data = ch;
        p->Left  = createBinaryTree();  //递归创建左子树
        p->Right = createBinaryTree();  //递归创建右子树
    }
    return p;
}
 
//先序遍历
void preOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        cout<<root->data<<' ';
        preOrderTraverse(root->Left);
        preOrderTraverse(root->Right);
    }
}
 
//中序遍历
void inOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        inOrderTraverse(root->Left);
        cout<<root->data<<' ';
        inOrderTraverse(root->Right);
    }
}
 
//后序遍历
void lastOrderTraverse(Node* root)
{
    
    
    if( root )
    {
    
    
        lastOrderTraverse(root->Left);
        lastOrderTraverse(root->Right);
        cout<<root->data<<' ';
    }
}
 
//二叉树节点总数目
int Nodenum(Node* root)
{
    
    
    if(root == NULL)
    {
    
    
        return 0;
    }
    else
    {
    
    
        return 1+Nodenum(root->Left)+Nodenum(root->Right);
 
    }
}
 
//二叉树的深度
int DepthOfTree(Node* root)
{
    
    
    if(root)
    {
    
    
        return DepthOfTree(root->Left)>DepthOfTree(root->Right)?DepthOfTree(root->Left)+1:DepthOfTree(root->Right)+1;
    }
    if( root == NULL )
    {
    
    
        return 0;
    }
}
 
//二叉树叶子节点数
int Leafnum(Node* root)
{
    
    
    if(!root)
    {
    
    
        return 0;
    }
    else if(  (root->Left == NULL) && (root->Right == NULL) )
    {
    
    
        return 1;
    }
    else
    {
    
    
        return  (Leafnum(root->Left) + Leafnum(root->Right)) ;
    }
}
 
 
int main()
{
    
    
    Node *root = NULL;
    root = createBinaryTree();
    printf("二叉树建立成功");
    cout<<endl;
 
    cout<<"二叉树总节点数为:"<<Nodenum(root)<<endl;
 
    cout<<"二叉树深度为:"<<DepthOfTree(root)<<endl;
 
    cout<<"二叉树叶子节点数为:"<<Leafnum(root)<<endl;
 
    cout<<"前序遍历结果:"<<endl;
    preOrderTraverse(root);
    cout<<endl;
 
    cout<<"中序遍历结果:"<<endl;
    inOrderTraverse(root);
    cout<<endl;
 
    cout<<"后序遍历结果:"<<endl;
    lastOrderTraverse(root);
    cout<<endl;
 
    return 0;
}

達成するために非再帰的

非再帰トラバーサルの主なアイデアは、ループやスタックなどのデータ構造を介してバイナリ ツリーをトラバースする問題を解決することです。
二分木の構造を定義します。

struct TreeNode {
    
    
    TreeNode(int val_) :val(val_), left(NULL), right(NULL) {
    
    }
    TreeNode* left;
    TreeNode* right;
    int val;
};

事前注文トラバーサル:
最初にルート ノードにアクセスし、次に左右の子ノードにそれぞれアクセスします。非再帰的な事前注文トラバーサルでは、スタックを使用してトラバーサル プロセスをシミュレートします。

  1. 最初にルートノードをスタックに置きます
  2. ループを開始し、ルート ノードにアクセスして、ルート ノードをスタックからポップします。
  3. ルート ノードの左の子ノードがスタックにプッシュされ、最後に右の子ノードがスタックにプッシュされ、サイクルが完了します。
  4. 次に、スタックが空になるまで、前の 3 つのステップの処理がループで実行され、トラバーサルが完了します。
void PreOrderNonRecursive(TreeNode* root) {
    
    
    stack<TreeNode*> sta;
    if (root == NULL)
        return;
    sta.push(root);
    while (!sta.empty()) {
    
    
        TreeNode* pNode = sta.top();
        cout << pNode->val << endl;
        sta.pop();
        if (pNode->right != NULL)
            sta.push(pNode->right);
        if (pNode->left != NULL)
            sta.push(pNode->left);
    }
}

順序通りのトラバーサル:
最初に左の子ノードにアクセスし、次にルート ノードにアクセスし、最後に右の子ノードにアクセスします。また、スタックを使用してトラバースします。

  1. 左の子ノードが最初にアクセスされるため、最初に左端のノードがアクセスされる必要があり、最初にルート ノードがスタックにプッシュされ、スタックにプッシュされた各ノードを記録するために中間変数が使用されます。左の子ノード
  2. 真ん中にプッシュされたノードに左の子ノードがある場合は、左の子ノードがなくなるまで左の子ノードをプッシュし続けます。
  3. スタックの最上位要素がポップアウトされます。つまり、ルート ノードにアクセスされます。
  4. スタックの最上位要素に右の子ノードがあるかどうかを判断し、ある場合はその右の子ノードをスタックにプッシュします
  5. スタックが空になるまでステップ 2 ~ 4 を繰り返し、走査を完了します。
void InOrderNonRecursive(TreeNode* root) {
    
    
    stack<TreeNode*> sta;
    if (root == NULL)
        return;
    sta.push(root);
    TreeNode* pNode = root;
    while (!sta.empty()) {
    
    
        while (pNode != NULL&&pNode->left != NULL) {
    
    
            pNode = pNode->left;
            sta.push(pNode);
        }
        pNode = sta.top();
        cout << pNode->val << endl;
        sta.pop();
        if (pNode->right != NULL) {
    
    
            sta.push(pNode->right);
            pNode = pNode->right;
        }
        else
            pNode = NULL;
    }
}

ポストオーダー トラバーサル:
最初に左右の子ノードをトラバースし、最後にルート ノードをトラバースします。非再帰的なポストオーダー トラバーサルは、3 つのトラバーサル メソッドの中で最も複雑です。最も重要なことは、各ノードの左右の子ノードが NULL であるかどうか、またはその左右の子ノードが訪問されたかどうかを判断し、そうであればそのノードを訪問することです。

  1. 境界条件チェック、スタック ルート ノード
  2. 現在のスタック トップ要素を記録します。スタック トップ要素の左右の子ノードが NULL であるか、アクセスされている場合は、現在のノードにアクセスします。
  3. それ以外の場合は、右の子ノードと左の子ノードを順番にスタックにプッシュします
  4. スタックが空になるまでステップ 2 とステップ 3 をループします。
void PostOrderNonRecursive(TreeNode* root) {
    
    
    if (root == NULL)
        return;
    stack<TreeNode*> sta;
    TreeNode* curNode = root;
    TreeNode* preNode = NULL;
    sta.push(root);
    while (!sta.empty()) {
    
    
        curNode = sta.top();
        if ((curNode->left == NULL&&curNode->right == NULL) || 
            (preNode != NULL && (preNode == curNode->left || preNode == curNode->right))) {
    
    
            cout << curNode->val << endl;
            sta.pop();
            preNode = curNode;
        }
        else {
    
    
            if (curNode->right != NULL)
                sta.push(curNode->right);
            if (curNode->left != NULL)
                sta.push(curNode->left);
        }
    }
}

おすすめ

転載: blog.csdn.net/Chenjiahui_LYee/article/details/103089113