题目1009:二叉搜索树
来源:互联网 发布:日本2017福袋 网络 编辑:程序博客网 时间:2024/05/16 14:33
时间限制: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
- 需要树一棵,字符串四个
实现中遇到的错误
- 递归基直接返回
return creat(x);
,少了一个操作,root = creat(x); return root;
。- 原因:递归的层次没有区分清楚,错误地认为构造一个节点,然后返回给
M->lchild
,这个想法中的M是上一层递归中的节点,不应出现在这层的递归实例中。 - 正确思路:本层递归实例,是最底层的递归,要往空树中插入节点,空树就是root,构造一个节点,要赋值给root,再返回root。
- 原因:递归的层次没有区分清楚,错误地认为构造一个节点,然后返回给
- 非递归基时,没有写return,致命错误,递归最重要的接口是传入的参数和返回的值。
- 如果不指明非递归基的递归实例中的返回值,将只能在递归基中返回,即只返回叶节点,每层递归结束后,树都只有一个节点!
- 一层正确的递归应该是,传入一棵树,添加一个节点,再返回增加了节点以后的树。
- 结合上述正确情况,再仔细想想不写return的情况,新一层的递归实例,不会增加树的节点!整体递归结束后,树只有一个节点——最后插入的节点。
- 对递归函数
insert()
的理解需要再进一步,每次传入一棵树的根的指针,最后返回这个指针。
- 出现一个“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
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 题目1009:二叉搜索树
- 九度题目1009:二叉搜索树
- 九度题目1009:二叉搜索树
- Jobdu 题目1009:二叉搜索树
- STL专题总结
- Cocos2d-x Programmers Guide v3.12
- 数据处理——One-Hot Encoding
- 超酷的 Vim 搜索技巧
- 我的希望,我的理想
- 题目1009:二叉搜索树
- 新手要掌握的linux 命令
- Android中通过ListView的实现简单新闻列表
- ReactNative重写的OSChina的Git@OSC客户端
- TCP套接字的java实现
- C++ 继承和组合混搭下的构造和析构调用原则
- MFC 派生图表
- nswoj 2327 MX的密码锁
- Android null value in entry: streamInputs=null 前言中不允许有内容