Skip to content

编程作业辅助

问题描述

数据结构课程作业:实现一个AVL树,支持插入、删除和查找操作。不太理解AVL树的旋转操作,代码总是有bug。

传统思路

  1. 看教材 - 文字描述太抽象
  2. 网上搜代码 - 抄完不理解,改不了bug
  3. 调试到崩溃 - 不知道哪里错了
  4. 问同学 - 他们也不太会

问题:不理解原理,抄来的代码出问题不会改

AI思维

第一步:先理解原理,不急着写代码

我要实现AVL树,但不太理解旋转操作。

请帮我解释:
1. 为什么AVL树需要旋转
2. 什么情况下需要左旋、右旋
3. 用简单的例子演示旋转过程
4. 四种旋转情况(LL, RR, LR, RL)分别是什么

AI 解释原理:

AVL树旋转详解

为什么需要旋转

AVL树是平衡二叉搜索树,任何节点的左右子树高度差不超过1。

当插入或删除节点后,可能破坏平衡,此时需要旋转来恢复。

不平衡的例子:

    30 (高度差=2,不平衡!)
   /
  20
 /
10

右旋(解决LL情况)

什么时候用:左子树的左边太重

右旋前:         右旋后:
    30              20
   /               /  \
  20      →      10    30
 /
10

操作步骤:
1. 让20成为新根
2. 30变成20的右孩子
3. 20原来的右孩子(如果有)变成30的左孩子

左旋(解决RR情况)

什么时候用:右子树的右边太重

左旋前:         左旋后:
10                  20
  \                /  \
   20      →     10    30
     \
      30

双旋(LR和RL情况)

LR情况:左子树的右边太重

    30          30           20
   /    左旋   /    右旋    /  \
  10    →    20     →    10    30
    \        /
    20      10

RL情况:右子树的左边太重(先右旋后左旋)

记忆口诀

LL右旋,RR左旋
LR先左后右,RL先右后左
旋转的字母表示往哪边转

第二步:分步实现代码

现在我理解原理了。请帮我用C++分步实现AVL树:
1. 先定义节点结构
2. 然后实现右旋和左旋函数
3. 最后实现插入函数

每一步都解释代码的含义。

AI 分步实现:

AVL树实现(C++)

Step 1: 节点结构

cpp
struct AVLNode {
    int val;           // 节点值
    int height;        // 节点高度(叶子节点高度为1)
    AVLNode* left;     // 左孩子
    AVLNode* right;    // 右孩子

    AVLNode(int v) : val(v), height(1), left(nullptr), right(nullptr) {}
};

Step 2: 辅助函数

cpp
// 获取节点高度(处理空指针)
int getHeight(AVLNode* node) {
    return node ? node->height : 0;
}

// 获取平衡因子 = 左子树高度 - 右子树高度
int getBalanceFactor(AVLNode* node) {
    return node ? getHeight(node->left) - getHeight(node->right) : 0;
}

// 更新节点高度
void updateHeight(AVLNode* node) {
    node->height = 1 + max(getHeight(node->left), getHeight(node->right));
}

Step 3: 旋转操作

cpp
// 右旋(处理LL情况)
//     y                x
//    / \              / \
//   x   T3    →     T1   y
//  / \                  / \
// T1  T2              T2   T3
AVLNode* rightRotate(AVLNode* y) {
    AVLNode* x = y->left;    // x是y的左孩子
    AVLNode* T2 = x->right;  // T2是x的右子树

    // 执行旋转
    x->right = y;   // y变成x的右孩子
    y->left = T2;   // T2变成y的左孩子

    // 更新高度(先更新y,因为y现在是x的孩子)
    updateHeight(y);
    updateHeight(x);

    return x;  // x成为新的根
}

// 左旋(处理RR情况)
AVLNode* leftRotate(AVLNode* x) {
    AVLNode* y = x->right;
    AVLNode* T2 = y->left;

    y->left = x;
    x->right = T2;

    updateHeight(x);
    updateHeight(y);

    return y;
}

Step 4: 插入操作

cpp
AVLNode* insert(AVLNode* node, int val) {
    // 1. 标准BST插入
    if (!node) return new AVLNode(val);

    if (val < node->val)
        node->left = insert(node->left, val);
    else if (val > node->val)
        node->right = insert(node->right, val);
    else
        return node;  // 不允许重复值

    // 2. 更新高度
    updateHeight(node);

    // 3. 获取平衡因子
    int bf = getBalanceFactor(node);

    // 4. 根据不平衡情况进行旋转
    // LL情况:左子树的左边插入
    if (bf > 1 && val < node->left->val)
        return rightRotate(node);

    // RR情况:右子树的右边插入
    if (bf < -1 && val > node->right->val)
        return leftRotate(node);

    // LR情况:左子树的右边插入
    if (bf > 1 && val > node->left->val) {
        node->left = leftRotate(node->left);  // 先左旋
        return rightRotate(node);              // 再右旋
    }

    // RL情况:右子树的左边插入
    if (bf < -1 && val < node->right->val) {
        node->right = rightRotate(node->right);  // 先右旋
        return leftRotate(node);                  // 再左旋
    }

    return node;  // 平衡,无需旋转
}

第三步:调试帮助

我的代码运行结果不对。插入序列 [30, 20, 10] 后,根节点应该是20,但我的结果是30。

这是我的rightRotate函数:
[粘贴你的代码]

请帮我找出bug。

AI 帮助调试:

我看到你的问题了。你的 insert 函数没有接收旋转后的返回值。

问题代码

cpp
if (bf > 1 && val < node->left->val)
    rightRotate(node);  // ❌ 没有return!

修复后

cpp
if (bf > 1 && val < node->left->val)
    return rightRotate(node);  // ✅ 返回新的根

旋转后返回的是新的子树根节点,必须要 return。

第四步:验证理解

请给我几个测试用例,让我验证我的AVL树实现是否正确。

AI 提供测试用例:

AVL树测试用例

测试1:单次右旋

插入顺序: 30, 20, 10
预期结构:
    20
   /  \
  10   30
验证: 根节点为20,中序遍历 10,20,30

测试2:双旋LR

插入顺序: 30, 10, 20
预期结构:
    20
   /  \
  10   30

测试3:复杂场景

插入顺序: 50, 30, 70, 20, 40, 60, 80, 10
验证: 所有节点平衡因子在 [-1, 1] 范围内

结果对比

指标传统方式AI思维
理解程度似懂非懂真正理解
调试效率盲目尝试精准定位
学习收获只会抄会改会拓展
完成时间可能卡住顺利完成

关键洞察

  1. 先理解后编码:不要急着写代码,先搞懂原理
  2. 分步实现:复杂任务拆成小步骤
  3. 精准调试:把错误现象和代码告诉AI,快速定位
  4. 测试验证:让AI提供测试用例确保正确

编程作业Prompt模板

## 理解原理
我要实现[算法/数据结构],请帮我解释:
1. 核心原理是什么
2. 关键步骤有哪些
3. 用简单例子演示

## 实现代码
请帮我用[语言]分步实现,每一步解释代码含义。

## 调试帮助
我的代码结果不对:
- 输入:[输入]
- 预期输出:[预期]
- 实际输出:[实际]
- 我的代码:[代码]
请帮我找出bug。

Released under the MIT License.