【九度OJ】题目1009-二叉搜索树

题目


思路

  • 构建二叉搜索树,并保存先序遍历和中序遍历的序列在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;
} 

转载于:https://www.cnblogs.com/tambura/p/5227694.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、查找 1. 算法设计题 :已知n元顺序表a0, a1, … , an-1按关键字递增有序存储。给定关键字值key,编写算法用对分查找求下标i,满足ai-1<key且aikey。 2. 编程题:输入n个两两互不相等的整数,以这些整数为关键字建立平衡的二叉排序树。判断该二叉树是否为平衡的,输出判断结果;输出该二叉树的序遍历关键字访问次序。 3. 从空树起连续插入以下20个关键字构建m=4的B-树。 50, 15, 09, 18, 03, 85, 33, 72, 48, 22, 91, 88, 11, 99, 06, 56, 68, 77, 43, 36。 4. 16个关键字组成的5阶B-树如下图所示,请按关键 字递减的次序删除所有结点至空树,画出每删除1个关键字后得到B-树,直至空树。 5. 12个关键字如本电子教案例1所示,设H(K)=K mod 13,地址空间范围0~15,用二次探测再散列解决冲突。画出哈希表;若各元素等概率查找,求成功查找时的平均查找长度。 二、内部排序 1.算法设计与分析题:将直接插入排序的内循环改造为使用对分查找实现元素插入,请写出基于对分查找的插入排序算法并给出其时间复杂度分析。 2.算法设计:将教案给出的非递归直接插入排序和冒泡排序算法用递归算法实现。 3.算法设计:带附加头结点单链表将各数据结点按关键字升序连接。 4.编程题:键盘输入n个无符号整数,用链式基数排序实现由小到大排序,输出排序结果。 提示:对于C语言32bit宽的unsigned类型,可以采用16进制形式来实现基数排序,即32bit共有8个16进制位,每个16进制位进行一趟分配和收集,共8趟。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值