PAT:树

来源:互联网 发布:怎么测试网络丢包率 编辑:程序博客网 时间:2024/05/21 18:38

1064. 完全二叉搜索树

给定一个数列,要求构造一个完全二叉搜索树,并输出其层序遍历的结果。
思路
巧妙的思路是利用完全二叉树的的一个特殊属性:
按照层序遍历的顺序给所有节点标号,根节点标1,第二层的左节点标为2,右节点标为3……
记当前节点的下标为i,则其左孩子节点的下标为2i,右孩子节点的下标为2i+1

现在,我们要得到层序遍历的结果,已知的则是排列好的递增序列。
我们注意到,对于一棵完全二叉搜索树,中序遍历可以得到递增序列。反过来,用中序遍历的方法也可以构造出一棵完全二叉搜索树。
在构造的过程中,我们就在tree数组中得到了层序遍历结果。

#include<stdlib.h>#include<iostream>#include<vector>#include<string>#include<queue>#include<stack>#include<limits.h>#include<cmath>#include<set>#include<map>#include<utility>#include<algorithm>using namespace std;vector<int> node;vector<int> tree(1005,0);int pos,n;void build(int root) {    if(root>n) return;    //计算左子树和右子树的下标位置    int lson = root<<1,rson =(root<<1)+1;     build(lson);    tree[root] = node[pos++];    build(rson);}int main() {    int pp;    cin>>n;    for(int i=0;i<n;++i) {        cin>>pp;        node.push_back(pp);    }    sort(node.begin(),node.end());    pos = 0;    build(1);    for(int i=1;i<=n;i++) {        cout<<tree[i];        if(i!=n) cout<<' ';    }    cout<<endl;    return 0;}

1020. 树的遍历

已知后序遍历和中序遍历的数列,要求构造二叉树,然后按层序输出。
思路
后序遍历的最后一个元素一定是整棵树的根,从后向前,分别是右、左子树的根。
相应的,中序序列中根结点左边的元素一定属于左子树,右边的元素一定属于右子树。
借鉴BFS层序遍历的思路:

  • postorder的最后一个元素即为整棵树的根结点;
  • 在inorder中找到该根结点,以它为分界,左边的元素构成左子树,右边的元素构成右子树,将两个范围[0,i-1] [i+1,n-1]存入队尾;
  • 从队首取一个元素(范围),考察inorder中该范围的元素,寻找postorder中从后往前出现的第一个元素,它就是该范围构成的子树的根结点;
  • 以该结点为分界,将该范围再细分成两个范围(左、右子树)存入队尾;
  • 继续直到队列变空。
#include <iostream>#include <vector>#include <queue>#include <algorithm>using namespace std;struct range {    int left;    int right;};int n,one;queue<range> que;vector<int> postorder;vector<int> inorder;vector<int> res;//寻找范围函数void search(int left,int right,int root) {    if(left>right) return;    for(int i=left;i<=right;i++) {        if(inorder[i]==root) {//如果找到了这个root            range new1={left,i-1};            que.push(new1);            range new2={i+1,right};            que.push(new2);            break;        }    }}int main() {    //初始化    cin>>n;    for(int i=0;i<n;i++) {        cin>>one;        postorder.push_back(one);    }    for(int i=0;i<n;i++) {        cin>>one;        inorder.push_back(one);    }    range a={0,n-1};    que.push(a);    //BFS层序遍历    vector<int>::iterator it,le,ri;    while(!que.empty()) {//只要queue非空        //取出队首元素        range aa=que.front();        que.pop();        if(aa.left>aa.right || right<0) continue;        //cout<<aa.left<<' '<<aa.right<<endl;        //开始在postorder里找指定子树的根节点        le=inorder.begin()+aa.left;        ri=inorder.begin()+aa.right+1;//注意这里的范围        //按照范围找出子树的根节点        for(int i=n-1;i>=0;i--) {            it=find(le,ri,postorder[i]);            if( it != ri ) {//如果找到了该根结点                res.push_back(postorder[i]);                search(aa.left,aa.right,postorder[i]);                break;            }        }    }    for(int i=0;i<n;i++) {        cout<<res[i];        if(i < (n-1) ) cout<<' ';    }    cout<<endl;    return 0;}

1021. 求图的最大深度

已知一个图,计算从任何一点开始,以此为根节点,树的最大深度。不保证图的连通性,如果不全部连通,求连通子图的个数。
如果用邻接矩阵的形式保存图形,那么将是O(n*n)的空间复杂度,就是10^8*4B个数据,为4*10^5KB内存,题目是3.2*10^5KB内存,所以内存超出。
所以我们想到的是用邻接表来保存图,但是我们实现的时候不用链表,而是用向量数组vector<int> adj[MAX]的形式来保存图,这个方式极好,要常用。

