【PAT甲级笔记】二叉树树题型解法及对应题解

对应的专业英语词汇

单词意思
even number偶数
positive正数
postorder traversal后序遍历
inorder traversal中序遍历
level order traversal层序遍历
invert反转

二叉树知识点

  • 二叉树的性质:root的索引为index时,
    它的对应左子节点为index2, 右子节点为 index2+1

  • 二叉查找树的中序遍历是一个递增的序列

  • 已知两个序列,求另一个(两个步骤)

  1. 用一个序列中root的值寻找另一个序列的位置
  2. 通过该位置,将左右的startend进行更新

二叉树三种遍历之间转换:

分为三步骤=-=

  • 边界条件进行返回
  • 通过后序和先序的root寻找中序中root的位置
  • 通过递归调用函数(核心点

当start和end相等时:也与root相等,此时i寻找到的即为本身,左右子树的递归达到边界,也可将此值存入

  • 根据后序中序建树时,模板代码为:
    root为值,startend为中序遍历中的索引。
    不能通过左子树求对应右子树的根,因为后序中的起始点不确定。
void pre(int root, int start, int end) {
    //当找到叶子节点时将返回;
    if(start > end) return ;
    int i = start;
    //寻找后序中root索引对应的中序位置,设为i
    while(i < end && in[i] != post[root]) i++;
    //先序遍历的性质决定。
    pre.push_back(post[root]);
	//遍历左右的子树,start和end根据中序遍历给出,
	//子树的root点主要根据后序遍历中的位置给出,
	//右子树的根节点为父亲root的前一个,
	//左子树的根节点为:父亲root-右子树个数的前一个,即(root-(end-(i+1)+1)-1)
    pre(root-end+i-1, start, i - 1);
    pre(root - 1, i + 1, end);
    //root索引根据后序遍历的性质进行更新
}
  • 根据先序中序遍历建树
void postorder(int root, int start, int end) {
    if (start > end) return;
    int i = start;
    while (i < end && in[i] != pre[root]) i++;
    postorder(root + 1, start, i - 1);
    postorder(root + i - start + 1, i + 1, end);
    //后序遍历的性质决定
    post.push_back(pre[root]);
}
  • 根据先序和后序遍历,给出中序并判断是否唯一
void inorder(int start,int end,int root)
{
    if(start>end) return;
    int i=0;
    while(i<n&&post[end-1]!=pre[i]) i++;
    if(pre[root+1]==pre[i]) flag=true;
    inorder(start,start+i-root-2,root+1);
    in.push_back(pre[root]);
    inorder(start+i-root-1,end-1,i);
    return;
}

二叉搜索树

中序遍历一棵二叉搜索树得到的序列为从小到大的序列

struct node
{
    int v;
    node *l,*r;
    vector<int> v1;
};
node* build(node *root,int v)
{
      if(root==NULL)
       {
           root=new node();
           root->v=t1;
           root->l=root->r=NULL;
       }
       else if(root->v<t1)
         root->l=build(root->l,v);
       else root->r=build(root->r,v);
    return root;
}
int main()
{
   scanf("%d %d\n", &n1, &n2);
   node *root=NULL;
   for(int i=0;i<n2;i++)
   {
       scanf("%d",&t1);
       root=build(root,t1);
   }
   return 0;
}

AVL平衡二叉树

介绍性概念:【链接】
代码实现讲解较好【链接】
模板及样例:
类似于管理一个指针:
1.指针传递:指针传递其实是值传递的一种,它传递的是地址。值传递过程中,被调函数的形参作为被调函数的局部变量来处理改变函数形参并不影响函数外部的实参,即在函数的栈中有开辟了内存空间来存放主调函数放进来实参的值。

#include <iostream>
using namespace std;
struct node {
    int val;
    struct node *left, *right;
};
//这里*指返回一个指针。为了同main函数里的root指针类型相同
node *rotateLeft(node *root) {
    node *t = root->right;
    root->right = t->left;
    t->left = root;
    return t;
}
node *rotateRight(node *root) {
    node *t = root->left;
    root->left = t->right;
    t->right = root;
    return t;
}
node *rotateLeftRight(node *root) {
    root->left = rotateLeft(root->left);
    return rotateRight(root);
}
node *rotateRightLeft(node *root) {
    root->right = rotateRight(root->right);
    return rotateLeft(root);
}
int getHeight(node *root) {
    if(root == NULL) return 0;
    return max(getHeight(root->left), getHeight(root->right)) + 1;
}
node *insert(node *root, int val) {
    if(root == NULL) {
        root = new node();
        root->val = val;
        root->left = root->right = NULL;
    } else if(val < root->val) {
        root->left = insert(root->left, val);
        if(getHeight(root->left) - getHeight(root->right) == 2)
            root = val < root->left->val ? rotateRight(root) : rotateLeftRight(root);
    } else {
        root->right = insert(root->right, val);
        if(getHeight(root->left) - getHeight(root->right) == -2)
            root = val > root->right->val ? rotateLeft(root) : rotateRightLeft(root);
    }
    return root;
}
int main() {
    int n, val;
    scanf("%d", &n);
    node *root = NULL;
    for(int i = 0; i < n; i++) {
        scanf("%d", &val);
        root = insert(root, val);
    }
    printf("%d", root->val);
    return 0;
}

如果把代码改成引用形式,也能成立,并且看起来更易读懂

2.引用传递:被调函数的形参虽然也作为局部变量在栈中开辟了内存空间,但在栈中放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被间接寻址,形参在任意改动都直接影响到实参。

struct node
{
    int val;
    node *left,*right;
};

int getheight(node *&root)
{
    if(root==NULL)
        return 0;
    else
        return max(getheight(root->left),getheight(root->right))+1;
}
void rotateLeft(node *&root)
{
    node *t=root->right;
    root->right=t->left;
    t->left=root;
    root=t;
}

void rotateRight(node *&root)
{
    node *t=root->left;
    root->left=t->right;
    t->right=root;
    root=t;
    //cout<<"R"<<endl;
}

void RL(node *&root)
{
    rotateRight(root->right);
    rotateLeft(root);
    //cout<<"RL"<<endl;
}
void LR(node *&root)
{
    rotateLeft(root->left);
    rotateRight(root);
}
void insert1(int t,node *&root)
{
    if(root==NULL)
    {
        root=new node();
        root->left=root->right=NULL;
        root->val=t;
        //cout<<"666";
    }
    else
    {
       if(t<root->val)
           {
             insert1(t,root->left);
             if(getheight(root->right)-getheight(root->left)==-2)
             {
               if(getheight(root->left->left)-getheight(root->left->right)==1)
                    rotateRight(root);
               else if(getheight(root->left->left)-getheight(root->left->right)==-1)
                    LR(root);
             }
           }
       else
           {
             insert1(t,root->right);
             if(getheight(root->right)-getheight(root->left)==2)
             {
               if(getheight(root->right->left)-getheight(root->right->right)==-1)
                    rotateLeft(root);
               else if(getheight(root->right->left)-getheight(root->right->right)==1)
                    RL(root);
             }
            //cout<<"666";
           }
    }
}

int main()
{
   int n,t;
   scanf("%d", &n);
   node * root=NULL;
   for(int i=0;i<n;i++)
   {
       cin>>t;
       insert1(t,root);
       //cout<<root->val;
   }

PAT甲级题目举例:

中序,后序,先序遍历之间的互相求解

1020 Tree Traversals (25分)

几个注意点:
  • 若左右子树为空,直接返回未压入新节点,相当于层序遍历结点容器中对应的序号空了出来,不影响后面按序号排序的顺序
  • sort()函数可以对vector进行排序,但排序时需要给cmp比较函数
  • vector类型,首地址并不能使用:a 这种写法。a.begin()被用来给出对应首地址,a.end()被用来给出对应结束的地址。(在fill()填充函数和sort()排序函数中进行了对应实验。)
  • 传入的根节点需要为1,不然index*2会一直为0
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;
struct node
{
    int index,value;
};
int post[50],in[50];
vector<node> level;

bool cmp(node d1, node d2)
{
    return d1.index<d2.index;
}

//对应的层序求解
void lev(int start, int end ,int root, int index)
{
    if(start>end) return;
    level.push_back({index,post[root]});
    int i=start;
    while(i<=end && post[root]!=in[i])
        i++;
    lev(start,i-1,root-end-1+i,2*index);
    lev(i+1,end, root-1, 2*index+1);

}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&post[i]);
    for(int i=0;i<n;i++)
        scanf("%d",&in[i]);
    lev(0,n-1,n-1,1);
    //对索引进行排序,就算有缺失,也能进行很好的排序并不影响结果。
    sort(level.begin(),level.end(),cmp);
    printf("%d",level[0].value);
    for(int j=1;j<level.size();j++)
        printf(" %d", level[j].value);
    return 0;
}

1086 Tree Traversals Again (25分)

  • 这道题有个很有意思的点,就是压入栈的节点按先序遍历的顺序…(????结果还真的是,之前根本想不到啊)
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>

using namespace std;
int number,n;
vector<int> pos,pre,ino;

void dfs(int start, int end1, int root)
{
    //root 非值,为先序索引,end,start为中序索引
    if(start>end1)
        return;
    int index=start;
    while(pre[root]!=ino[index]&& index<end1) index++;
    dfs(start, index-1, root+1);
    dfs(index+1,end1, root+index+1-start);
    pos.push_back(pre[root]);
}

int main()
{

    scanf("%d", &n);
    char op[6];
    vector<int> s;
    for(int i=0;i<2*n;i++)
    {
        scanf("%s",&op);
        if(strlen(op)==4)
          {
            scanf("%d", &number);
            pre.push_back(number);
            s.push_back(number);
            //printf("push\n");
          }
        else
          {
           ino.push_back(s[s.size()-1]);
            //printf("pop %d\n ",pre[pre.size()-1]);
            s.pop_back();
          }

    }
    dfs(0, n-1, 0);
    for(int i=0;i<pos.size()-1;i++)
        printf("%d ", pos[i]);
    printf("%d",pos[pos.size()-1]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值