二叉树的基本操作 |中根遍历|迭代|已知先序序列和中序序列重建二叉树|混合栈洗数|

二叉树:是一种有层次的存储数据的结构;可以把查找的复杂度降低到logn;

struct node{
    typename data;//数据域
    node* lchild;
    node* rchild;
    node(int x):data(x),lchild(NULL),rchild(NULL){}
};
node* newnnode(typename x){//新建结点
    node* Node=new node;
    Node->data=v;
    Node->lchild=Node->rchild=NULL;
    return Node;//生成一个结点并返回
}
void insert(node*&,data){//注意&
    if(root=NULL) {
        root=newNode(x);
        return root ;
    }
    if(case1) insert(root->lchild,data);//按条件插入到左子树
    else insert(root->rchild,data);//或右子树
}
node* Create(int data[],int n){//新建二叉树
    node*root=NULL;
    for(int i=0;i<n;i++)
        insert(root, data[i]);
return root;
}

区别

root=NULL:指针是不是空

*root=NULL:指针指向的位置是不是空

遍历

递归

三要素:参数和返回类型;递归终点;单层逻辑;

后序遍历:每个子结点可以是父结点;把子结点当父结点看,先递归穷尽其左子树,再走尽其右子树,最后是它;

//先序遍历
void preorder(node*root ){
    if(node=NULL) return ;
    printf("%d\n",root->data);
    preorder(root->lchild);
    preorder(root->rchild);
}
//非递归的先根遍历

void  Order( Node* t){
    stack S;
    Node*p=t;
    while(1){
        while(p!=NULL){
            printf("%d",p->data);//先根序列 :进栈序列
            S.push(p);
            p=p->lchild;
        }
        if(S.Isempty()) return ;
        p=S.pop();
         //  printf("%d",p->data); 中根序列:出栈序列
        p=p->rchild;
    }
return ;
}

//中序遍历
//只有中序遍历可以完整的确定一颗二叉树
void preorder(node*root ){
    if(node=NULL) return ;
    preorder(root->lchild);
    printf("%d\n",root->data);
    preorder(root->rchild);
}
//后序遍历
//特点:最后一个一定是根节点;
void postorder(node*root ){
    if(node=NULL) return ;
    preorder(root->lchild);
    printf("%d\n",root->data);
    preorder(root->rchild);
}

迭代

顺序访问到的数据存储在栈中,通过修改弹栈顺序实现 先根/中根/后根遍历

先根遍历:处理顺序和访问顺序一致

 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->right) st.push(node->right);           // 右  先入栈,后出栈
            if (node->left) st.push(node->left);             // 左 
        }
        return result;
    }

中根遍历:处理顺序和访问顺序不一致,需要指针

vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }

问题:如何已知先序序列和中序序列重建二叉树?

思路:

找到pre1=intk;即中序遍历的根;对中序遍历,[1,k-1]的结点都是是左子树,[k+1,n]是右子树;

对先序遍历来说,(1,k]个结点对应左子树, [k+1,n]对应右子树;

对左/右子树,继续寻找k';直到左/右子树为空

6baf382f78854e509a629d299e3966e8.jpeg

//先序序列区间 [preL,preR],中序序列 [inL,inR]
node* Create(int preL,int preR,int inL,int inR){
    if(preL>preR) return NULL;//
    node* root=new node;
    root->data=pre[preL];//当前根节点的值
    for(int k=inL,k<=inR;i++){
        if(in[k]==pre[preL]) break;//找到中序遍历的根结点位置
    }
    int numleft=k-inL;
    root->lchild=Create(preL+1,preL+numleft,inL,k-1);
    root->rchild=Create(preL+numleft+1,preR,k+1,inR );

    return root;
}

结论:

中序遍历可以和其他任意顺序一起确定一颗二叉树;必须有中序遍历来区分左/右子树

 而后序遍历,前序遍历,层序遍历中任意2/3个都无法构成二叉树!


问题2:

-先序序列(前序序列)为a,b,c,d 的不同二叉树的个数 /  n个结点的二叉树有多少种的形态?

-栈混洗总数的数量?

思路:已知,前序序列和中序序列可以唯一地确定一棵二叉树。

把前序序列看作为入栈次序,把中序序列看作为出栈次序

那么题意相当于“以序列 a,b,c,d 为入栈次序,则出栈序列的个数为?”

一个入栈顺序可以确定的出栈顺序为 C(2n,n) / (n+1)(卡特兰数)。


层序遍历、分层存储的两种方式


    int depth(TreeNode *root)
    {
        if (root == NULL)
            return 0;
        return max(depth(root->left), depth(root->right)) + 1;
    }
    void levelOrder(vector<vector<int>> &ans, TreeNode *node, int level)
    {
        if (!node)
            return;
        ans[level].push_back(node->val);
        levelOrder(ans, node->left, level - 1);
        levelOrder(ans, node->right, level - 1);
    }
    vector<vector<int>> levelOrderBottom(TreeNode *root)
    {
        int d = depth(root);
        vector<vector<int>> ans(d, vector<int>{});
        levelOrder(ans, root, d - 1);
        return ans;
    }

 


    vector<vector<int>> levelOrder(TreeNode *root)
    {
        vector<vector<int>> ret;
        if (!root)
            return ret;
        queue<TreeNode *> q;
        q.push(root);
        while (!q.empty())
        {
            int currNodeSize = q.size();
            ret.push_back(vector<int>());

            for (int i = 1; i <= currNodeSize; i++)
            {
                TreeNode *node = q.front();
                q.pop();

                ret.back().push_back(node->val);

                if (node->left)
                    q.push(node->left);
                if (node->right)
                    q.push(node->right);
            }
        }
        return ret;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值