#include<stdio.h>#include<string>#include<iostream>#include<vector>using namespace std;#define MAX 10001int maxInt=0xffffffff>>1;bool visited[MAX];int height[MAX];vector<int> adj[MAX];int n;int dfs(int node) {    bool flag=false;//node有其他与之相连的结点的标志    int max=-1;    visited[node]=true;    vector<int>::iterator it=adj[node].begin();    //遍历与node相连的结点    for(;it!=adj[node].end();it++) {        int i=*it;        if(!visited[i]) {            flag=true;            int temp=dfs(i);            if(max<temp) max=temp;        }    }    if(!flag)      return 1;    return max+1;}void init() {    for(int i=1;i<=n;i++) visited[i]=false;}int main() {    while(cin>>n) {        int a,b;        visited[n]=false;        for(int i=1;i<=n;i++) adj[i].clear();        //构建邻接表        for(int i=1;i<=n-1;i++) {            visited[i]=false;            cin>>a>>b;            adj[a].push_back(b);            adj[b].push_back(a);        }        bool iserr=false;        int count=1;        int max_height=-1;        for(int i=1;i<=n;i++) {            init();            height[i]=dfs(i);            if(height[i]>max_height)                 max_height=height[i];            //判断是否全部结点都连通            for(int j=1;j<=n;j++) {                if(!visited[j]) {                    count++;                    dfs(j);                }            }            if(count>1) {              iserr=true;              break;            }        }        if(iserr)            printf("Error: %d components\n",count);        else {            for(int i=1;i<=n;i++) {                if(height[i]==max_height)                    printf("%d\n",i);            }        }    }    return 0;}

1115. 计算BST的最后两行的节点数

给定一组数,要求构造BST,并求该树的最后两行的节点数,并且求和。
思路
首先根据输入的数列建立二叉搜索树,这里用的是递归的方法。
然后用bfs的方法计算最后最底两层的节点数,最后按照题目要求输出答案。

#include <iostream>  #include <vector>  #include <queue>  using namespace std;  struct node  {      int val;      node *left,*right;      node(int v):val(v),left(NULL),right(NULL){}  };  void build(node *&root,int val)  {      if(root==NULL)      {          root=new node(val);          return;      }      if(val<=root->val) build(root->left,val);      else build(root->right,val);  }  int main()  {      int n;      cin>>n;      node *root=NULL;      for(int i=0;i<n;i++)      {          int v;          cin>>v;          build(root,v);      }      queue<node*>que;      que.push(root);      int n1=0,n2=0;      while(!que.empty())      {          n2=n1;          n1=que.size();          for(int i=0;i<n1;i++)          {              node *tmp=que.front();              que.pop();              if(tmp->left) que.push(tmp->left);              if(tmp->right) que.push(tmp->right);          }      }      printf("%d + %d = %d\n",n1,n2,n1+n2);  } 

1119. 前序后序转中序

题意
给出一棵树的结点个数n,以及它的前序遍历和后序遍历,输出它的中序遍历,如果中序遍历不唯一就输出No,且输出其中一个中序即可,如果中序遍历唯一就输出Yes,并输出它的中序
思路
用unique标记是否唯一,如果为1就表示中序是唯一的。
已知二叉树的前序和后序是无法唯一确定一颗二叉树的,因为可能会存在多种情况,这种情况就是一个结点可能是根的左孩子也可能是根的右孩子,如果发现了一个无法确定的状态,置unique = 0,又因为题目只需要输出一个方案,可以假定这个不可确定的孩子的状态是右孩子,接下来的问题是如何求根结点和左右孩子划分的问题了。
首先我们需要知道树的表示范围,需要四个变量,分别是前序的开始位置prel,前序的结束位置prer,后序的开始位置postl,后序的结束位置postr,前序的第一个元素和后序的最后一个元素是相等的:树的根结点,以后序的根结点的前面一个结点作为参考,寻找这个结点在前序的位置,就可以根据这个位置来划分左右孩子,递归处理。

#include <cstdio>#include <vector>using namespace std;vector<int> ans;int *pre, *post, unique = 1;int findFromPre (int x, int l, int r) { //在前序中寻找元素x    for (int i = l; i <= r; i++) {        if (x == pre[i]) {            return i;        }    }    return -1;}void setIn (int prel, int prer, int postl, int postr) { //递归地划分左右子树    if (prel == prer) {        ans.push_back(pre[prel]);        return;    }    if (pre[prel] == post[postr]) {        int x = findFromPre(post[postr - 1], prel + 1, prer);        if (x - prel > 1) {            setIn(prel + 1, x - 1, postl, postl + x - prel - 2);            ans.push_back(post[postr]);            setIn(x, prer, postl + x - prel - 2 + 1, postr - 1);        } else {            unique = 0;            ans.push_back(post[postr]);            setIn(x, prer, postl + x - prel - 2 + 1, postr - 1);        }    } }int main() {    int n = 0;    scanf("%d", &n);    pre = new int [n];    post = new int [n];    for (int i = 0; i < n; i++) {        scanf("%d", &pre[i]);    }    for (int i = 0; i < n; i++) {        scanf("%d", &post[i]);    }    setIn(0, n - 1, 0, n - 1);    printf("%s\n", unique ? "Yes" : "No");    printf("%d", ans[0]);    for (int i = 1; i < ans.size(); i++) {        printf(" %d", ans[i]);    }    printf("\n");    return 0;}
0 0
原创粉丝点击