【九度OJ】题目1201-二叉排序树
建树过程是递归,"递归的思路不是很复杂",经过题目1078的训练,直接开始编码。提交及修改的过程告诉自己,这是一个错觉,对递归的理解还应该再进一步。
自己的实现
#include <stdio.h>
typedef struct BTNode {
BTNode * lchild;
BTNode * rchild;
int data;
}BTNode;
BTNode tree[110];
int loc; //指向tree[]中第一个空节点,建树过程中会移动
//申请一个新节点
BTNode * creat(int x) {
tree[loc].data = x;
tree[loc].lchild = tree[loc].rchild = NULL;
loc++;
return &tree[loc - 1];
}
//构造BST
void build(int x, BTNode *root) {
if (loc == 0) { //如果树空,直接插入
tree[loc].lchild = tree[loc].lchild = NULL;
tree[loc].data = x;
loc++;
}
else if (x <= root->data) { //如果待插入x小于根,插入左子树
if (root->lchild != NULL) {
build(x, root->lchild);
}
else {
root->lchild = creat(x);
}
}
else { //如果待插入x大于根,插入右子树
if (root->rchild != NULL) {
build(x, root->rchild);
}
else {
root->rchild = creat(x);
}
}
}
void preOrder(BTNode * root) {
printf("%d ", root->data);
if (root->lchild != NULL) {
preOrder(root->lchild);
}
if (root->rchild != NULL) {
preOrder(root->rchild);
}
}
void inOrder(BTNode * root) {
if (root->lchild != NULL) {
inOrder(root->lchild);
}
printf("%d ", root->data);
if (root->rchild != NULL) {
inOrder(root->rchild);
}
}
void postOrder(BTNode * root) {
if (root->lchild != NULL) {
postOrder(root->lchild);
}
if (root->rchild != NULL) {
postOrder(root->rchild);
}
printf("%d ", root->data);
}
int main()
{
int n, next;
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) {
scanf("%d", &next);
build(next, &tree[0]); //每插入一个,都从根开始找合适的位置
}
preOrder(&tree[0]);
printf("\n");
inOrder(&tree[0]);
printf("\n");
postOrder(&tree[0]);
printf("\n");
loc = 0; //将树清空
}
return 0;
}
直接实现的这个版本超时,慢的原因是什么???不太确定,可能的原因是:
- 当条件判断if嵌套很深时,要简化逻辑,尽量避免太深的if嵌套。无法避免的时候,这个if分支内部的操作也不能太复杂???
- 由对象组成的数组,首先访问数组,再访问对象的成员,再计算并赋值。这个操作只有一行,但它的复杂度应该比O(1)要大???
一个递归的程序超时,必然是递归本身的逻辑写的太复杂了,递归过程没有构思好。分析下面的代码哪里需要改进
//构造BST
void build(int x, BTNode *root) {
/*如果树空,直接插入。这个判断条件只在插入根节点(第一个节点)时用到。
*不是一个好的递归基,导致下面的分支变复杂,每个分支中多出一层判断
*除了全树的根节点,每插入一个节点,都要做此不必要的判断。
*/
if (loc == 0) {
tree[loc].lchild = tree[loc].lchild = NULL;
tree[loc].data = x;
loc++;
}
else if (x <= root->data) { //如果待插入x小于根,插入左子树
if (root->lchild != NULL) { //这层的if判断可以省略
build(x, root->lchild);
}
else {
/*由于递归基没有构思好,多出这么一个分支,这部分工作和在一棵空树种插入新节点(就是递归基)是一样的。
*因为build这个程序的本身,是往树中插入节点,在一棵很高的树中插入节点,是一次build,在这次build的过程中会进入到子树中,
*对子树而言,这也要启用一个新的build过程;
*直到子树成为NULL,这就到达了递归基:往空树中插入节点
*再回头看上面的if(loc==0)的情况,无法往空“子树”中插入节点
*这个else和下面的else,实际上将一个递归基复制,放入两个分支中
*/
root->lchild = creat(x);
}
}
else { //如果待插入x大于根,插入右子树
if (root->rchild != NULL) {
build(x, root->rchild);
}
else {
root->rchild = creat(x);
}
}
}
修改上述代码
- 通过了简单的测试,只是满足了基本的“做到”的要求,这样的代码中一定还有优化的空间。
- 修正一个思路,该思路事关对“递归层次/递归调用栈”的理解
- 不应判断
A->lchild == NULL,这个想法的着眼点在A->lchild上,错。 - 应该是每调用一次build,都相当于往一棵新树中插入节点,对于二叉排序树,新节点总是在底层的NULL位置处插入,即总是往一个空树中插入。
- 当前传入的树root是否为空,是这次递归调用
build(x, root)应该考虑的着眼点,即root == NULL是否成立。着眼点在本次递归中以root为根的树。
- 不应判断
- 修改递归基为
if(root == NULL) - 修改了递归函数
build的返回值,不再是void,而是返回一个BTNode *,引起了错误。一个函数的返回值改变,需要改变:- 所有该函数出现的位置处的代码
- 为了使用该函数,提前申请的一些变量。(该函数的上下文)
完整代码
#include <stdio.h>
typedef struct BTNode {
BTNode * lchild;
BTNode * rchild;
int data;
}BTNode;
BTNode tree[110];
int loc; //指向tree[]中第一个空节点,建树过程中会移动
//申请一个新节点
BTNode * creat(int x) {
tree[loc].data = x;
tree[loc].lchild = tree[loc].rchild = NULL;
loc++;
return &tree[loc - 1];
}
//构造BST
BTNode * build(int x, BTNode *root) {
/*
if (loc == 0) { //如果树空,直接插入
tree[loc].lchild = tree[loc].lchild = NULL;
tree[loc].data = x;
loc++;
}
*/
if (root == NULL) {
root = creat(x);
return root;
}
else if (x < root->data) { //如果待插入x小于根,插入左子树dd
root->lchild = build(x, root->lchild);
}
else if(x > root->data) { //如果待插入x大于根,插入右子树
root->rchild = build(x, root->rchild);
}
return root;
}
void preOrder(BTNode * root) {
printf("%d ", root->data);
if (root->lchild != NULL) {
preOrder(root->lchild);
}
if (root->rchild != NULL) {
preOrder(root->rchild);
}
}
void inOrder(BTNode * root) {
if (root->lchild != NULL) {
inOrder(root->lchild);
}
printf("%d ", root->data);
if (root->rchild != NULL) {
inOrder(root->rchild);
}
}
void postOrder(BTNode * root) {
if (root->lchild != NULL) {
postOrder(root->lchild);
}
if (root->rchild != NULL) {
postOrder(root->rchild);
}
printf("%d ", root->data);
}
int main()
{
int n, next;
while (scanf("%d", &n) != EOF) {
loc = 0;
BTNode * root = NULL;
for (int i = 0; i < n; i++) {
scanf("%d", &next);
root = build(next, root); //每插入一个,都从根开始找合适的位置
}
preOrder(&tree[0]);
printf("\n");
inOrder(&tree[0]);
printf("\n");
postOrder(&tree[0]);
printf("\n");
}
return 0;
}
【九度OJ】题目1201-二叉排序树的更多相关文章
- 九度oj 题目1201:二叉排序树
题目描述: 输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历. 输入: 输入第一行包括一个整数n(1<=n<=100). 接下来的一行包括n个整数. 输出: 可能有多组测试 ...
- 九度OJ 题目1384:二维数组中的查找
/********************************* * 日期:2013-10-11 * 作者:SJF0115 * 题号: 九度OJ 题目1384:二维数组中的查找 * 来源:http ...
- hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人
钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- 九度oj题目&吉大考研11年机试题全解
九度oj题目(吉大考研11年机试题全解) 吉大考研机试2011年题目: 题目一(jobdu1105:字符串的反码). http://ac.jobdu.com/problem.php?pid=11 ...
- 九度oj 题目1007:奥运排序问题
九度oj 题目1007:奥运排序问题 恢复 题目描述: 按要求,给国家进行排名. 输入: 有多组数据. 第一行给出国家数N,要求排名的国家数M,国家号 ...
- 九度oj 题目1087:约数的个数
题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...
- 九度OJ题目1105:字符串的反码
tips:scanf,cin输入字符串遇到空格就停止,所以想输入一行字符并保留最后的"\0"还是用gets()函数比较好,九度OJ真操蛋,true?没有这个关键字,还是用1吧,还是 ...
- 九度oj题目1009:二叉搜索树
题目描述: 判断两序列是否为同一二叉搜索树序列 输入: 开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束. 接 ...
- 九度oj题目1002:Grading
//不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...
- 九度OJ题目1003:A+B
while(cin>>str1>>str2)就行了,多简单,不得不吐槽,九度的OJ真奇葩 题目描述: 给定两个整数A和B,其表示形式是:从个位开始,每三位数用逗号", ...
随机推荐
- 解决Eclipse10配置Pydev不成功的问题
本人在线配置还在本地配置后 重启Eclipse,Windows-Preferences中并无Python选项,新建项目也无Python可选 这个尝试了好多种方法,重新安装Eclipse10,重新安装j ...
- lintcode : 二叉树的最小深度
题目: 二叉树的最小深度 给定一个二叉树,找出其最小深度. 二叉树的最小深度为根节点到最近叶子节点的距离. 样例 给出一棵如下的二叉树: 1 / \ 2 3 / \ 4 ...
- [转]C++常见内存错误汇总
在系统开发过程中出现的bug相对而言是比较好解决的,花费在这个上面的调试代价不是很大,但是在系统集成后的bug往往是难以定位的bug(最好方式是打桩,通过打桩可以初步锁定出错的位置,如:进入函数前打印 ...
- window下安装composer and yii2
我的环境是集合包xampp 1,下载composer:下载地址https://getcomposer.org/download/, 点击蓝色字体“Composer-Setup.exe” 2,安装com ...
- C++:析构函数
析构函数的特点: 1.析构函数与类名相同,但它前面必须加上波浪号~ 2.析构函数不返回任何值,在定义析构函数时,是不能说明它的类型的,甚至说明void类型也不能 3.析构函数没有参数,因此不能被重载. ...
- SqlServer 如何以脚本形式导出数据
你是否遇到这样的情况,在公司导出一个数据库,回到家里导入自己的电脑里,然后发现数据库版本不匹配,这真是一个悲剧. 那么以下这个方法就可以避免这个悲剧,将数据以脚本的形式导出,这样灵活性更好. 1.选择 ...
- 简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现。
简单的算法题, Find Minimum in Rotated Sorted Array 的Python实现. 题目: Suppose a sorted array is rotated at som ...
- 使用 Oracle GoldenGate 在 Microsoft SQL Server 和 Oracle Database 之间复制事务
使用 Oracle GoldenGate 在 Microsoft SQL Server 和 Oracle Database 之间复制事务 作者:Nikolay Manchev 分步构建一个跨这些平台的 ...
- log4j配置webapp日志系统
1.基础知识: Log4j的中文文档 (这是根据最新的log4j(jakarta-log4j-1.2.8)的开发包自带文档的manual翻译的) http://dev.csdn.net/develop ...
- uva11181Probability|Given
枚举,条件概率. 2^20次方等于100w,是大约可以没准还能过的. 二进制枚举时,如果买东西的人恰好为r个,设概率为p,就将sum[i]+=p(sum[i]为r个人买东西时第i个人买东西的概率),t ...