题目1009:二叉搜索树

来源:互联网 发布:日本2017福袋 网络 编辑:程序博客网 时间:2024/05/16 14:33
题目1009:二叉搜索树

时间限制:1 秒

内存限制:32 兆

特殊判题:

提交:9254

解决:4068

题目描述:
判断两序列是否为同一二叉搜索树序列
输入:
开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。
输出:

如果序列相同则输出YES,否则输出NO

样例输入:
25674325432675763420
样例输出:
YESNO
来源:
2010年浙江大学计算机及软件工程研究生机试真题
答疑:
解题遇到问题?分享解题心得?讨论本题请访问:http://t.jobdu.com/thread-7733-1-1.html
思路:其实很简单,写一个生成二叉排序树的函数,然后再写一个判断是否相等的即可。代码如下:
#include<iostream>#include <string>#include<fstream>using namespace std;struct node{int key;node *lchild;node *rchild;};int BSTInsert(node *&bt,int key);//强调,因为指针bt要改变,所以用引用型指针 bool BSTJudge(node *BT,node *bt);//用来判断二叉排序树的节点是否相等 int main(){ifstream in;in.open("2.txt");int n;while(in>>n,n){string str;in.get();//读取第一行数字输入队列的回车键 getline(in,str);//其实可以完全直接cin>>str node *BT=NULL;//原始二叉搜索树的根节点指针 for(int i=0;i<str.length();i++)BSTInsert(BT,str[i]-'0');for(int i=0;i<n;i++){string str1;getline(in,str1); node *bt=NULL;for(int j=0;j<str1.length();j++)BSTInsert(bt,str1[j]-'0');if(BSTJudge(BT,bt))cout<<"YES"<<endl;else cout<<"NO"<<endl;}}return 0;} int BSTInsert(node *&bt,int key) //再次强调,因为指针bt要改变,所以用引用型指针 {if(bt==NULL){bt=new node;bt->lchild=bt->rchild=NULL;bt->key=key;return 1;}else{if(key<bt->key)return BSTInsert(bt->lchild,key);//不能少了return,否则没有返回值  else return BSTInsert(bt->rchild,key);}}bool BSTJudge(node *BT,node *bt){if(BT!=NULL && bt!=NULL){if(BT->key!=bt->key)return false;else{//if(BSTJudge(BT->lchild,bt->lchild)==false) return false;//if(BSTJudge(BT->rchild,bt->rchild)==false) return false;return BSTJudge(BT->lchild,bt->lchild) && BSTJudge(BT->rchild,bt->rchild);}}return true; }

(2)附上别人的代码,体会不同的思路

思路

  • 构建二叉搜索树,并保存先序遍历和中序遍历的序列在samplePreOrder,sampleInOrder
  • 每遇到一个新的序列,构建一棵二叉搜索树,保存先序遍历和中序遍历的序列testPreOrder,testInOrder
  • 需要树一棵,字符串四个

实现中遇到的错误

  1. 递归基直接返回return creat(x);,少了一个操作,root = creat(x); return root;
    • 原因:递归的层次没有区分清楚,错误地认为构造一个节点,然后返回给M->lchild,这个想法中的M是上一层递归中的节点,不应出现在这层的递归实例中。
    • 正确思路:本层递归实例,是最底层的递归,要往空树中插入节点,空树就是root,构造一个节点,要赋值给root,再返回root。
  2. 非递归基时,没有写return,致命错误,递归最重要的接口是传入的参数和返回的值。
    • 如果不指明非递归基的递归实例中的返回值,将只能在递归基中返回,即只返回叶节点,每层递归结束后,树都只有一个节点!
    • 一层正确的递归应该是,传入一棵树,添加一个节点,再返回增加了节点以后的树。
    • 结合上述正确情况,再仔细想想不写return的情况,新一层的递归实例,不会增加树的节点!整体递归结束后,树只有一个节点——最后插入的节点。
    • 对递归函数insert()的理解需要再进一步,每次传入一棵树的根的指针,最后返回这个指针。
  3. 出现一个“Runtime Error”
    • 参考序列为sampleString,待考察序列为testString。
    • 第一组testString读入并构造二叉搜索树后,没有清空loc,使第二组testString直接在前面的树中插入。
    • 当测试数据再多几组时,loc一直增加,使得保存遍历结果的字符串testInOrder超出了预设的长度,内存访问错误。
    • 在Visual Studio中,运行时不会检查字符串是否以0结束,strcmp()仍然会做比较,并给出NO的结果,似乎输出正确,实则访问了非法地址而没有提示。

总结

  • 递归
    • 在逻辑上,要区分开层次,每层递归调用不可互相干扰。
    • 表现在实现上,要写好每层递归的:
      • 传入的参数;
      • 返回的值;
      • 递归基的语句中是对谁操作,谁是指参与操作的变量是属于哪层递归调用(调用栈)的。
      • 函数体,是一次调用过程中,对形参的操作(语句中全是形参);上一层递归调用本层递归,将以实参的形式向本层传入数据,不要被实参干扰本层递归的思路。
  • 一两组数据测试的结果不可信,运行时的错误不易被发现,只看静态代码很难找出逻辑错误。当遇到运行时的错误,调试的能力至关重要
    • 合理设置断点,合理选择单步、逐过程、继续。
    • 合理选择监视哪些变量,程序运行到什么阶段,从哪里开始出错,全是从监视的变量中观察到的。
