1 实现二叉树所有节点左右子树交换

#include 
#include 
// 二叉树节点结构定义
struct TreeNode {
    int data;
    struct TreeNode* lchild;  // 左子树指针
    struct TreeNode* rchild;  // 右子树指针
};
// 创建新节点
struct TreeNode* createNode(int data) {
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    if (node == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    node->data = data;
    node->lchild = NULL;
    node->rchild = NULL;
    return node;
}
// 交换所有节点的左右子树
void swapLeftRight(struct TreeNode* root) {
    if (root == NULL) {  // 空树直接返回
        return;
    }
    // 交换当前节点的左右子树
    struct TreeNode* temp = root->lchild;
    root->lchild = root->rchild;
    root->rchild = temp;
    // 递归处理左子树
    swapLeftRight(root->lchild);
    // 递归处理右子树
    swapLeftRight(root->rchild);
}
// 前序遍历(用于验证结果)
void preOrder(struct TreeNode* root) {
    if (root == NULL) {
        return;
    }
    printf("%d ", root->data);
    preOrder(root->lchild);
    preOrder(root->rchild);
}
// 释放二叉树内存
void freeTree(struct TreeNode* root) {
    if (root == NULL) {
        return;
    }
    freeTree(root->lchild);
    freeTree(root->rchild);
    free(root);
}
int main() {
    // 构建示例二叉树
    struct TreeNode* root = createNode(1);
    root->lchild = createNode(2);
    root->rchild = createNode(3);
    root->lchild->lchild = createNode(4);
    root->lchild->rchild = createNode(5);
    printf("交换前前序遍历:");
    preOrder(root);
    printf("\n");
    // 执行左右子树交换
    swapLeftRight(root);
    printf("交换后前序遍历:");
    preOrder(root);
    printf("\n");
    // 释放内存
    freeTree(root);
    return 0;
}

代码说明:

  1. 节点结构:使用struct TreeNode定义二叉树节点,包含数据域和左右子树指针。
  2. 创建节点createNode函数负责动态分配节点内存并初始化。
  3. 交换逻辑swapLeftRight函数通过递归实现:
    • 先交换当前节点的左右子树
    • 再递归交换左子树和右子树
  4. 验证方式:通过前序遍历输出交换前后的节点序列,直观展示交换效果。
  5. 内存管理freeTree函数递归释放所有节点内存,避免内存泄漏。

运行结果:

对于示例树:

    1
   / \
  2   3
 / \
4   5

输出为:

交换前前序遍历:1 2 4 5 3
交换后前序遍历:1 3 2 5 4

时间复杂度为O(n)(每个节点处理一次),空间复杂度为O(h)h为树的高度,递归栈深度)。

2 使用队列来实现二叉树的层次遍历,同时统计度为 1 的结点数目

算法思路

  1. 层次遍历:借助队列,从根节点开始,依次将节点入队,然后出队并处理,同时将其左右子节点入队(若存在)。
  2. 统计度为 1 的结点:对于每个出队的节点,判断其左、右子节点的存在情况
    • 若只有左子节点或只有右子节点,则该节点的度为 1,计数加 1。

C 语言实现代码

