💙系列文章💙
【初阶数据结构与算法】第一篇:算法中的时间复杂度和空间复杂度
【初阶数据结构与算法】第六篇:栈和队列(各个功能实现+练习题包含多种方法)
【初阶数据结构与算法】第七篇:二叉树和堆的基本概念+以及堆的实现
【初阶数据结构与算法】第八篇——二叉树的顺序结构的应用(堆排序+TOPK问题)
目录
前言
🌏一、二叉树链式结构
🍯1.选择原因
⭐️普通二插树没有增删查改的价值,单纯的为了存储数据不如线性表
⭐️链式二叉树可以更好控制结构
🌏二、二叉树简单创建
🍯链式结构
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* right;
struct BinaryTreeNode* left;
}BTNode;
🍯创建二叉树,直接暴力创建
BTNode* CreatBinaryTree()
{
BTNode* treeNode1 = (BTNode*)malloc(sizeof(BTNode));
BTNode* treeNode2 = (BTNode*)malloc(sizeof(BTNode));
BTNode* treeNode3 = (BTNode*)malloc(sizeof(BTNode));
BTNode* treeNode4 = (BTNode*)malloc(sizeof(BTNode));
BTNode* treeNode5 = (BTNode*)malloc(sizeof(BTNode));
BTNode* treeNode6 = (BTNode*)malloc(sizeof(BTNode));
treeNode1->data = 'A';
treeNode2->data = 'B';
treeNode3->data = 'C';
treeNode4->data = 'D';
treeNode5->data = 'E';
treeNode6->data = 'F';
treeNode1->left = treeNode2;
treeNode1->right = treeNode3;
treeNode2->left = treeNode4;
treeNode2->right = treeNode5;
treeNode3->left = treeNode6;
treeNode3->right = NULL;
treeNode4->left = treeNode4->right = NULL;
treeNode5->left = treeNode5->right = NULL;
treeNode6->left = treeNode6->right = NULL;
return treeNode1;
}
🌏三、二叉树遍历
⭐️二叉树的遍历分为两类,一类是深度优先遍历,一类是广度优先遍历。
深度优先遍历:前序、中序和后序遍历,但是因为前序遍历是最先访问根在往深处走,所以是最符合深度优先的
广度优先遍历:层序遍历
🍯1.前序遍历
⭐️前序遍历指的是先遍历根,再遍历左子树,再遍历右子树。
⭐️思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历根,再遍历左子树,左子树又可以分解为,根、左子树和右子树,直到把所以左子树的部分遍历完,然后就遍历右子树,右子树又可以分解为,根、左子树和右子树。
void PreOrder(BTNode* root) {
// 遍历到NULL就返回
if (root == NULL) {
printf("NULL ");
return;
}
// 先遍历根
printf("%c ", root->data);
//再遍历左子树
PreOrder(root->left);
//再遍历右子树
PreOrder(root->right);
}
🍯2.中序遍历
⭐️中序遍历指的是先遍历左子树,再遍历根,再遍历右子树。
⭐️思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历左子树,左子树又可以分解为,左子树、根和右子树,直到把所以左子树的部分遍历完,然后就遍历根,再遍历右子树,右子树又可以分解为,左子树、根和右子树。
void PreOrder(BTNode* root) {
// 遍历到NULL就返回
if (root == NULL) {
printf("NULL ");
return;
}
//遍历左子树
PreOrder(root->left);
// 遍历根
printf("%c ", root->data);
//遍历右子树
PreOrder(root->right);
}
🍯3.后序遍历
⭐️后序遍历指的是先遍历左子树,再遍历右子树,再遍历根树。
⭐️思想: 二叉树本身就是一种递归结构,所以通过递归来遍历这棵树,如何递归遍历呢?
是这样的,先遍历左子树,左子树又可以分解为,左子树、右子树和根,直到把所以左子树的部分遍历完,然后遍历右子树,右子树又可以分解为,左子树、右子树和根,最后遍历根。
void PreOrder(BTNode* root) {
// 遍历到NULL就返回
if (root == NULL) {
printf("NULL ");
return;
}
//遍历左子树
PreOrder(root->left);
//遍历右子树
PreOrder(root->right);
// 遍历根
printf("%c ", root->data);
}
🍯4.层序遍历
⭐️层序遍历是设置root为第一层,然后从第一层开始,从左往右一层一层向下遍历
层序遍历通常用队列解决,首先将不为空的root放入到队列中,然后依次将队头取出,同时将root的左右不为空的子树存放到队头中,直到队列为空停止
下面两段分别是C++和C语言
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
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;
}
//前置声明,
typedef struct BinaryTreeNode* QDataType;
//队列
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
//size_t size;
}Queue;
//树
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
int data;
}BTNode;
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
assert(pq->head == NULL);
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head && pq->tail);
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
//return pq->head == NULL && pq->tail == NULL;
return pq->head == NULL;
}
// 层序遍历-C语言
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
//如果root不是空,就把root放到队列中
if (root)
{
QueuePush(&q, root);
}
//队列不为空,把队头数据出来
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
//将队列存的指针拿掉了,指针存的节点给了front
QueuePop(&q);
printf("%d ", front->data);
//将root的左子树和右子树都放到队列中
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
printf("\n");
QueueDestory(&q);
}
🌏四、二叉树基本操作
🍯二叉树的节点个数
🍍方式一:全局遍历
int size = 0;//这里不妨思考下能不能直接放入BinaryTreeSize函数内部进行定义
int BinaryTreeSize(BTNode* root)
{
if (root == NULL){
return;
}else{
++size;
}
BinaryTreeSize(root->left);
BinaryTreeSize(root->right);
}
🍍方式二:局部遍历
void BinaryTreeSize(BTNode* root, int* psize)
{
if (root == NULL)
return;
else
++(*psize);
BinaryTreeSize(root->left, psize);
BinaryTreeSize(root->right, psize);
}
🍍方式三:递归分治
⭐️空树,最小规模子问题,节点数返回0
⭐️非空,左子树节点数+右子树节点数+1(自己)
//3、分治,不再遍历
int BinaryTreeSize(BTNode* root)
{
return root == NULL ? 0 : 1
+ BinaryTreeSize(root->left)
+ BinaryTreeSize(root->right);
}
🍯二叉树的叶子节点个数
⭐️分解为求左子树叶子节点个数+右子树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
//第K层
if (root->left == NULL && root->right == NULL)
return 1;
//既不是空也不是第K层
return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
🍯二叉树第k层节点个数
⭐️求解左子树的第k-1层节点个数+右子树的第k-1层节点个数,当k==1时,就可以直接返回1,节点为空就返回0。
下面如果K==3,那么就是求A的第三层,B的第二层,D的第一层(直到为K为1时候,则找到所求层数,计数即可)
int BinaryTreeLevelKSize(BTNode* root, int K)
{
//检查K合法性
assert(k >= 1);
if (root == NULL)
{
return 0;
}
if (K == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, K- 1) + BinaryTreeLevelKSize(root->right, K - 1);
}
🍯二叉树查找值为x的节点
⭐️前序遍历一遍二叉树,先判断当前结点是否是目标,然后查找左子树,再查找右子树.
如果左右子树都没有找到,就返回
NULL
,直到找到x就返回。
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
if (root == NULL) {
return NULL;
}
if (root->data == x) {
return root;
}
BTNode* left = BinaryTreeFind(root->left, x);
if (left) {
return left;
}
BTNode* right = BinaryTreeFind(root->right, x);
if (right) {
return right;
}
return NULL;
}
🍯求二叉树的深度/高度
⭐️二叉树的高度等于1 + 左右子树高度的最大值.
int getdepth(treenode* node) {
if (node == NULL) return 0;
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
}
int maxdepth(treenode* root) {
return getdepth(root);
}
🍯二叉树的销毁
⭐️直接依次分治递归销毁即可,这里置空root没有用,是临时变量,真正的root指针没有没有置空
void BinaryTreeDestory(BTNode* root) {
if (root == NULL) {
return ;
}
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}
🍯完全二叉树判断
⭐️首先创建一个队列,利用层序遍历,将每一层都遍历一遍,如果,遇到空了,则结束循环,之后再遍历判断如果不是空,则不是完全二叉树。
如果说上一层都从队列中出来了,那么下一层会全部进入队列,哪怕下一层都是空或者只有一个在最左边的节点或者最右边的节点
记得销毁队列,防止内存泄漏
// 完全二叉树判断
bool Completely_binary_tree(BTNode* root)
{
Queue q;
QueueInit(&q);
//如果root不是空,就把root放到队列中
if (root)
{
QueuePush(&q, root);
}
//队列不为空,把队头数据出来
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
//将队列存的指针拿掉了,指针存的节点给了front
QueuePop(&q);
//遇到空则结束循环
if (front == NULL) {
break;
}
//将root的左子树和右子树都放到队列中
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
//将队列存的指针拿掉了,指针存的节点给了front
QueuePop(&q);
//后面遇到空则说明不是完全二叉树
if (front) {
QueueDestory(&q);
return false;
}
}
QueueDestory(&q);
return true;
}
🌏五,二叉树基本练习题
🍯单值二叉树(传送门)
⭐️如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回
true
;否则返回false
。
输入:[1,1,1,1,1,null,1] 输出:true
🔑思路:
当root为空时,说明此条分支遍历完了,直接返回true,如果root和left和right值不相等,返回false,同时判断left和right是不是为空,之后分治遍历左子树和右边子树
bool isUnivalTree(struct TreeNode* root){
//为空不违反规则
if(root == NULL){
return true;
//比较左边
}else if(root->left && root->left->val != root->val){
return false;
//比较右边
}else if(root->right && root->right->val != root->val){
return false;
}else{
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
}
🍯相同的树(传送门)
⭐️给你两棵二叉树的根节点
p
和q
,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
输入:p = [1,2,3], q = [1,2,3] 输出:true
🔑思路:当两个树都为空时,返回true,当两个树一个为空一个不为空时,返回false,当两个树都不为空时,如果两个树值不相等返回false,如果相等,则继续分治遍历左子树和右子树
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//两个都为空
if(p == NULL && q == NULL){
return true;
}
//一个为空
if(p == NULL || q == NULL){
return false;
}
//都不为空
if(p->val != q->val){
return false;
}
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
🍯对称二叉树(传送门)
⭐️给你一个二叉树的根节点
root
, 检查它是否轴对称。
输入:root = [1,2,2,3,4,4,3] 输出:true
🔑思路:
首先判断根是不是为空,然后将左子树和右子树一分为二比较,判断是否镜像相同,判断相同可参照上一题
bool compare(TreeNode* left, TreeNode* right) {
// 首先排除空节点的情况
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
// 排除了空节点,再排除数值不相同的情况
else if (left->val != right->val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
🍯二叉树的前序遍历(传送门)
⭐️给你二叉树的根节点
root
,返回它节点值的 前序 遍历。
输入:root = [1,null,2,3] 输出:[1,2,3]
🔑思路:
首先计算树的节点个数,然后开辟相应的内存,之后将root和内存数组和数组下标地址单独传过去用函数分治判断,之后就是前置分治了
int TreeSize(struct TreeNode* root){
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//要传送i的地址,不然栈帧销毁的的时候i也就丢失了
void _preorderTraversal(struct TreeNode* root, int* arr, int* i){
//这里判空了,主函数就不用判空了
if(root == NULL){
return ;
}
arr[(*i)++] = root->val;
_preorderTraversal(root->left, arr, i);
_preorderTraversal(root->right, arr, i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
//为空直接返回即可
// if(root == NULL){
// return NULL;
// }
//计算出数的长度
int len = TreeSize(root);
*returnSize = len;
int* arr = (int*)malloc(sizeof(int)*len);
int i = 0;
//数组计算每一个节点
_preorderTraversal(root, arr, &i);
return arr;
}
🍯二叉树的中序遍历(传送门)
⭐️给你二叉树的根节点
root
,返回它节点值的 中序 遍历。
输入:root = [1,null,2,3] 输出:[1,3,2]
🔑思路:
和前序思路基本一样
int TreeSize(struct TreeNode* root){
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//要传送i的地址,不然栈帧销毁的的时候i也就丢失了
void _inorderTraversal(struct TreeNode* root, int* arr, int* i){
//这里判空了,主函数就不用判空了
if(root == NULL){
return ;
}
_inorderTraversal(root->left, arr, i);
arr[(*i)++] = root->val;
_inorderTraversal(root->right, arr, i);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
//为空直接返回即可
// if(root == NULL){
// return NULL;
// }
//计算出数的长度
int len = TreeSize(root);
*returnSize = len;
int* arr = (int*)malloc(sizeof(int)*len);
int i = 0;
//数组计算每一个节点
_inorderTraversal(root, arr, &i);
return arr;
}
🍯二叉树的后续遍历(传送门)
⭐️给你二叉树的根节点
root
,返回它节点值的 后序 遍历。
输入:root = [1,null,2,3] 输出:[3,2,1]
🔑思路:
和前序思路基本一样
int TreeSize(struct TreeNode* root){
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//要传送i的地址,不然栈帧销毁的的时候i也就丢失了
void _postorderTraversal(struct TreeNode* root, int* arr, int* i){
//这里判空了,主函数就不用判空了
if(root == NULL){
return ;
}
_postorderTraversal(root->left, arr, i);
_postorderTraversal(root->right, arr, i);
arr[(*i)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
//为空直接返回即可
// if(root == NULL){
// return NULL;
// }
//计算出数的长度
int len = TreeSize(root);
*returnSize = len;
int* arr = (int*)malloc(sizeof(int)*len);
int i = 0;
//数组计算每一个节点
_postorderTraversal(root, arr, &i);
return arr;
}
🍯另一颗树的子树(传送门)
⭐️给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
输入:root = [3,4,5,1,2], subRoot = [4,1,2] 输出:true
🔑思路:
首先判断头是不是为空,然后将主树和子树传入比较相同的树中,然后依次分治递归主树左子树和右子树
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
//两个都为空
if(p == NULL && q == NULL){
return true;
}
//一个为空,一个不为空
if(p == NULL || q == NULL){
return false;
}
//都不为空
if(p->val != q->val){
return false;
}
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root == NULL){
return false;
}
//相等直接返回true,否则左右子树开始递归
return isSameTree(root, subRoot)
|| isSubtree(root->left, subRoot)
|| isSubtree(root->right, subRoot);
}
🍯二叉树遍历(传送门)
⭐️编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
输入:
abc##de#g##f###输出:
c b e g d f a
🔑思路:
首先创建一个数组a然后将数组a中所有元素按照前序遍历的思想创建一个树,之后按照中序遍历的思想打印出来
#include<stdio.h>
#include<stdlib.h>
//创建一个节点
struct TreeNode
{
char val;
struct TreeNode* left;
struct TreeNode* right;
};
//用前序遍历创建一个树
struct TreeNode* CreatTree(char* str,int* pi)
{
//遇到#就返回空
if(str[*pi]=='#')
{
(*pi)++;
return NULL;
}
//创建节点
struct TreeNode*root=(struct TreeNode*)
malloc(sizeof(struct TreeNode));
//将数组中的值给节点
root->val=str[(*pi)++];
//前序遍历
root->left=CreatTree(str, pi);
root->right=CreatTree(str, pi);
return root;
}
//中序遍历
void InOrder(struct TreeNode* root)
{
if(root==NULL)
{
return;
}
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
int main()
{
char str[100];
scanf("%s",str);
int i=0;
struct TreeNode* root=CreatTree(str,&i);
InOrder(root);
}
🍯 翻转二叉树(传送门)
⭐️
🔑思路:
翻转每一个根的左右子树
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right); // 中
invertTree(root->left); // 左
invertTree(root->right); // 右
return root;
}
🍯已知前中后序遍历其中两种,求剩下一种
⭐️已知某二叉树的前序遍历序列为5 7 4 9 6 2 1,中序遍历序列为4 7 5 6 9 1 2,则其后序遍历序列为
4 7 6 1 2 9 5
🔑思路:
首先根据前序遍历,得到root是5,然后根据中序遍历7在5的左边,说明是5的左子树,然后根据中序遍历4在5的左边同时在7的左边,所以是7的左子树,然后根据中序遍历9在5的左边,是右子树,然后6在五的左边但是在9的右边所以是9的左子树,然后根据中序遍历2在9的右边所以是9的右子树,然后根据中序遍历1在2的左边9的右边所以是2的左子树
⭐️已知某二叉树的中序遍历序列为JGDHKBAELIMCF,后序遍历序列为JGKHDBLMIEFCA,则其前序遍历序列为
ABDGJHKCEILMF
🔑思路:
首先根据后序遍历得到A是root,然后根据中序遍历C在A的右边所以是A右子树,根据中序遍历F在C的右边所以是C的右子树,根据中序遍历E在A的右边在C的前面,所以是C的左子树,根据中序遍历I在E的右边所以是E的右子树,M在I的右边,所以是I的右子树,L在I的左边E的右边,所以是I的左子树,B在A的左边所以是A的左子树,D在A的左边B的左边,所以是B的左子树,H在B的左边B的右边,所以是D的右子树,K在B的左边H的右边,所以是H的右子树,G在D的左边,所以是D的左子树,J在G的左边所以是G的左子树
🍯非递归中序
举例用非递归的方法实现中序遍历(左中右),那么非递归的顺序是右中左
vector<int> order (TreeNode* root){
vector<int> res;
stack<TreeeNode*>st;
if(root!= NUUL)st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
if(node != NULL){
//右
st.pop();
if(node->right)st.push(node->right);
//中
st.push(node);
st.push(NULL);
//左
if(node->left)st.push(node->left);
}else{
st.pop();
node = st.top();
st.pop();
res.push_back(node->val);
}
}
return res;
}
🍯从中序与后序遍历序列构造二叉树
来看一下一共分几步:
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
class Solution {
private:
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return NULL;
// 后序遍历数组最后一个元素,就是当前的中间节点
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
// 叶子节点
if (postorder.size() == 1) return root;
// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左闭右开区间:[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
// [delimiterIndex + 1, end)
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );
// postorder 舍弃末尾元素
postorder.resize(postorder.size() - 1);
// 切割后序数组
// 依然左闭右开,注意这里使用了左中序数组大小作为切割点
// [0, leftInorder.size)
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
// [leftInorder.size(), end)
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
🍯路径总和
class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
if (cur->left) { // 左
count -= cur->left->val; // 递归,处理节点;
if (traversal(cur->left, count)) return true;
count += cur->left->val; // 回溯,撤销处理结果
}
if (cur->right) { // 右
count -= cur->right->val; // 递归,处理节点;
if (traversal(cur->right, count)) return true;
count += cur->right->val; // 回溯,撤销处理结果
}
return false;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
return traversal(root, sum - root->val);
}
};
🍯路径总和ii
class solution {
private:
vector<vector<int>> result;
vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
result.push_back(path);
return;
}
if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回
if (cur->left) { // 左 (空节点不遍历)
path.push_back(cur->left->val);
count -= cur->left->val;
traversal(cur->left, count); // 递归
count += cur->left->val; // 回溯
path.pop_back(); // 回溯
}
if (cur->right) { // 右 (空节点不遍历)
path.push_back(cur->right->val);
count -= cur->right->val;
traversal(cur->right, count); // 递归
count += cur->right->val; // 回溯
path.pop_back(); // 回溯
}
return ;
}
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val);
return result;
}
};
🍯数组建立二叉树(递归和非递归)
非递归建立二叉树,遇到#表示空
TreeNode* creat(int* a, int n) {
if (n == 0 || a[i] == '#')return NULL;
Treenode* res;
for (int i = 0; i < n; ++i) {
//遇到空跳过
if (a[i] == '#')continue;
Treenode* root = new Treenode;
if (i == 0)res = root;
root->data = a[i];
if (i * 2 + 1 < n) {
if (a[i * 2 + 1] = '#') {
Treenode* left = new Treenode;
left->data = a[i * 2 + 1];
root->left = left;
}
}
else
root->left = NULL;
if (i * 2 + 2 < n) {
if (a[i * 2 + 2] != '#') {
Treenode* right = new Treenode;
right->data = a[i * 2 + 2];
root->right = right;
}
}
else
root->right = NULL;
}
}
递归建立二叉树,遇到#表示空
TreeNode* creat(int* a, int i,int n) {
if (i >= n) return NULL;
else {
Treenode* root = new Treenode;
root->data = a[i];
root->left = creat(a, 2 * i + 1, n);
root->right = creat(a, 2 * i + 2, n);
return root;
}
}
🍯最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
}
};
🌏总结
二叉树初阶总算是学完了,中间递归思想和其他分治之类的都值得反复咀嚼。