【链式二叉树】数据结构链式二叉树的(万字详解)
前言:
在上一篇博客中,我们已经详解学习了堆的基本知识,今天带大家进入的是二叉树的另外一种存储方式----“链式二叉树”的学习,主要用到的就是“递归思想”!!
本文目录
- 1.链式二叉树的实现
- 1.1前置说明
- 1.2结构体以及声明
- 2.遍历二叉树
- 2.1算法描述
- 2.2先序遍历
- 2.3中序遍历
- 2.4后序遍历
- 2.5层序遍历
- 2.6算法分析
- 3.接口功能的实现
- 3.1二叉树节点个数
- 3.2二叉树叶子节点个数
- 3.3二叉树第k层节点个数
- 3.4二叉树查找值为x的节点
- 3.5二叉树的高度
- 3.6二叉树的销毁
- 3.7判断是否为完全二叉树
- 4.选择题练习
- 5.OJ题练习
- 5.1 单值二叉树(LeetCode 965题)
- 5.2检查两颗树是否相同(LeetCode 100题)
- 5.3 对称二叉树(LeetCode 101题)
- 5.4另一颗树的子树(LeetCode 572题)
- 5.5二叉树的前序遍历(LeetCode 144题)
- 5.6 二叉树中序遍历(LeetCode 94题)
- 5.7二叉树的后序遍历(LeetCode 145题)
- 6.总结
前情回顾:
再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:
- 空树
- 非空:根节点,根节点的左子树、根节点的右子树组成的。
1.链式二叉树的实现
1.1前置说明
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式,代码如下:
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;
BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->_left = node2;node1->_right = node4;node2->_left = node3;node4->_left = node5;node4->_right = node6;return node1;
}
注意:
上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。
1.2结构体以及声明
跟我们之前学习的数据结构一样,我们为了方便存储各种数据类型,我们会先对堆存储的数据类型进行重定义,具体如下:
typedef int BTDataType;
在这里我们实现的是二叉树链式存储的存储结构。还是跟之前一样,结构体中有三个成员,一个指向当前结点的值,还有两个是指向当前结点左孩子结点的指针以及指向右孩子结点的指针,具体如下:
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;
具体如下图:
2.遍历二叉树
2.1算法描述
遍历二叉树( traversing binary tree )是指按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。
访问的含义很广,可以是对结点做各种处理,包括输出结点的信息,对结点进行运算和修改等。遍历二叉树是二叉树最基本的操作,也是二叉树其他各种操作的基础,遍历的实质是对二叉树进行线性化的过程,即遍历的结果是将非线性结构的树中结点排成一个线性序列。由于二叉树的每个结点都可能有两棵子树,因而需要寻找一种规律,以便使二叉树的结点能排列在一个线性队列上,从而便于遍历
回顾二叉树的递归定义可知,二叉树是由3个基本单元组成:根结点、左子树和右子树。依次遍历这三部分,便是遍历了整个二叉树。假如从 L 、 D 、 R 分别表示遍历左子树和遍历右子树,则可有 DLR 、 LDR 、 LRD 、 DRL 、 RDL 、 RLD 这6种遍历二叉树的方左后右,则只有前3种情况,分别称之为先(根)序遍历、中(根)序遍历和后(根)遍历。基于二叉树的递归定义,可得下述遍历二叉树的递归算法定义,我们一个一个认识。
2.2先序遍历
定义:
前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
思想:
若二叉树为空,则空操作;否则
(1)访问根结点
(2)先序遍历左子树
(3)先序遍历右子树
即考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)
举例:
因为这是第一次认识,我们举个具体的例子来带大家深入理解,我们以下图为例来具体分析先序遍历的步奏:
如图所示,采用先序遍历访问这颗二叉树的详细过程为:
1.访问该二叉树的根节点,找到 1;
2.访问节点 1 的左子树,找到节点 2;
3.访问节点 2 的左子树,找到节点 4;
4.由于访问节点 4 左子树失败,且也没有右子树,因此以节点 4 为根节点的子树遍历完成。但节点 2 还没有遍历其右子树,因此现在开始遍历,即访问节点 5;
5.由于节点 5 无左右子树,因此节点 5 遍历完成,并且由此以节点 2 为根节点的子树也遍历完成。现在回到节点 1 ,并开始遍历该节点的右子树,即访问节点 3;
6.访问节点 3 左子树,找到节点 6;
7.由于节点 6 无左右子树,因此节点 6 遍历完成,回到节点 3 并遍历其右子树,找到节点 7;
8.节点 7 无左右子树,因此以节点 3 为根节点的子树遍历完成,同时回归节点 1。由于节点 1 的左右子树全部遍历完成,因此整个二叉树遍历完成;
因此,图 中二叉树采用先序遍历得到的序列为:1 2 4 5 3 6 7
我们在通过另外一个例子图解解递归算法:
代码如下:
void PrevOrder(BTNode* root)
{if (root == NULL) {printf("NULL ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}
2.3中序遍历
定义:
中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)
思想:
若二叉树为空,则空操作;否则
(1)中序遍历左子树
(2)访问根结点
(3)中序遍历右子树
即考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)
举例:
以上图为例,采用中序遍历的思想遍历该二叉树的过程为:
1.访问该二叉树的根节点,找到 1;
2.遍历节点 1 的左子树,找到节点 2;
3.遍历节点 2 的左子树,找到节点 4;
4.由于节点 4 无左孩子,因此找到节点 4,并遍历节点 4 的右子树;
5.由于节点 4 无右子树,因此节点 2 的左子树遍历完成,访问节点 2;
6.遍历节点 2 的右子树,找到节点 5;
7.由于节点 5 无左子树,因此访问节点 5 ,又因为节点 5 没有右子树,因此节点 1 的左子树遍历完成,访问节点 1 ,并遍历节点 1 的右子树,找到节点 3;
8.遍历节点 3 的左子树,找到节点 6;
9.由于节点 6 无左子树,因此访问节点 6,又因为该节点无右子树,因此节点 3 的左子树遍历完成,开始访问节点 3 ,并遍历节点 3 的右子树,找到节点 7;
10.由于节点 7 无左子树,因此访问节点 7,又因为该节点无右子树,因此节点 1 的右子树遍历完成,即整棵树遍历完成;
因此,上图中二叉树采用中序遍历得到的序列为:4 2 5 1 6 3 7
代码如下:
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}
2.4后序遍历
定义:
后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
思想:
若二叉树为空,则空操作;否则
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点
即考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)
举例:
我们还是以之前的图为例:
如上图中,对此二叉树进行后序遍历的操作过程为:
从根节点 1 开始,遍历该节点的左子树(以节点 2 为根节点);
1.遍历节点 2 的左子树(以节点 4 为根节点);
2.由于节点 4 既没有左子树,也没有右子树,此时访问该节点中的元素 4,并回退到节点 2 ,遍历节点 2 的右子树(以 5 为根节点);
3.由于节点 5 无左右子树,因此可以访问节点 5 ,并且此时节点 2 的左右子树也遍历完成,因此也可以访问节点 2;
4.此时回退到节点 1 ,开始遍历节点 1 的右子树(以节点 3 为根节点);
5.遍历节点 3 的左子树(以节点 6 为根节点);
6.由于节点 6 无左右子树,因此访问节点 6,并回退到节点 3,开始遍历节点 3 的右子树(以节点 7 为根节点);
7.由于节点 7 无左右子树,因此访问节点 7,并且节点 3 的左右子树也遍历完成,可以访问节点 3;节点 1 的左右子树也遍历完成,可以访问节点 1;
由此,对上图 中二叉树进行后序遍历的结果为:4 5 2 6 7 3 1
代码如下:
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}
2.5层序遍历
定义:
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
举例:
解析:
层次遍历如上图中的二叉树:
1.根结点 1 入队;
2.根结点 1 出队,出队的同时,将左孩子 2 和右孩子 3 分别入队;
3.队头结点 2 出队,出队的同时,将结点 2 的左孩子 4 和右孩子 5 依次入队;
4.队头结点 3 出队,出队的同时,将结点 3 的左孩子 6 和右孩子 7 依次入队;
5.不断地循环,直至队列内为空。
由此,对上图 中二叉树进行层序遍历的结果为:1 2 3 4 5 6 7
层序遍历不是一个递归过程,层序遍历的实现可以借助队列这种数据结构来进行实现,具体如下:
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->data);QueuePop(&q);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}}printf("\n");QueueDestroy(&q);
}
2.6算法分析
无论是递归还是非递归遍历二叉树,因为每个结点被访问一次,则不论按哪一种次序进行遍历,对于含有n个结点的二叉树,其时间复杂度均为O(n)。所需辅助空间为遍历过程中队列的最大容量,即树的深度,最坏情况下为n,则空间复杂度为O(n).
3.接口功能的实现
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
//二叉树的高度
int TreeHeight(BTNode* root)
3.1二叉树节点个数
思想:
这里需要用到递归的思想去进行解决,进行一个分块求解然后再加起来就可以了。单次逻辑是只要求出左子树结点个数,再加上右子树节点个数,最后再加1就得到二叉树的总结点个数了。
int TreeSize2(BTNode* root)
{return root == NULL ? 0 :TreeSize2(root->left) + TreeSize2(root->right) + 1;
}
3.2二叉树叶子节点个数
思想:
整体思想就是不断向下递归找叶子结点,然后把左右子树的叶子结点总个数加起来,然后在递归进行展开。
程序的结束条件有两个:
1.一个是遇到了叶子结点;
2.另一个是遇到NULL结点,需要返回0.
代码如下 :
// 叶子节点
int TreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
3.3二叉树第k层节点个数
思想:
整体思路还是很简单:
首先判断一个传进来的根结点是否为空
当k > 1 时,第k层的结点个数为其左孩子的第k - 1层 + 其右孩子的第k - 1层结点个数
当k ==1时,return 1
代码如下:
// 求第k层的节点个数 k >= 1
int TreeKLevelSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;// k > 1 子树的k-1return TreeKLevelSize(root->left, k - 1)+ TreeKLevelSize(root->right, k - 1);
}
3.4二叉树查找值为x的节点
思想:
整体思路比较明显,要么遍历根节点,如果是则表示找到我们想要的值;要么就是遍历完整个二叉树都没有找到该结点,至此递归便结束。具体实现就是根节点data和x比较看是否相等,相等我们立马返回;不相等就拿左子树根节点的data和x比较,如果还不相等,我们就拿右子树根节点的data和x进行比较,如果最后还是没有找到,则返回NULL
代码如下:
BTNode* TreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;struct BinaryTreeNode* ret1 = TreeFind(root->left, x);if (ret1)return ret1;struct BinaryTreeNode* ret2 = TreeFind(root->right, x);if (ret2)return ret2;return NULL;
}
3.5二叉树的高度
思想:
整体思想就是先比较出左子树和右子树中高度高的那个,最后在加上根节点的高度【1】即可!
代码如下:
int TreeHeight(BTNode* root)
{if (root == NULL){return 0;}int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
3.6二叉树的销毁
思想:
如果开始时从根节点开始往下销毁,现将根节点销毁了之后,那左右子树不是找不到了,不符合我们所需要的。因此我们应该先从左右子树开始,但是这样又会遇到一个问题,对于左子树来说它又可以作为一个根结点,依旧是需要先去销毁其左右子树,右子树也是一样,因此这就又成了一个递归的问题。跟我们后序遍历的思想就不谋而合了!!!
代码如下:
void DestroyTree(BTNode* root)
{if (root == NULL) //if(!root)return;DestroyTree(root->lchild);DestroyTree(root->rchild);free(root);
}
3.7判断是否为完全二叉树
思想:
通过前面的学习,我们已经对完全的基本概念有了一定的了解。因此,这里在进行判断是我们可以考虑使用层序遍历的思想去进行解决。
具体:
首先二叉树元素全部入队,如果队列中的元素有空结点,并且空结点后面有不为空的元素那就说明此二叉树不是完全二叉树;
如果后面的元素都是空结点,那就说明这个二叉树是完全二叉树
具体代码如下:
```c
bool TreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)//遇到NULL,就可以开始判断是否为完全二叉树了{break;}else{QueuePush(&q, front->left);QueuePush(&q, front->right);}}//出到NULL以后,如果后面全是空,则是完全二叉树,如果有非空结点,那就不是完全二叉树while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);//防止内存泄露return false;}}QueueDestroy(&q);return true;
}
4.选择题练习
1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为( )
A ABDHECFG
B ABCDEFGH
C HDBEAFCG
D HDEBFGCA
解答:
标记文本选A,根据层序遍历的特点以及完全二叉树的概念,我们可以改二叉树为下图:
2.二叉树的先序遍历和中序遍历如下:先序遍历: EFHIGJK ;中序遍历: HFIEJKG 该二又树根的右子树的根是()
A E
B F
C G
D H
解答:
选C。考察的是先序(根左右),中序(左根右)来推断二叉树的结构。
根据题干中的先序和中序可以确定二叉树的结构。先序:确定E为二叉树的根节点,中序:HFI为E的左子树节点,JKG为E右子树节点。
先序:GJK 中序:JKG 根据先序得出G为右子树的根节点
3.设一棵二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树先序遍历序列为( )。
A FEDCBA
B CBAFED
C DEFCBA
D ABCDEF
解答:选A
前序遍历:根结点 —> 左子树 —> 右子树
中序遍历:左子树—> 根结点 —> 右子树
后序遍历:左子树 —> 右子树 —> 根结点
既然后序遍历和中序遍历结果一样,那就说明整棵二叉树都没有右子树,所以整棵树看起来就像是普通的链式结构一样,如下图:
5.OJ题练习
5.1 单值二叉树(LeetCode 965题)
链接:单值二叉树
题目:
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true;否则返回 false。
思路:
我们判断一棵树的所有节点都有相同的值,当且仅当对于树的孩子结点都相等时才满足相应的条件(这样根据传递性,所有节点都有相同的值)。 因此每次比较其根节点和其左右结点是否相等,若是发现不相等,立马返回false,若是相等,则递归其左右子树继续比较,直到访问最后的结点为NULL时,则得出此树为单值二叉树
代码如下:
bool isUnivalTree(struct TreeNode* root){if (!root) {return true;}if (root->left) {if (root->val != root->left->val || !isUnivalTree(root->left)) {return false;}}if (root->right) {if (root->val != root->right->val || !isUnivalTree(root->right)) {return false;}}return true;
}
5.2检查两颗树是否相同(LeetCode 100题)
链接:相同的树
题目:
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
思路:
总体思想就是要比较两个二叉树相同,当且仅当两个二叉树的结构完全相同,且所有对应节点的值相同。如果满足上述条件则判断两颗树完全相同。
具体过程:
1.如果两个二叉树都为空,则两个二叉树相同。如果两个二叉树中有且只有一个为空,则两个二叉树一定不相同;
2.如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同;
3.若相同,再递归的去分别判断两个二叉树的左子树和右子树是否相同
代码如下:
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);
}
5.3 对称二叉树(LeetCode 101题)
链接:对称二叉树
题目:
给你一个二叉树的根节点 root , 检查它是否轴对称。
思路:
对称二叉树也称为【镜像二叉树】两个树在什么情况下互为镜像?
1.它们的两个根结点具有相同的值
2.每个树的右子树都与另一个树的左子树镜像对称
基本思想就是以根结点为对称轴,递归进行左子树和右子树中的元素的比较,如果都相同,则判断是对称二叉树
代码如下(我们这里复用了上述相同的树的代码思想):
bool isSameTree(struct TreeNode* LeftTree, struct TreeNode* RightTree)
{if(LeftTree == NULL && RightTree == NULL)return true;if(LeftTree == NULL || RightTree == NULL)return false;if(LeftTree->val != RightTree->val)return false;return isSameTree(LeftTree->left,RightTree->right)&& isSameTree (LeftTree->right,RightTree->left);
}bool isSymmetric(struct TreeNode* root){if(root == NULL)return true;return isSameTree(root->left,root->right); }
5.4另一颗树的子树(LeetCode 572题)
链接:另一颗树的子树
题目:
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
思路:
这道题直接暴力挺简单的,首先判断一个树是否是另一棵树的子树,很明显想到可以用递归:
1.先用跟节点去比较
2.不成功,递归用左孩子去比较
3.不成功,递归用右孩子去比较
代码如下:
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;if(isSameTree(root,subRoot))return true;return isSubtree(root->left,subRoot) ||isSubtree(root->right,subRoot);
}
大家可以发现,有了上述【相同的树】之后,做这两道题就很简单!!!
5.5二叉树的前序遍历(LeetCode 144题)
链接:二叉树的前序遍历
题目:
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
思路:
按照访问根节点——左子树——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。
此题要保存节点,所以需要先获取节点个数,然后进行前序遍历,保存每一个节点值。
代码如下:
void preorder(struct TreeNode* root,int* index,int* ret)
// index 当做数组的下标 ret是数组名称
{if(NULL == root)return; else{ret[(*index)++] = root->val;preorder(root->left,index,ret);preorder(root->right,index,ret);}
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{int* ret =(int*)malloc(100* sizeof(int));*returnSize = 0;preorder(root,returnSize,ret);return ret;
}
5.6 二叉树中序遍历(LeetCode 94题)
链接:二叉树中序遍历
思路:
当我们了解先序遍历,中序遍历在这里就直接给出代码:
void inorder(struct TreeNode* root,int* index,int* ret)// index 当做数组的下标 ret是数组名称
{if(NULL == root)return; else{inorder(root->left,index,ret);ret[(*index)++] = root->val;inorder(root->right,index,ret);}
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){int* ret =(int*)malloc(100* sizeof(int));*returnSize = 0;inorder(root,returnSize,ret);return ret;
}
5.7二叉树的后序遍历(LeetCode 145题)
链接:二叉树的后序遍历
思路:
后序遍历也是一样的,我们直接给出代码:
void postorder(struct TreeNode* root,int* index,int* ret)// index 当做数组的下标 ret是数组名称
{if(NULL == root)return; else{postorder(root->left,index,ret);postorder(root->right,index,ret);ret[(*index)++] = root->val;}
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){int* ret =(int*)malloc(100* sizeof(int));*returnSize = 0;postorder(root,returnSize,ret);return ret;
}
6.总结
在这里最重要的就是要我们掌握递归的基本思想。其实不管是哪种遍历方式,我们最终的目的就是访问所有的树(子树)的根节点,左孩子,右孩子(我们以左孩子节点为基准,先序遍历是在访问左孩子节点之前打印节点,中序遍历是在左孩子节点压栈之后打印节点,后序遍历是在访问完左右孩子节点之后打印节点)。当大家不懂时多画图理解(会有意想不到的效果哟!!!)
好了,本期博文就到这里了。如果觉得有用的话记得三连支持哟!!
相关文章:
【链式二叉树】数据结构链式二叉树的(万字详解)
前言: 在上一篇博客中,我们已经详解学习了堆的基本知识,今天带大家进入的是二叉树的另外一种存储方式----“链式二叉树”的学习,主要用到的就是“递归思想”!! 本文目录1.链式二叉树的实现1.1前置说明1.2结…...
Koa2篇-简单介绍及使用
一.简介koa2是基于 Node.js 平台的下一代 web 开发框架, 致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 可以避免异步嵌套. express中间件是异步回调,Koa2原生支持async/await二.async/awaitconst { rejects } require("assert"); const { resolve } req…...
Linux ALSA 之十一:ALSA ASOC Path 完整路径追踪
ALSA ASOC Path 完整路径追踪一、ASoc Path 简介二、ASoc Path 完整路径2.1 tinymix 设置2.2 完整路径 route一、ASoc Path 简介 如前面小节所描述,ASoc 中 Machine Driver 是 platform driver 和 codec driver 的粘合剂,audio path 离不开 FE/BE/DAI l…...
【Spring Cloud总结】1、服务提供者与服务消费者快速上手
目录 文件结构 代码 1、api 1.1实体类(Dept ) 1.2数据库 2、provider 2.1 DeptController 2.2 DeptDao 2.3 DeptService 2.4 DeptServiceImpl 2.5 application.yml 3、consumer 3.1 ConfigBean 3.2 DeptConsumerController 测试 1.启动…...
若依项目学习之登录生成验证码
若依项目学习之登录生成验证码 使用DefaultKaptcha生成验证码 /*** 验证码配置* * author ruoyi*/ Configuration public class CaptchaConfig {/*** 生成字符类型的验证码**/Bean(name "captchaProducer")public DefaultKaptcha getKaptchaBean(){DefaultKaptcha…...
计算机网络5:数据在两台计算机之间是怎样传输的?
数据在两台计算机之间的传输总的来说包括了封装和解封两个过程 封装(5层协议) 以传送一张图片为例 **应用层:**将jpg格式的图片数据转化成计算机可以识别的0101的二进制的比特流 **传输层:**将应用层传输下来的数据进行分段&…...
就现在!为元宇宙和Web3对互联网的改造做准备!
欢迎来到Hubbleverse 🌍 关注我们 关注宇宙新鲜事 📌 预计阅读时长:8分钟 本文仅代表作者个人观点,不代表平台意见,不构成投资建议。 如今,互联网是各种不同的网站、应用程序和平台的集合。由于彼此分离…...
【mysql数据库】
目录SQL数据库分页聚合函数表跟表之间的关联关系SQL中怎么将行转成列SQL注入将一张表的部分数据更新到另一张表WHERE和HAVING的区别索引索引分类如何创建及保存MySQL的索引?怎么判断要不要加索引?索引设计原理只要创建了索引,就一定会走索引吗…...
【测试开发】web 自动化测试 --- selenium4
目录1. 什么是自动化为什么要做自动化2. 为什么选择selenium作为我使用的web自动化工具3. 什么是驱动?驱动的工作原理是什么5. 第一个自动化程序演示6. selenium基本语法6.1 定位元素的方法6.2 操作页面元素6.3 等待6.4 信息打印获取当前页面句柄,窗口切…...
Elasticsearch7.8.0版本进阶——路由计算
目录一、路由计算1.1、路由计算的前提理解1.2、路由计算的概述1.3、路由计算的概述一、路由计算 1.1、路由计算的前提理解 当索引一个文档的时候,文档会被存储到一个主分片中。Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时…...
c#反射-获取属性和字段的值
演示类 示例类具有一个私有实例字段,一个实例属性,一个实例字段,一个静态私有属性。 class Fight {private int hp;public int Hp{get > hp; set{if (value > 0){ hp value; }else if (-value > Def){ hp value - Def; }}}publi…...
前后端分离-小项目-1前端布局
技术栈前后端分离开发,前端主体框架Vue3后端基础框架Spring-Boot1.前端技术栈:Vue3AxiosElementPlus2.后端技术栈:Spring BootMyBatis Plus3.数据库-MySQL4.项目的依赖管理-Maven5.分页-MyBatis Plus的分页插件环境搭建安装Node.js LTSnode.j…...
基于jsp的网络电子相册的设计与实现
技术:Java、JSP等摘要:随着科学技术的不断进步,云技术以及大数据的不断完善,越来越多的网络忠实用户告别了冲洗相片的时代,他们更喜欢将相片上传至网络,这样就省去了携带和查找的麻烦,随时随地只…...
Python快速上手系列--类--详解篇
本章是自动化测试的真正开始,因为在后续的过程中,你会接触到unittest框架,pytest框架,而不仅仅只是写一个函数selenium脚本这么简单了。1、创建类1.1、了解类我们首先了解一下,为什么要使用类,类可以拿来干…...
Dubbo基本原理和用法讲解
Dubbo基本原理和用法讲解 序言:学习一项新技术,一般从是什么、为什么、怎么用三个方面进行学习。本篇文章也不例外,笔者将从Dubbo是什么?、为什么会产生Dubbo技术?、如何在项目中使用Dubbo技术。最后,笔者…...
TCP详解及面试相关问题
文章目录1、计算机模型2、客户端和服务端通信——TCP协议(1)socket套接字(2)TCP三次握手——创建socket(3)连接的本质(4)TCP四次挥手——释放socket资源(5)TC…...
LVGL V9.0基于VS2022仿真搭建
完整Demo,lvgl,lvgl_drivers相关资料下载 链接:https://pan.baidu.com/s/1DNJeHdoaPyfe1BsLb9wjRg 提取码:wov7 其它资料下载 链接:https://pan.baidu.com/s/1nV9jojPEPWSWZdYhaCZWTA 提取码:91j8 下载资料后解压文…...
多线程面试题开胃菜2(5道)
一.一个线程的生命周期有哪几种状态?它们之间如何流转的?NEW:毫无疑问表示的是刚创建的线程,还没有开始启动。RUNNABLE: 表示线程已经触发 start()方式调用,线程正式启动,线程处于运行中状态。BLOCKED&…...
第三次作业
一、单表查询素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作 等CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) N…...
基础算法(直接插入,希尔排序,快排,归并,折半查找)
/*直接插入:把待排序序列分为有无序区和和无序区,使用无序区的数据一次插入倒有序区中,最终结果尾有序序列 1> 把数据分为有序区和无序区,默认第一个元素在有序区,剩下在无序区 2> 外层循环,循环无…...
电子学会2022年12月青少年软件编程(图形化)等级考试试卷(一级)答案解析
目录 一、单选题(共25题,共50分) 二、判断题(共10题,共20分) 三、编程题(共2题,共30分) 青少年软件编程(图形化)等级考试试卷(一级) 一、单选题(共25题,共50分) 1. 小明想在开始…...
基于JAVA的超级玛丽设计与实现
技术:Java等摘要:随着计算机技术及网络技术的不断发展,电子游戏越来越普及。经典游戏“超级玛丽”因其本身所具有的娱乐性与教育意义而被人们广泛接受,在广大的青少年玩家中享有极高的知名度。Java语言作为一种完全面向对象的程序…...
硬件工程师入门基础知识(一)基础元器件认识(二)
硬件工程师入门基础知识 (一)基础元器件认识(二) tips:学习资料和数据来自《硬件工程师炼成之路》、百度百科、网上资料。 1.二极管 2.三极管 3.MOS管 4.IGBT 5.晶振 1.二极管 肖特基二极管和硅二极管的比较&#…...
Python-项目实战--贪吃蛇小游戏-游戏框架搭建(2)
1.游戏框架搭建介绍pygame开发图像界面游戏的几个要素,并且把贪吃蛇游戏的整体框架搭建完成本节知识点包括:pygame的初始化和退出游戏主窗口游戏循环和游戏时钟主窗口背景颜色绘制文本pygame的坐标系游戏事件监听绘制图形定时器事件1.1pygame的初始化和退…...
JVM基础
JVM基础 1.JVM的位置 JVM是运行在操作系统之上的,它与硬件没有直接的交互 2.JVM体系结构图 这个区域一定不会有垃圾回收 所谓JVM的调优,其实就是在调这个区域,而且99%情况下都在调堆 ! 3.类加载器ClassLoader 先来看看一个类加载到 JVM 的…...
Android 内存优化(基础轮)必看~
本次分享主要分为五个部分内容,第一部分内容是 5W2H 分析内存优化,第二部分内容是内存管理机制,第三部分内容是内存优化 SOP,第四部分内容是 内存优化指导原则, 最后一部分内容是总结与展望。 如果学完内存优化的基础论…...
STM32单片机GSM短信自动存取快递柜
实践制作DIY- GC0104-自动存取快递柜 一、功能说明: 基于STM32单片机设计-自动存取快递柜 二、功能介绍: STM32F103C系列最小系统板0.96寸OLED显示器DY-SV17F串口语音播报模块4*4矩阵键盘GSM短信模块4路舵机(模拟4个柜子) ***…...
力扣(LeetCode)410. 分割数组的最大值(2023.02.12)
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法使得这 m 个子数组各自和的最大值最小。 示例 1: 输入:nums [7,2,5,10,8], m 2 输出:18 解释: 一共有四种方法…...
管理还原数据
还原数据还原数据是:• 原始的、修改之前的数据副本• 针对更改数据的每个事务处理而捕获• 至少保留到事务处理结束• 用于支持:– 回退操作– 读取一致性查询– Oracle 闪回查询、Oracle 闪回事务处理和 Oracle 闪回表– 从失败的事务处理中进行恢复存…...
c的关键字有那些
编程语言中的关键字 C语言简洁、紧凑,使用方便、灵活。ANSI C标准C语言共有32个关键字,9种控制语句,程序书写形式自由,区分大小写。把高级语言的基本结构和语句与低级语言的实用性结合起来。 C 语言可以像汇编语言一样对位、字节和…...
互联网运营培训班哪个好/优化 seo
1、查看firewall服务状态systemctl status firewalld2、查看firewall的状态firewall-cmd --state3、开启、重启、关闭、firewalld.service服务# 开启 service firewalld start # 重启 service firewalld restart # 关闭 service firewalld stop4、查看防火墙规则firewall-cmd -…...
科技公司起名字大全免费/文军seo
入门 简单使用 <script src"../js/vue.js"></script><div id"app">{{message}}{{movies}} </div><script> const app new Vue({el:#app, //用于管理要管理的元素data:{ //定义数据 或者服务器请求数据message:你好啊,mo…...
乐平网站建设/南宁seo外包靠谱吗
课程题目:做一个球员管理系统。Github地址:https://github.com/Radium1209/Player_Management_SystemPS:已经录入了部分账户,admin账户密码111111,各个国家队密码都为123456,guest账户密码123456录入国家队…...
网站建设课程设计/seo推广绩效考核指标是什么
 1、Android-Universal-Image-Loader 可以高度配置的网络图片缓存库,非常灵活,用户量最多 缓存过期实现: File cacheDir StorageUtils.getCacheDirectory(context); // or any other folder MemoryCacheAware&l…...
昆山网站/广告推广计划
据权威机构统计,在所有的软件开发类人才中对Java开发人才的需求量最大,达到了60%-70%,Java因其所提供的强大功能平台而受到越来越多企业的青睐。 那么,Java开发就业前景如何?让千锋带你好好了解! 1、程序员擅长语言 图表显示&…...
帮别人做彩票网站犯法嘛/seo外包是什么
Port Knocking for Ubuntu 14.04 Server OS:ubuntu 14.04 server 原理简单分析: 端口敲门服务,即:knockd服务。该服务通过动态的添加iptables规则来隐藏系统开启的服务,使用自定义的一系列序列号来“敲门”,使系统开启…...