#include 
#include 
// 二叉树结点结构
typedef struct TreeNode {
    int data;
    struct TreeNode *lchild, *rchild;
} TreeNode;
// 队列结点结构
typedef struct QueueNode {
    TreeNode *treeNode;
    struct QueueNode *next;
} QueueNode;
// 队列结构
typedef struct {
    QueueNode *front, *rear;
} Queue;
// 初始化队列
void initQueue(Queue *q) {
    q->front = q->rear = NULL;
}
// 入队操作
void enQueue(Queue *q, TreeNode *node) {
    QueueNode *newNode = (QueueNode *)malloc(sizeof(QueueNode));
    newNode->treeNode = node;
    newNode->next = NULL;
    if (q->rear == NULL) {
        q->front = q->rear = newNode;
    } else {
        q->rear->next = newNode;
        q->rear = newNode;
    }
}
// 出队操作
TreeNode* deQueue(Queue *q) {
    if (q->front == NULL) return NULL;
    QueueNode *temp = q->front;
    TreeNode *node = temp->treeNode;
    q->front = q->front->next;
    if (q->front == NULL) q->rear = NULL;
    free(temp);
    return node;
}
// 统计度为1的结点数目
int countDegree1Nodes(TreeNode *root) {
    if (root == NULL) return 0;
    Queue q;
    initQueue(&q);
    enQueue(&q, root);
    int count = 0;
    while (q.front != NULL) {
        TreeNode *node = deQueue(&q);
        // 判断结点的度是否为1
        if ((node->lchild != NULL && node->rchild == NULL) ||
            (node->lchild == NULL && node->rchild != NULL)) {
            count++;
        }
        if (node->lchild != NULL) enQueue(&q, node->lchild);
        if (node->rchild != NULL) enQueue(&q, node->rchild);
    }
    return count;
}
// 创建二叉树结点(示例用)
TreeNode* createNode(int data) {
    TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode));
    node->data = data;
    node->lchild = node->rchild = NULL;
    return node;
}
// 主函数示例
int main() {
    // 构建示例二叉树
    TreeNode *root = createNode(1);
    root->lchild = createNode(2);
    root->rchild = createNode(3);
    root->lchild->lchild = createNode(4);
    root->rchild->lchild = createNode(5);
    root->rchild->rchild = createNode(6);
    root->rchild->lchild->lchild = createNode(7);
    int result = countDegree1Nodes(root);
    printf("度为1的结点数目:%d\n", result);
    return 0;
}

代码说明

  • 队列操作initQueue初始化队列,enQueue实现节点入队,deQueue实现节点出队,用于层次遍历的顺序控制。
  • 统计逻辑:在层次遍历过程中,对每个节点判断其左、右子节点的存在情况,若只有一个子节点则计数加 1。
  • 时间复杂度:\(O(n)\)(n为二叉树结点数,每个结点入队、出队各一次)。
  • 空间复杂度:\(O(n)\)(队列最多存储一层的结点,最坏情况下为满二叉树的最后一层,结点数约为\(n/2\))。

算法思路

  1. 层次遍历:利用队列(std::queue)实现二叉树的层次遍历,按层访问每个节点。
  2. 统计度为 1 的节点:对每个节点,判断其左、右子节点的存在情况:
    • 若仅存在左子节点或仅存在右子节点,则该节点的度为 1,计数加 1。

C++ 代码实现

#include 
#include   // 用于队列操作
using namespace std;
// 二叉树节点结构
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 统计度为1的节点数量
int countDegree1Nodes(TreeNode* root) {
    if (root == nullptr) {
        return 0;  // 空树,直接返回0
    }
    queue q;  // 队列用于层次遍历
    q.push(root);        // 根节点入队
    int count = 0;       // 计数变量
    while (!q.empty()) {
        TreeNode* curr = q.front();  // 取出队头节点
        q.pop();
        // 判断当前节点的度是否为1(仅左或仅右子节点存在)
        bool hasLeft = (curr->left != nullptr);
        bool hasRight = (curr->right != nullptr);
        if (hasLeft != hasRight) {  // 异或:一个存在,一个不存在
            count++;
        }
        // 左右子节点入队(若存在)
        if (hasLeft) {
            q.push(curr->left);
        }
        if (hasRight) {
            q.push(curr->right);
        }
    }
    return count;
}
// 构建示例二叉树(用于测试)
TreeNode* buildExampleTree() {
    // 构建如下二叉树:
    //       1
    //      / \
    //     2   3
    //    /   / \
    //   4   5   6
    //      /
    //     7
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);  // 节点2的左子节点
    root->right->left = new TreeNode(5); // 节点3的左子节点
    root->right->right = new TreeNode(6);// 节点3的右子节点
    root->right->left->left = new TreeNode(7); // 节点5的左子节点
    return root;
}
// 释放二叉树内存
void freeTree(TreeNode* root) {
    if (root == nullptr) return;
    freeTree(root->left);
    freeTree(root->right);
    delete root;
}
int main() {
    TreeNode* root = buildExampleTree();
    int result = countDegree1Nodes(root);
    cout << "度为1的节点数量:" << result << endl;  // 示例中结果为2(节点2和节点5)
    freeTree(root);  // 释放内存,避免泄漏
    return 0;
}

