二叉树的先序遍历


二叉树的三种遍历方式,其中每一种遍历方式都有三种实现方式。

节点定义:

struct TreeNode
{
    int val;
    TreeNode *left,*right;
    TreeNode(int val){
        this->val = val;
        this ->left = this->right = NULL;
    }
};

先序遍历

img

以上面这张图为例:我们讲讲树的三种遍历方式:

先序遍历:先访问根节点,然后访问左孩子,最后访问右孩子。

所以,上面遍历的结果是:GEDACHS。

下面,我们来看看具体代码实现

1.递归实现

void preOrder(TreeNode *root){
    if (root==NULL)
        return;
    cout<<root->val<<endl;
    preOrder(root->left);
    preOrder(root->right);
}

2.使用辅助栈

实现思路:1.将根节点入栈
       2.每次从栈顶弹出一个节点,访问该节点
       3.把当前节点的右孩子入栈
       4.把当前节点的左孩子入栈

具体实现:

void preOrder2(TreeNode *root){
    if (root == NULL)
        return;
    stack<TreeNode*> stk; //开辟一个栈空间
    stk.push(root);
    while(!stk.empty()){
        TreeNode* pNode = stk.pop();
        cout<<pNode->val;
        if (pNode->right!=NULL)
            stk.push(pNode->right);
        if (pNode->left!=NULL)
            stk.push(pNode->left);

	}

}

3.Morris遍历

Morris遍历,常数的空间即可在O(n)时间内完成二叉树的遍历。

O(1)空间进行遍历困难之处在于在遍历的子结点的时候如何重新返回其父节点?

在Morris遍历算法中,通过修改叶子结点的左右空指针来指向其前驱或者后继结点来实现的。

其本质:线索二叉树(Threaded Binary Tree),通过利用叶子节点空的right指针,指向中序遍历的后继节点,从而避免了对 stack 的依赖。

具体实现:

void preOrder(TreeNode* root){
    if (root == NULL)
        return;

    TreeNode* pNode = root;
    while(pNode != NULL){
        if (pNode->left == NULL)
        {
            cout<<pNode->val<<endl;
            pNode = pNode->right;
        }
        else{
            TreeNode* pPre = pNode->left;
            while(pPre->right != NULL && pPre->right != pNode){
                pPre = pPre->right;
            }

            if (pPre->right == NULL)
            {
                /* code */
                pPre->right = pNode;
                cout<<pNode->val<<endl;
                pNode = pNode->left;
            }
            else{
                pPre->right = NULL;
                pNode = pNode->right;
            }
        }
    }
}

附:二叉树的先序遍历

二叉树的先序遍历

附:二叉树的中序遍历

二叉树的中序遍历

附:二叉树的后序遍历

二叉树的后序遍历

附:二叉树的三种遍历对比及用图片展现说明

二叉树的三种遍历对比及用图片展现说明

### 二叉树序遍历的原理 序遍历是一种深度优遍历方法,其核心原则是按照“根 -> 左 -> 右”的顺序访问二叉树中的节点[^1]。这意味着,在访问任何子树之前,总是访问该子树的根节点。 具体来说,对于任意给定的一棵树或子树: - 首访问根节点; - 接着递归地对左子树执行相同的序遍历操作; - 最后递归地对右子树执行同样的序遍历操作。 这种递归性质使得序遍历非常适合通过递归函数实现[^4]。 --- ### 序遍历的递归实现思路 递归实现的核心在于分解问题为更小的部分,并逐步解决每一个部分。以下是递归实现的具体逻辑: 1. 如果当前节点为空,则直接返回。 2. 否则,首访问当前节点(打印或存储其值)。 3. 对当前节点的左子树调用递归函数。 4. 对当前节点的右子树调用递归函数。 #### 示例代码 (递归版) ```c void preOrderTraversalRecursive(Node* root) { if (root == NULL) { // 基本情况:遇到空节点停止 return; } printf("%d ", root->data); // 访问根节点 preOrderTraversalRecursive(root->left); // 递归遍历左子树 preOrderTraversalRecursive(root->right); // 递归遍历右子树 } ``` 上述代码展示了如何利用递归来实现序遍历。 --- ### 序遍历的非递归实现思路 虽然递归实现简单直观,但在某些情况下可能因为栈溢出而失效。为了克服这一点,可以通过显式的堆栈模拟递归过程来实现非递归版本的序遍历。 主要步骤如下: 1. 初始化一个辅助栈并将根节点压入栈中。 2. 当栈不为空时,重复以下操作: - 弹出栈顶元素并访问它。 - 将弹出节点的右子节点(如果有)压入栈中。 - 将弹出节点的左子节点(如果有)压入栈中。 注意:由于栈的特点是后进出,所以要将右子节点压入栈中,然后再压入左子节点,从而保证左子节点被优处理[^2]。 #### 示例代码 (非递归版) ```c void preOrderTraversalIterative(Node* root) { if (root == NULL) { return; } Stack stack; // 定义一个栈 initStack(&stack); push(&stack, root); while (!isEmpty(&stack)) { Node* node = pop(&stack); // 弹出栈顶元素 printf("%d ", node->data); // 访问节点 if (node->right != NULL) { // 压入右子节点 push(&stack, node->right); } if (node->left != NULL) { // 再压入左子节点 push(&stack, node->left); } } } ``` 此代码片段展示了一种基于栈的非递归解决方案[^2]。 --- ### 结论 无论是递归还是非递归的方式,它们都遵循“根 -> 左 -> 右”的基本规则。递归方法更加简洁易懂,而非递归方法则提供了更好的内存控制能力,尤其是在面对深嵌套的二叉树时更为适用。 --- 问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

、工藤新一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值