C++使用遞歸和非遞歸的方式實現二叉樹的遍歷

二叉樹是常見的數據結構,常見的有三種遍歷方式:前序遍歷(根→左→右)、中序遍歷(左→根→右)和後序遍歷(左→右→根)。使用遞歸的方式實現遍歷比較簡單,書上看到一個不使用遞歸的方法比較複雜,記錄一下。

二叉樹節點定義

typedef struct node {
    int value;
    struct node *left;
    struct node *right;
}Node;

前序遍歷(遞歸)

遞歸方式實現前序遍歷,先打印根節點,然後依次把左節點和右節點當做根節點來打印。

void Tree::preOrderRecur(Node *tree) {
    if (!tree) return;

    std::cout << tree->value << " ";
    preOrderRecur(tree->left);
    preOrderRecur(tree->right);
}

前序遍歷(非遞歸)

非遞歸遍歷使用棧結構(先進先出)來暫存節點,先打印根節點,同時依次把右節點和左節點壓到棧中。接着取棧頂節點作爲根節點進行打印,然後再依次把右節點和左節點壓到棧中。。。這樣可以始終保持先打印的是根節點,且左節點比右節點先打印。

void Tree::preOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    nodeStack.push(tree);
    Node *root = nullptr;
    while (!nodeStack.empty()) {
        root = nodeStack.top();
        nodeStack.pop();
        std::cout << root->value << " ";

        if (root->right)
            nodeStack.push(root->right);

        if (root->left)
            nodeStack.push(root->left);
    }
}

中序遍歷(遞歸)

遞歸方式依次打印左、根、右節點。

void Tree::inOrderRecur(Node *tree) {
    if (!tree) return;

    inOrderRecur(tree->left);
    std::cout << tree->value << " ";
    inOrderRecur(tree->right);
}

中序遍歷(非遞歸)

先依次把最左邊的節點壓到棧中,(這樣彈出節點打印的時候可以保證先打印左節點再打印根節點)。一旦檢測到左節點爲空,就彈出棧頂的節點並打印該節點(此時可以理解爲根節點)。接着把其右節點作爲根節點循環處理。

void Tree::inOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    Node *root = tree;

    while (!nodeStack.empty() || root) {
       if (root) {
           nodeStack.push(root);
           root = root->left;
       } else {
           root = nodeStack.top();
           std::cout << root->value << " ";

           nodeStack.pop();
           root = root->right;
       }
    }
}

後序遍歷(遞歸)

依次處理左右節點再打印根節點。

void Tree::posOrderRecur(Node *tree) {
    if (!tree) return;

    posOrderRecur(tree->left);
    posOrderRecur(tree->right);
    std::cout << tree->value << " ";
}

後序遍歷(非遞歸)

把根節點壓入棧中,記錄棧頂元素爲current當前節點。如果左節點不爲空則壓入棧中,否則把右節點壓入棧中(不爲空時)。如果當前節點的左右節點都爲空則打印當前節點,(可以保證根節點最後打印)。
壓入右節點的時候要保證上一次沒有打印過右節點,避免根節點和右節點打印後再壓棧變成死循環。壓入左節點的時候也要保證上次打印不是做節點,且右節點不存在或者已經處理過了,(可以保證左節點始終在右節點的前面被打印)。

void Tree::posOrderUnRecur(Node *tree) {
    if (!tree) return;

    std::stack<Node*> nodeStack;
    nodeStack.push(tree);

    Node *last = nullptr;
    Node *current = nullptr;

    while (!nodeStack.empty()) {
        current = nodeStack.top();

        if ((current->left) && (last != current->left) 
            && ((last != current->right) || !current->right)) {
            nodeStack.push(current->left);
        } else if(current->right && (last != current->right)) {
            nodeStack.push(current->right);
        } else {
            std::cout << current->value << " ";
            last = current;
            nodeStack.pop();
        }
    }
}

二叉樹的創建和銷燬

這裏我使用一個類似二叉樹層序遍歷的數組的形式創建二叉樹(避免歧義使用-1代表空節點),銷燬二叉樹要釋放所有申請空間的節點。

// 創建
Node *Tree::createBiTree(const int *arr, int size, int index) {
    if (index >= size || arr[index] == -1){
        return nullptr;
    }

    Node *node = new Node;

    if (index != size) {
        node->value = arr[index];
        node->left = createBiTree(arr, size, 2*(index+1)-1);
        node->right = createBiTree(arr, size, 2*(index+1));
    } else {
        node->left = NULL;
        node->right = NULL;
    }
    return node;
}

// 銷燬
void Tree::destoryBiTree(Node *tree) {
    if (tree == NULL)
        return;

    destoryBiTree(tree->left);
    if (tree->left)
        tree->left = NULL;

    destoryBiTree(tree->right);
    if (tree->right)
        tree->right = NULL;

    delete tree;
}

測試

int main() 
{

     /*  
     * 使用如下二叉樹
     *             1
     *         2       3
     *      4    5   6   7
     *    8  x x  x x x 9  10
     * /
    int array[15] = {1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,9,10};
    Tree *ins = new Tree;
    Node *tree = ins->createBiTree(array, 15, 0);

    std::cout << "\n前序遍歷(遞歸):" << std::endl;
    ins->preOrderRecur(tree);

    std::cout << "\n前序遍歷(非遞歸):" << std::endl;
    ins->preOrderUnRecur(tree);

    std::cout << "\n中序遍歷(遞歸):" << std::endl;
    ins->inOrderRecur(tree);

    std::cout << "\n中序遍歷(非遞歸):" << std::endl;
    ins->inOrderUnRecur(tree);

    std::cout << "\n後序遍歷(遞歸):" << std::endl;
    ins->posOrderRecur(tree);

    std::cout << "\n後序遍歷(非遞歸):" << std::endl;
    ins->posOrderUnRecur(tree);

    ins->destoryBiTree(tree);
}

程序輸出:

前序遍歷(遞歸):
1 2 4 8 5 3 6 7 9 10 
前序遍歷(非遞歸):
1 2 4 8 5 3 6 7 9 10 
中序遍歷(遞歸):
8 4 2 5 1 6 3 9 7 10 
中序遍歷(非遞歸):
8 4 2 5 1 6 3 9 7 10 
後序遍歷(遞歸):
8 4 5 2 6 9 10 7 3 1 
後序遍歷(非遞歸):
8 4 5 2 6 9 10 7 3 1 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章