代码说明

  1. 节点结构TreeNode包含值val和左右子节点指针leftright,构造函数初始化节点。
  2. 层次遍历:使用std::queue存储节点,通过push(入队)和pop(出队)操作实现按层访问。
  3. 统计逻辑:对每个节点,通过判断leftright是否存在(异或逻辑),确定其度是否为 1,若满足则计数加 1。
  4. 内存管理freeTree函数递归释放所有节点内存,避免内存泄漏。

运行结果

对于示例二叉树,度为 1 的节点是节点 2(仅有左子节点 4)和节点 5(仅有左子节点 7),因此输出:

度为1的节点数量:2

复杂度分析

  • 时间复杂度:(O(n)),其中n为二叉树节点总数,每个节点仅被访问一次。
  • 空间复杂度:(O(m)),其中m为二叉树中某一层的最大节点数(队列的最大存储量),最坏情况下为满二叉树的最后一层(约(n/2)个节点)。

3 计算二叉树的深度

cpp

要计算二叉树的深度,可以采用递归层次遍历的方法,以下分别给出两种实现:

方法一:递归法

思路:二叉树的深度 = max (左子树深度,右子树深度) + 1(根节点自身深度)。

  • 空树深度为 0;
  • 叶子节点深度为 1。
#include 
using namespace std;
// 二叉树节点结构
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int d) : data(d), left(nullptr), right(nullptr) {}
};
// 递归求二叉树深度
int getDepth(TreeNode* root) {
    if (root == nullptr) {
        return 0; // 空树深度为0
    }
    // 左子树深度
    int leftDepth = getDepth(root->left);
    // 右子树深度
    int rightDepth = getDepth(root->right);
    // 返回较大深度 + 1(根节点)
    return max(leftDepth, rightDepth) + 1;
}
// 测试示例
int main() {
    // 构建二叉树:
    //     1
    //    / \
    //   2   3
    //  / \
    // 4   5
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    cout << "二叉树深度:" << getDepth(root) << endl; // 输出3
    return 0;
}

方法二:层次遍历(迭代法)

思路:通过队列按层遍历二叉树,每遍历完一层,深度加 1。

#include 
#include 
using namespace std;
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int d) : data(d), left(nullptr), right(nullptr) {}
};
// 层次遍历求二叉树深度
int getDepth(TreeNode* root) {
    if (root == nullptr) {
        return 0;
    }
    queue q;
    q.push(root);
    int depth = 0;
    while (!q.empty()) {
        int levelSize = q.size(); // 当前层的节点数
        depth++; // 遍历完一层,深度加1
        for (int i = 0; i < levelSize; i++) {
            TreeNode* node = q.front();
            q.pop();
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
    }
    return depth;
}
// 测试示例(同递归法)
int main() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    cout << "二叉树深度:" << getDepth(root) << endl; // 输出3
    return 0;
}

复杂度分析

  • 递归法
    • 时间复杂度:(O(n))(每个节点仅访问一次)。
    • 空间复杂度:(O(h))(h为树的高度,递归栈的深度)。
  • 层次遍历法
    • 时间复杂度:(O(n))(每个节点入队、出队一次)。
    • 空间复杂度:(O(n))(队列最多存储一层的节点,最坏情况为满二叉树的最后一层)。

两种方法均可高效求解二叉树深度,递归法代码更简洁,层次遍历法更直观易理解。

c

方法一:递归法

思路:二叉树的深度 = 左右子树深度的最大值 + 1(根节点自身)。

  • 空树深度为 0;
  • 递归计算左、右子树深度,取最大值加 1 即为当前树的深度。
