目录
概念及结构
概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树有两种主要的形式:满二叉树和完全二叉树。
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
节点的度:一个节点含有的子树的个数称为该节点的度
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树
完全二叉树
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
二叉树的存储方式
有链式存储也有顺序存储;
链式存储如下
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
};
顺序存储如下
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
堆:大根堆和小跟堆
堆的性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
二叉树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
};
遍历
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
- 深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
前后中遍历看的是中间遍历的顺序
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
递归遍历
递归遍历要注意三要素:确定递归函数的参数和返回值
确定终止条件
确定单层递归的逻辑
前序遍历(递归)
/*前序遍历*/
void PreOrder(BiTree T)
{
if (T == NULL) return;
printf("树结点的值:%c\n", T->data); // 访问结点
PreOrder(T->lchild); // 遍历结点左子树
PreOrder(T->rchild); // 遍历结点右子树
}
递归可以用栈来实现,访问结点并入栈遍历左子树,结点出栈遍历右子树。
(非递归)
算法思路:
1、二叉树为空啥也不做;
2、结点不为空,访问并入栈,接着遍历其左子树;
3、结点为空但栈不为空,栈顶元素出栈,遍历栈顶元素的右子树;
4、结点为空并且栈为空结束遍历。
/*前序遍历*/
void PreOrder2(BiTree T)
{
Stack<BiTree>a; // 申请一个辅助栈
BiTree p = T; // p为遍历指针
while (p || !empty(a)) // 栈不为空或p不为空时循环
{
if (p!==NULL) // 一路向左直到尽头
{
visit(p); // 访问当前节点,并入栈
a.Push(p)
p = p->lchild; // 左孩子不空,一直向左走
}
else //出栈,并转向出栈结点的右子树
{
Pop(); // 栈顶元素出栈
p = p->rchild; // 向右子树走,p赋值为当前结点的右孩子
} // 返回while循环继续进入if-else语句
}
}
中序遍历(递归)
/*中序遍历*/
void InOrder(BiTree T)
{
if (T == NULL) return ;
InOrder(T->lchild); // 遍历结点左子树
printf("树结点的值:%c\n", T->data); // 访问结点
InOrder(T->rchild); // 遍历结点右子树
}
非递归
/*中序遍历*/
void InOrder2(BiTree T)
{
Stack<BiTree>a; // 申请一个辅助栈
BiTree p = T; // p为遍历指针
while (p || !Empty(S)) // 栈不空或p不空时循环
{
if (p) // 一路向左
{
a.Push(p); // 当前结点入栈
p = p->lchild; // 左孩子不空,一直向左走
}
else // 出栈,并转向出栈结点的右子树
{
a.Pop(); // 栈顶元素出栈
visit(p); // 访问出栈结点
p = p->rchild; // 向右子树走,p赋值为当前结点的右孩子
} // 返回while循环继续进入if-else语句
}
}
后序遍历(递归)
/*后序遍历*/
void PostOrder(BiTree T)
{
if (T == NULL) return;
PostOrder(T->lchild); // 遍历结点左子树
PostOrder(T->rchild); // 遍历结点右子树
printf("树结点的值:%c\n", T->data); // 访问结点
}
非递归
层序遍历
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树
可以用队列来解决 相当于广搜
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;//二维数组result
while (!que.empty()) {
int size = que.size();//size大小是每一层结点
vector<int> vec;//一维数组 记录每一层的结点
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
while(size--) {
TreeNode* node = que.front();//记录这个点是为了找它的子孩子
que.pop();
vec.push_back(node->val);//这一层的结点被收集进这一个一维数组
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);//将每一个一维数组收集起来合成一个二维数组
}
return result;
}
层序遍历题目
最右边的节点就是层序遍历时每一层最后一个数 只要在写时判断一下这个节点是不是这层最后一个节点就行 ,若是,则收集起来
vector<int> rightSideView(TreeNode* root) //返回为一维数组
{
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<int> result;//result为一个一维数组
while (!que.empty())
{
int size = que.size();//size为每一层的节点个数
while(size--) {
TreeNode* node = que.front();
que.pop();
if (i == size ) result.push_back(node->val); // 将每一层的最后元素放入result数组中
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
n叉树的层序遍历与二叉树的层序遍历差不多,只是在找子孩子结点时有所不同
二叉树
结构体定义时每一个节点设置两个指针(left right)
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
n叉树
结构体定义时每一个节点设置多个指针(此时可以用数组来表示)
struct TreeNode {
int val;
TreeNode *children[100];
};
for (int i = 0; i < node->children.size(); i++) { // 将节点孩子加入队列
if (node->children[i]) que.push(node->children[i]);
}
求最大深度也可以用层序遍历 在求每一层结点个数(size)后面加一个depth++;
而求最小深度时看的是当一层层遍历时,遇到的第一个叶节点(左右孩子都为空时)的层数就是最小深度
int minDepth(TreeNode* root) //看返回值
{
if (root == NULL) return 0;
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
int size = que.size();
depth++; // 记录最小深度
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
return depth;//此时为最小深度
}
}
}
return depth;
}