day14.|二叉树01

本文详细介绍了二叉树的基本概念,包括满二叉树、完全二叉树和各种搜索树类型。重点讨论了深度优先遍历(前序、中序、后序)和广度优先遍历(层序)的实现,以及递归遍历的三要素。此外,还探讨了平衡二叉搜索树在数据结构中的应用和迭代法的使用,强调了前序与中序遍历的代码风格差异。
摘要由CSDN通过智能技术生成

1.二叉树理论

1.种类:满二叉树、 完全二叉树、二叉搜索树、平衡二叉搜索树

(搜索一个结点的时间复杂度是long n级别的)

2.存储方式:顺序存储、链式存储

3.遍历方式:

深度优先遍历 和 广度优先遍历

深度优先遍历:前序遍历、中序遍历、后序遍历(1.递归法  2.迭代法)

广度优先遍历:层序遍历(迭代法)

其中,栈 -> 递归 -> 深度优先遍历;队列 -> 迭代 -> 广度优先遍历

4.二叉搜索树 对结点的布局结构并无要求,仅对结点上的元素有顺序要求。

5.平衡二叉搜索树的应用: map、set、multimap、multiset

这些容器中的key和value 均是有序的,因为底层实现是平衡二叉搜索树,它是有序树。

2.递归遍历

写递归有三要素:

1.确定递归函数的参数和返回值;

2.确定终止条件;

3.确定单层循环的逻辑。

前序遍历、中序遍历、后序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:

    void traversal(TreeNode* cur,vector<int>& vec){
        if(cur == NULL)  
        return;  //终止条件

        //前序(中左右)
        vec.push_back(cur -> val);
        traversal(cur -> left,vec);
        traversal(cur -> right,vec);
    } 

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root,result);
        return result;
    }
};

补存: 

vector的操作:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    // 创建空向量
    vector<int> myVector;

    // 向向量尾部添加元素
    myVector.push_back(10); //添加元素时,使用的是push_back()
    myVector.push_back(20);
    myVector.push_back(30);

    // 访问元素
    cout << myVector[0] << endl;  // 输出第一个元素

    // 删除元素
    myVector.pop_back();  // 删除最后一个元素
                          //弹出元素时,使用的是pop_back()

    int firstElement = myVector.front(); //获得第一个元素

    // 获取大小
    cout << myVector.size() << endl;  // 输出向量大小

    // 修改元素
    myVector[0] = 5;

    // 清空向量
    myVector.clear();

    return 0;
}

3.二叉树的非递归 ---- 迭代法 

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

用栈也可以是实现二叉树的前后中序遍历了。

注:前后序遍历的迭代法代码相似,但是中序遍历的迭代法的代码有较大的不同。是两种不同的代码风格。

理论上,所有的递归逻辑均可以使用栈来模拟出来。

前序:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        //迭代法    
        stack<TreeNode*> st;
        vector<int> result;

        //空结点不入栈
        if(root == NULL) return result;

        //前序(中左右)
        st.push(root);
        while(!st.empty()){  //注意终止条件,迭代法的终止条件是看栈 或者 队列是否为空
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node -> val);
            // if(node -> left) st.push(node -> left);
            if(node -> right) st.push(node -> right);
            if(node -> left) st.push(node -> left); //一定注意顺序!!!
            //先处理结点的右孩子,后处理结点的左孩子。唯有这样,出栈的时候才是中左右的顺序。
        }
        return result;
    }
};

后序: 

后序的代码与前序类似,仅仅交换了几句代码的顺序。

前序,中左右;后序,左右中。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

中序: 

1.二叉树的遍历主要是两个操作:访问(遍历结点) 处理(将元素放入result数组)

2.前序和后序 的进行访问和处理操作时,两个操作是同步的,操作的是同一个结点,要访问的元素和要处理的元素顺序是一致的,都是中间节点;

但是中序 的访问和处理操作时,操作的结点不同。所以逻辑上不同,访问的顺序和处理的顺序不同。

在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

指针遍历结点,栈记录我们遍历过的元素。

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        //迭代法(中序:左中右)
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root; //指针访问
        while(!st.empty() || cur != NULL){
            if(cur != NULL){
                st.push(cur);
                cur = cur -> left;
            }
            else{
                cur = st.top();
                st.pop();
                result.push_back(cur -> val);
                cur = cur -> right;
            }
        }
        return result;
    }

总结: 

用迭代法写出了二叉树的前后中序遍历,可以看出前序和中序是完全两种代码风格,并不像递归写法那样代码稍做调整,就可以实现前后中序。

这是因为前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值