#include 
#include 
#include   // 用于max函数(需链接数学库,编译时加-lm)
// 二叉树节点结构
struct TreeNode {
    int data;
    struct TreeNode* left;
    struct TreeNode* right;
};
// 创建节点
struct TreeNode* createNode(int data) {
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    if (node == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}
// 递归求二叉树深度
int getDepth(struct TreeNode* root) {
    if (root == NULL) {
        return 0;  // 空树深度为0
    }
    // 计算左子树深度
    int leftDepth = getDepth(root->left);
    // 计算右子树深度
    int rightDepth = getDepth(root->right);
    // 返回较大深度 + 1(当前节点)
    return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
}
// 释放二叉树内存
void freeTree(struct TreeNode* root) {
    if (root == NULL) return;
    freeTree(root->left);
    freeTree(root->right);
    free(root);
}
int main() {
    // 构建示例二叉树:
    //     1
    //    / \
    //   2   3
    //  / \
    // 4   5
    struct TreeNode* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    printf("二叉树深度:%d\n", getDepth(root));  // 输出3
    freeTree(root);
    return 0;
}

方法二:层次遍历法(迭代法)

思路:通过队列按层遍历二叉树,每遍历完一层,深度加 1。

  • 队列存储当前层的所有节点;
  • 记录每层节点数量,遍历完该层后深度加 1,再将下一层节点入队。
#include 
#include 
// 二叉树节点结构
struct TreeNode {
    int data;
    struct TreeNode* left;
    struct TreeNode* right;
};
// 队列节点结构(用于层次遍历)
struct QueueNode {
    struct TreeNode* treeNode;
    struct QueueNode* next;
};
// 队列结构
struct Queue {
    struct QueueNode* front;  // 队头
    struct QueueNode* rear;   // 队尾
};
// 创建二叉树节点
struct TreeNode* createNode(int data) {
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    if (node == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    node->data = data;
    node->left = node->right = NULL;
    return node;
}
// 初始化队列
void initQueue(struct Queue* q) {
    q->front = q->rear = NULL;
}
// 入队操作
void enQueue(struct Queue* q, struct TreeNode* node) {
    struct QueueNode* newNode = (struct QueueNode*)malloc(sizeof(struct QueueNode));
    newNode->treeNode = node;
    newNode->next = NULL;
    if (q->rear == NULL) {  // 队列空时,队头队尾指向新节点
        q->front = q->rear = newNode;
    } else {
        q->rear->next = newNode;
        q->rear = newNode;
    }
}
// 出队操作
struct TreeNode* deQueue(struct Queue* q) {
    if (q->front == NULL) return NULL;  // 队列为空
    struct QueueNode* temp = q->front;
    struct TreeNode* node = temp->treeNode;
    q->front = q->front->next;
    if (q->front == NULL) {  // 队列为空时,队尾置空
        q->rear = NULL;
    }
    free(temp);
    return node;
}
// 层次遍历求二叉树深度
int getDepth(struct TreeNode* root) {
    if (root == NULL) return 0;  // 空树深度为0
    struct Queue q;
    initQueue(&q);
    enQueue(&q, root);
    int depth = 0;
    while (q.front != NULL) {
        int levelSize = 0;  // 当前层的节点数
        struct QueueNode* curr = q.front;
        // 统计当前层的节点数
        while (curr != NULL) {
            levelSize++;
            curr = curr->next;
        }
        // 遍历当前层所有节点,并将下一层节点入队
        for (int i = 0; i < levelSize; i++) {
            struct TreeNode* node = deQueue(&q);
            if (node->left != NULL) enQueue(&q, node->left);
            if (node->right != NULL) enQueue(&q, node->right);
        }
        depth++;  // 遍历完一层,深度加1
    }
    return depth;
}
// 释放二叉树内存
void freeTree(struct TreeNode* root) {
    if (root == NULL) return;
    freeTree(root->left);
    freeTree(root->right);
    free(root);
}
int main() {
    // 构建与递归法相同的示例二叉树
    struct TreeNode* root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    printf("二叉树深度:%d\n", getDepth(root));  // 输出3
    freeTree(root);
    return 0;
}

两种方法对比

方法时间复杂度空间复杂度特点
递归法\(O(n)\)\(O(h)\)(h 为树高)代码简洁,依赖递归栈
层次遍历法\(O(n)\)\(O(m)\)(m 为层最大节点数)非递归,需手动实现队列

说明