#include <stdio.h>#include <string.h>using namespace std;typedef struct BTNode {    BTNode * lchild;    BTNode * rchild;    char data;}BTNode;BTNode tree[15];int loc;int strIdx; //遍历树时存储到字符串的当前位置//构造节点BTNode * creat(char x) {    tree[loc].data = x;    tree[loc].lchild = tree[loc].rchild = NULL;    return &tree[loc++];}//将节点插入树,构建二叉搜索树BTNode * insert(char x, BTNode * root) {    if (root == NULL) {        /*return creat(x); 【错误1】,到达递归基,直接返回一个指针         *到达递归基时insert(x, M->lchild)         *M无lchild,插入后要将M->lchild->data=x,         *而这里仅仅创造了一个新节点,没有把新创造的节点插入树中         *将新构造的节点插入树中的方法是将creat返回的指针赋值给root         */        root = creat(x);        return root;    }    else if (x < root->data) {        //insert(x, root->lchild);到达递归基后返回一个指针,不保存这个返回值,错        root->lchild = insert(x, root->lchild);    }    else if (x > root->data) {        root->rchild = insert(x, root->rchild);    }    /*【错误2】没有写return,因此所有的返回值都是上面那个return返回的     *在main中,往树根插入节点,并将insert的返回值赋值给根节点     *若此处返回值总是上面那个return,则每次将新构造的节点返回给根节点     *树永远无法往下生长,每新插入一个节点,都把这个节点作为根     */    return root;}//遍历树,将结果保存在字符串str中void preOrder(BTNode * root, char str[]) {    str[strIdx] = root->data;    str[++strIdx] = 0; //每写入一个字符都添加结束符    if (root->lchild) {        preOrder(root->lchild, str);    }    if (root->rchild) {        preOrder(root->rchild, str);    }}void inOrder(BTNode * root, char str[]) {    if (root->lchild) {        inOrder(root->lchild, str);    }    str[strIdx] = root->data;    str[++strIdx] = 0;    if (root->rchild) {        inOrder(root->rchild, str);    }}void postOrder(BTNode * root, char str[]) {    if (root->lchild) {        postOrder(root->lchild, str);    }    if (root->rchild) {        postOrder(root->rchild, str);    }    str[strIdx] = root->data;    str[++strIdx] = 0;}int main(){    int n;    while (scanf("%d", &n) != EOF) {        if (n == 0) break;        char sampleString[15];        char samplePreOrder[15];        char sampleInOrder[15];        char testString[15];        char testPreOrder[15];        char testInOrder[15];        char blank[5];        //gets_s(blank); //消除第一行的换行符的影响        //gets_s(sampleString);        scanf("%s", sampleString);        loc = 0;        BTNode * R;        //构造作为参照的二叉搜索树        R = NULL;        for (int i = 0; sampleString[i] != '\0'; i++) {            //insert(sampleString[i], R);这个不保存insert的返回值会使R一直就是空树            R = insert(sampleString[i], R);        }        strIdx = 0; //字符串下标回到0        preOrder(R, samplePreOrder);        strIdx = 0;        inOrder(R, sampleInOrder);        loc = 0; //将树清空        for (int j = 0; j < n; j++) {  //循环n次,每次输出一个结果YES或NO                                       //构造测试数据组成的树,并存储遍历序列            //gets_s(testString);            scanf("%s", testString);            R = NULL;            //【错误3】这里忘记将树清空,如果n=2时,将把第二组testString继续往树中插入,从而造成数组越界            loc = 0;            for (int i = 0; testString[i] != '\0'; i++) {                R = insert(testString[i], R);            }            strIdx = 0;            preOrder(R, testPreOrder);            strIdx = 0;            inOrder(R, testInOrder);            //输出判断结果            if (strcmp(samplePreOrder, testPreOrder) == 0 && strcmp(sampleInOrder, testInOrder) == 0) {                printf("YES\n");            }            else {                printf("NO\n");            }         } //for    } //while    return 0;} 
(3)来一种没有用结构体和链表指针,但用的是数组存储解决的。

思路:由于仅仅只需要遍历二叉树即可,所以没有必要花费时间建立指针树,只需要在相应位置储存数据遍历即可。

开一个一维数组,从a[1]开始存储,a[1]的左孩子是a[2],右孩子是a[3],

同理,a[2]的左孩子是a[4],右孩子是a[5],a[3]的左孩子是a[6],右孩子是a[7]

如图:

                     a[1]

            a[2]         a[3]

       a[4]         a[5]   a[6]    a[7]

可以得出结论,a[i]的左孩子是a[2*i],右孩子是a[2*i+1]。首先将空的数组清为-1(输入值都是大于0的),然后对插入的值判断,如果当前节点为-1,说明有空间,将数放在此位置即可。如果当前节点不为-1,说明位置已被占,按照二叉排序树的定义,我们根据它与此节点的大小来判断它往左还是右插

#include<stdio.h>#include<string.h>char s[100];int a[100],b[100];void CreatTree(char s[],int a[]){   int j,n=strlen(s);   int i=0;   while(i<n)   {      for(j=0;j<100;)      {          if(a[j]==-1)          {             a[j]=s[i]-'0';             break;            }          else          {             if(a[j]>s[i]-'0')             j=2*j;             else             j=2*j+1;          }      }       i++;         }}int main(){    int i,j,n,m;    while(scanf("%d",&n),n!=0)    {       memset(s,0,sizeof(s));       scanf("%s",s);       for(i=1;i<100;i++)       a[i]=-1;       CreatTree(s,a);       while(n--)       {          memset(s,0,sizeof(s));          scanf("%s",s);          for(i=1;i<100;i++)          b[i]=-1;          CreatTree(s,b);          for(i=0;i<100;i++)          if(a[i]!=b[i]) break;          if(i==100)          printf("YES\n");          else          printf("NO\n");       }    }    return 0;}


0 0
原创粉丝点击