【九度OJ】题目1201-二叉排序树

本文通过对比两种构建二叉搜索树的方法,详细分析了递归逻辑的优化过程,指出递归基的重要性,并给出具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目


建树过程是递归,"递归的思路不是很复杂",经过题目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;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值