  • 递归法通过函数调用栈隐式处理节点访问顺序,代码更简洁,但对于深度极大的树可能导致栈溢出。
  • 层次遍历法通过显式队列控制节点访问,逻辑更直观,避免了递归栈溢出风险,适合大规模树。
  • 两种方法均需遍历所有节点,时间复杂度均为 \(O(n)\)(n 为节点总数)。

4 先序线索化

核心前提:先序遍历与线索化规则

  1. 先序遍历顺序:根节点 → 左子树 → 右子树(空树直接返回)。
  2. 线索化规则
    • 节点的左指针:若左子树为空,就指向它的「先序前驱」(遍历顺序中前一个节点);若左子树存在,正常指向左子节点。
    • 节点的右指针:若右子树为空,就指向它的「先序后继」(遍历顺序中后一个节点);若右子树存在,正常指向右子节点。
    • 只有整棵树的「最后一个遍历节点」,右指针没有后继,才会保留为空。

举例:左子树为空的二叉树

假设二叉树结构(左子树全空):

    A(根)
     \
      B
       \
        C(叶子)
  1. 先序遍历顺序:A → B → C(因为左子树都为空,直接走右子树)。
  2. 逐个分析节点的指针
    • 节点 A:左子树为空 → 左指针指向「先序前驱」(A 是第一个节点,无前驱,按规则左指针仍为空?不!线索化会强制利用空指针,此时 A 的左指针虽无前驱,但右子树存在(指向 B),所以右指针正常。
    • 节点 B:左子树为空 → 左指针指向「先序前驱」(A);右子树存在(指向 C),右指针正常。
    • 节点 C:左子树为空 → 左指针指向「先序前驱」(B);右子树为空 → 右指针指向「先序后继」(C 是最后一个节点,无后继),所以右指针保留为空。
  3. 空链域统计:只有节点 C 的右指针是空的,共 1 个。

结论

左子树为空的二叉树,先序线索化后,空链域个数为 1,对应选项 C。

5 判断二叉树类型

答案:C

解析

  • 先序遍历顺序是 “根 - 左 - 右”,后序遍历顺序是 “左 - 右 - 根”。
  • 若先序和后序序列正好相反,说明二叉树的结构是一条单链(每个节点只有一个子节点)。
  • 这种结构中,只有一个叶子节点(最底层的那个节点)。
  • 选项 A(均无左孩子)或 B(均无右孩子)只是单链的两种特殊情况,不能涵盖所有可能;选项 D(任意二叉树)显然不成立。

以 “全右链” 为例,结构如下:

  A
   \
    B
     \
      C
       \
        D(叶子节点)
  • 先序遍历:A → B → C → D
  • 后序遍历:D → C → B → A两者完全相反,且只有 D 一个叶子节点。

同理 “全左链” 结构,先序和后序也会完全相反,且只有最底层的节点是叶子。所以这种二叉树的核心特征是只有一个叶子节点,结构退化为单链(类似链表)。

6 树的存储形式

答案:D

解析

  • 选项 A(双亲表示法):通过记录每个节点的双亲节点来存储树,是树的常见存储形式。
  • 选项 B(孩子链表表示法):为每个节点维护一个孩子链表,存储其所有子节点,是树的存储形式。
  • 选项 C(孩子兄弟表示法):将树转换为二叉树的形式存储(左孩子表示第一个子节点,右兄弟表示下一个兄弟节点),可用于树的存储。
  • 选项 D(顺序存储表示法):树的结构不规则,难以用纯顺序存储准确表达节点间的层次和父子关系,不是树的常规存储形式。

7 后根遍历序列

答案:B

解析:树转换为二叉树时,采用 “孩子 - 兄弟” 表示法(左孩子为原树的第一个子节点,右兄弟为原树的下一个兄弟节点)。树的后根遍历顺序是 “先遍历所有子树,再访问根节点”;而该二叉树的中序遍历顺序与之完全一致,因此树的后根遍历序列等同于其对应二叉树的中序序列。

8 完全二叉树的深度