数据结构 10 二叉树作业
1 实现二叉树所有节点左右子树交换
#include <stdio.h>
#include <stdlib.h>// 二叉树节点结构定义
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;
}
代码说明:
- 节点结构:使用
struct TreeNode定义二叉树节点,包含数据域和左右子树指针。 - 创建节点:
createNode函数负责动态分配节点内存并初始化。 - 交换逻辑:
swapLeftRight函数通过递归实现:- 先交换当前节点的左右子树
- 再递归交换左子树和右子树
- 验证方式:通过前序遍历输出交换前后的节点序列,直观展示交换效果。
- 内存管理:
freeTree函数递归释放所有节点内存,避免内存泄漏。
运行结果:
对于示例树:
1/ \2 3/ \
4 5
输出为:
交换前前序遍历:1 2 4 5 3
交换后前序遍历:1 3 2 5 4
时间复杂度为O(n)(每个节点处理一次),空间复杂度为O(h)(h为树的高度,递归栈深度)。
2 使用队列来实现二叉树的层次遍历,同时统计度为 1 的结点数目
算法思路
- 层次遍历:借助队列,从根节点开始,依次将节点入队,然后出队并处理,同时将其左右子节点入队(若存在)。
- 统计度为 1 的结点:对于每个出队的节点,判断其左、右子节点的存在情况:
- 若只有左子节点或只有右子节点,则该节点的度为 1,计数加 1。
C 语言实现代码
#include <stdio.h>
#include <stdlib.h>// 二叉树结点结构
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);// 判断结点的度是否为1if ((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\))。
算法思路
- 层次遍历:利用队列(
std::queue)实现二叉树的层次遍历,按层访问每个节点。 - 统计度为 1 的节点:对每个节点,判断其左、右子节点的存在情况:
- 若仅存在左子节点或仅存在右子节点,则该节点的度为 1,计数加 1。
C++ 代码实现
#include <iostream>
#include <queue> // 用于队列操作
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<TreeNode*> 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// /// 7TreeNode* 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;
}
代码说明
- 节点结构:
TreeNode包含值val和左右子节点指针left、right,构造函数初始化节点。 - 层次遍历:使用
std::queue存储节点,通过push(入队)和pop(出队)操作实现按层访问。 - 统计逻辑:对每个节点,通过判断
left和right是否存在(异或逻辑),确定其度是否为 1,若满足则计数加 1。 - 内存管理:
freeTree函数递归释放所有节点内存,避免内存泄漏。
运行结果
对于示例二叉树,度为 1 的节点是节点 2(仅有左子节点 4)和节点 5(仅有左子节点 7),因此输出:
度为1的节点数量:2
复杂度分析
- 时间复杂度:(O(n)),其中n为二叉树节点总数,每个节点仅被访问一次。
- 空间复杂度:(O(m)),其中m为二叉树中某一层的最大节点数(队列的最大存储量),最坏情况下为满二叉树的最后一层(约(n/2)个节点)。
3 计算二叉树的深度
cpp
要计算二叉树的深度,可以采用递归或层次遍历的方法,以下分别给出两种实现:
方法一:递归法
思路:二叉树的深度 = max (左子树深度,右子树深度) + 1(根节点自身深度)。
- 空树深度为 0;
- 叶子节点深度为 1。
#include <iostream>
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 5TreeNode* 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; // 输出3return 0;
}
方法二:层次遍历(迭代法)
思路:通过队列按层遍历二叉树,每遍历完一层,深度加 1。
#include <iostream>
#include <queue>
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<TreeNode*> q;q.push(root);int depth = 0;while (!q.empty()) {int levelSize = q.size(); // 当前层的节点数depth++; // 遍历完一层,深度加1for (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; // 输出3return 0;
}
复杂度分析
- 递归法:
- 时间复杂度:(O(n))(每个节点仅访问一次)。
- 空间复杂度:(O(h))(h为树的高度,递归栈的深度)。
- 层次遍历法:
- 时间复杂度:(O(n))(每个节点入队、出队一次)。
- 空间复杂度:(O(n))(队列最多存储一层的节点,最坏情况为满二叉树的最后一层)。
两种方法均可高效求解二叉树深度,递归法代码更简洁,层次遍历法更直观易理解。
c
方法一:递归法
思路:二叉树的深度 = 左右子树深度的最大值 + 1(根节点自身)。
- 空树深度为 0;
- 递归计算左、右子树深度,取最大值加 1 即为当前树的深度。
#include <stdio.h>
#include <stdlib.h>
#include <math.h> // 用于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 5struct 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)); // 输出3freeTree(root);return 0;
}
方法二:层次遍历法(迭代法)
思路:通过队列按层遍历二叉树,每遍历完一层,深度加 1。
- 队列存储当前层的所有节点;
- 记录每层节点数量,遍历完该层后深度加 1,再将下一层节点入队。
#include <stdio.h>
#include <stdlib.h>// 二叉树节点结构
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; // 空树深度为0struct 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)); // 输出3freeTree(root);return 0;
}
两种方法对比
| 方法 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| 递归法 | \(O(n)\) | \(O(h)\)(h 为树高) | 代码简洁,依赖递归栈 |
| 层次遍历法 | \(O(n)\) | \(O(m)\)(m 为层最大节点数) | 非递归,需手动实现队列 |
说明
- 递归法通过函数调用栈隐式处理节点访问顺序,代码更简洁,但对于深度极大的树可能导致栈溢出。
- 层次遍历法通过显式队列控制节点访问,逻辑更直观,避免了递归栈溢出风险,适合大规模树。
- 两种方法均需遍历所有节点,时间复杂度均为 \(O(n)\)(n 为节点总数)。
4 先序线索化

核心前提:先序遍历与线索化规则
- 先序遍历顺序:根节点 → 左子树 → 右子树(空树直接返回)。
- 线索化规则:
- 节点的左指针:若左子树为空,就指向它的「先序前驱」(遍历顺序中前一个节点);若左子树存在,正常指向左子节点。
- 节点的右指针:若右子树为空,就指向它的「先序后继」(遍历顺序中后一个节点);若右子树存在,正常指向右子节点。
- 只有整棵树的「最后一个遍历节点」,右指针没有后继,才会保留为空。
举例:左子树为空的二叉树
假设二叉树结构(左子树全空):
A(根)\B\C(叶子)
- 先序遍历顺序:A → B → C(因为左子树都为空,直接走右子树)。
- 逐个分析节点的指针:
- 节点 A:左子树为空 → 左指针指向「先序前驱」(A 是第一个节点,无前驱,按规则左指针仍为空?不!线索化会强制利用空指针,此时 A 的左指针虽无前驱,但右子树存在(指向 B),所以右指针正常。
- 节点 B:左子树为空 → 左指针指向「先序前驱」(A);右子树存在(指向 C),右指针正常。
- 节点 C:左子树为空 → 左指针指向「先序前驱」(B);右子树为空 → 右指针指向「先序后继」(C 是最后一个节点,无后继),所以右指针保留为空。
- 空链域统计:只有节点 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 完全二叉树的深度
![]()
