二叉树是我们在数据结构中学到的第一个非线性结构,是后续学习更为复杂的树、图结构的基础。本文整理了二叉树的概念定义、基本操作、遍历算法、伪代码与代码实现以及实例说明,方便大家随时查找对应。
一、定义与基本术语
二叉树是一种树形结构,每个节点最多有两个子节点,分别称为左节点和右节点。
基本术语:
- 根节点:树的顶部节点。
- 叶节点:没有子节点的节点。
- 父节点:某个节点的直接上级节点。
- 子节点:某个节点的直接下级节点。
- 兄弟节点:具有相同父节点的节点。
- 深度:从根节点到某个节点的边的数量。
- 高度:从某个节点到最远叶节点的边的数量。
A (Depth: 0, Root)
/
B (Depth: 1)
/ \
C1 C2(Depth: 2)
\
D (Depth: 3, Right)
\
E (Depth: 4, Right)
/ \
F1 F2(Depth: 4)
二、主要特点
- 递归性质:二叉树的许多操作可以通过递归实现。
- 动态集合:二叉树可以用来实现动态集合,支持插入、删除和查找操作。
- 层次结构:二叉树具有明确的层次结构,适合表示具有层次关系的数据。
三、基本操作
1. 构建二叉树:
① 构建节点:
struct TreeNode{int key;TreeNode* left;TreeNode* right;TreeNode(int k): key(k),left(nullptr),right(nullptr){}
};
② 插入节点:
插入操作会根据节点的值来决定插入的位置。例如,在二叉搜索树(BST)中,插入操作遵循以下规则:
- 如果当前节点的值大于插入值,则递归地插入到左子树。
- 如果当前节点的值小于插入值,则递归地插入到右子树。
- 如果当前节点的值等于插入值,则可以选择不插入或插入到左/右子树。
TreeNode* insert(TreeNode* node, int key){if(node == nullptr){return new TreeNode(key);}if(key < node->key){node->left = insert(node->left, key);}else if(key > node->key){node->right = insert(node->right,key);}return node;
}
③ 构建树:
从一个空树开始,通过多次插入节点,可以逐步构建二叉树。
TreeNode* root = nullptr;
// 如果是基于一个给定数组构建二叉树
for(int i = 0;i<length;i++){root = insert(root,nums[i]);
}
//手动输入构建
root = insert(root,10);
root = insert(root,4);
root = insert(root, 7);
root = insert(root, 20);
root = insert(root, 34);
root = insert(root, 16);
root = insert(root, 8);
2. 删除节点:
TreeNode* deleteNode(TreeNode* node, int key){if(node == nullptr) return node;if(key < node->key){node->left = deleteNode(node->left, key);} else if (key > node->key) {node->right = deleteNode(node->right, key);}else{if(node->left == nullptr){TreeNode* tmp = node->right;delete node;return tmp;}else if(node->right == nullptr){TreeNode* tmp = node->left;delete node;return tmp;}TreeNode* temp = findMin(node->right);node->key = temp->key;node->right = deleteNode(node->right, temp->key);}return node;
}TreeNode* findMin(TreeNode* node){while(node->left != nullptr){node = node->left;}return node;
}
3. 查找节点:
TreeNode* searchNode(TreeNode* node, int key){if(node == nullptr||node->key = key) return node;if(key < node->key){return searchNode(node->left,key);}if(key > node->key){return searchNode(node->right, key);}
}
四、遍历算法
需要了解:前序遍历、中序遍历、后序遍历、层序遍历
1. 前序遍历:
A
/ \
B C
/ \ \
D E F遍历顺序:A->B->D->E->C->F
1
/ \
2 3
/ \ \
4 5 6
/ \
7 8遍历顺序:1->2->4->7->8->5->3->6
前序遍历的访问顺序:根节点-->左子树-->右子树
// 以int整数型为例
vector<int> result
void preorder(TreeNode* node){if(node == nullptr){return;}// 如果题目要求的是前序遍历打印节点值,那么可以直接:// cout<<node->key<<" ";// 如果题目要求前序遍历将节点值存储到数组中,需要先定义一个vector数组(如↑),再进行存储result.push_back(node_key);preorder(node->left);preorder(nofe->right);
}
2. 中序遍历
A
/ \
B C
/ \ \
D E F遍历顺序:D->B->E->A->C->F
1
/ \
2 3
/ \ \
4 5 6
/ \
7 8遍历顺序:7->4->8->2->5->1->3->6
10
/ \
6 14
/ \ / \
4 8 12 16遍历顺序:4->6->8->10->12->14->16
中序遍历的访问顺序:左子树-->根节点-->右子树
vector<int> result;
void inorder(TreeNode* node) {if (node == nullptr) return;inorder(node->left);visit(node);inorder(node->right);
}void visit(TreeNode* node){// cout<< node->key <<" ";result.push_back(node->key);
}
3. 后序遍历
A
/ \
B C
/ \ \
D E F遍历顺序:D->E->B->F->C->A
1
/ \
2 3
/ \ \
4 5 6
/ \
7 8遍历顺序:7->8->4->5->2->6->3->1
后序遍历的访问顺序:左子树->右子树->根节点。
void postorder(TreeNode* node){if(node==nullptr) return;postorder(node->left);postorder(node->right);visit(node);
}
//visit函数要根据题目要求来定义,示例可参考中序遍历的定义
4. 层序遍历
A
/ \
B C
/ \ \
D E F遍历顺序:A->B->C->D->E->F
1
/ \
2 3
/ / \
4 5 6
/
7遍历顺序:1->2->3->4->5->6->7
层序遍历的顺序是:从上到下,从左到右。
层序遍历的结果特别好写,从上往下盯齐就没问题,但是代码是最长的。。
先来看看伪代码:
LevelOrder(root)
queue = new Queue()
queue.enqueue(root)
while not queue.isEmpty()
node = queue.dequeue()
visit(node)
if node.left != null
queue.enqueue(node.left)
if node.right != null
queue.enqueue(node.right)
void levelorder(TreeNode* root){if(root == nullptr) return;queue<TreeNode* > q; // 创建一个队列,用于存储待访问的节点q.push(root); // 把现在的根节点存进去while(!q.empty()){TreeNode* node = q.front(); // 取出队列的第一个节点q.pop(); // 将该节点从队列中移除visit(node);if (node->left != nullptr) q.push(node->left);if (node->right != nullptr) q.push(node->right); // 这里加入队列的顺序是先左再右,队列又是FIFO结构,遍历得到的顺序也就满足了先左再右}
}
五、适用场景举例
- 表达式树:用于表示和计算算术表达式。
- 二叉搜索树(BST):用于实现动态集合,支持高效的插入、删除和查找操作。
- 哈夫曼树:用于数据压缩。
- 堆:用于实现优先队列。
- 线索二叉树:用于高效地遍历二叉树。
六、Leetcode 二叉树 练习题汇总
1. 初级题目
题号 | 题目名称 | 知识点 |
---|---|---|
94 | 二叉树的中序遍历 | 中序遍历,递归与迭代 |
144 | 二叉树的前序遍历 | 前序遍历,递归与迭代 |
145 | 二叉树的后序遍历 | 后序遍历,递归与迭代 |
102 | 二叉树的层序遍历 | 层序遍历,队列的应用 |
226 | 翻转二叉树 | 递归,树的翻转 |
617 | 合并二叉树 | 递归,树的合并 |
589 | N叉树的前序遍历 | N叉树,前序遍历 |
897 | 递增顺序搜索树 | 二叉搜索树,中序遍历的应用 |
2. 中级题目
题号 | 题目名称 | 知识点 |
---|---|---|
98 | 验证二叉搜索树 | 二叉搜索树的性质,递归 |
113 | 路径总和 II | 深度优先搜索,路径求和 |
257 | 二叉树的所有路径 | 深度优先搜索,路径记录 |
114 | 二叉树展开为链表 | 递归,树的展开 |
116 | 填充每个节点的下一个右侧节点指针 | 层序遍历,队列的应用 |
104 | 二叉树的最大深度 | 深度优先搜索或层序遍历 |
543 | 二叉树的直径 | 深度优先搜索,树的直径 |
236 | 二叉树的最近公共祖先 | 递归,最近公共祖先 |
希望对你有帮助!