【初阶数据结构与算法】二叉树链式结构的定义与实现万字笔记(附源码)

在这里插入图片描述

文章目录

  • 一、二叉树链式结构的定义
  • 二、二叉树链式结构功能的基本实现
    • 1.链式二叉树的手动创建
    • 2.链式二叉树的前中后序遍历
      • 前序遍历
      • 中序遍历
      • 后序遍历
    • 3.链式二叉树节点的个数
    • 4.链式二叉树叶子节点的个数
    • 5.链式二叉树的高度/深度
    • 6.链式二叉树第k层节点的个数
    • 7.链式二叉树的查找
    • 8.链式二叉树层序遍历
    • 9.判断链式二叉树是否为完全二叉树
    • 10.链式二叉树的销毁
  • 三、源码

一、二叉树链式结构的定义

   链式二叉树就是⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系,通常的⽅法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址,数据域则是当前节点存放的数据,其结构如下:

typedef int BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;

   当我们使用链式结构来表示二叉树时,这个二叉树并不会有很多限制,它对节点的插入删除等操作要求并不高,所以我们后面会手动来创建链式二叉树,本文重要的不是创建二叉树的方法,而是二叉树各种功能的实现
   那么什么时候我们不能手动创建二叉树了呢?这个就要等我们后面在C++部分讲到二叉搜索树才能揭晓答案,二叉搜索树就只能通过特定的函数创建,而不能手动创建了,接着我们就再次回到我们今天的主题—二叉树链式结构功能的基本实现

二、二叉树链式结构功能的基本实现

1.链式二叉树的手动创建

   链式二叉树的手动创建很简单,就是创建一堆的节点,然后将它们互相连接起来,这里给出一个二叉树的图片,我们按照图片的结构手动创建一颗链式二叉树,如下:
在这里插入图片描述

   为了方便我们直接操作,我们先写一个申请二叉树节点的函数,可以通过传节点的值来帮我们申请一个具有该值的节点,如下:

//申请节点
BTNode* BTBuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("mallc");return NULL;}newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}

   接下来我们就挨个的申请节点,然后使用左右指针将这些节点串联起来,如下:

//手动创建一颗链式二叉树
//返回根节点
BTNode* CreateBinaryTree()
{BTNode* nodeA = BTBuyNode('A');BTNode* nodeB = BTBuyNode('B');BTNode* nodeC = BTBuyNode('C');BTNode* nodeD = BTBuyNode('D');BTNode* nodeE = BTBuyNode('E');BTNode* nodeF = BTBuyNode('F');BTNode* nodeG = BTBuyNode('G');BTNode* nodeH = BTBuyNode('H');nodeA->left = nodeB;nodeA->right = nodeC;nodeB->left = nodeD;nodeB->right = nodeE;nodeC->left = nodeF;nodeC->right = nodeG;nodeD->left = nodeH;return nodeA;
}

   这样我们手动创建一颗链式二叉树,返回了它的根节点,接下来我们就可以开始对它进行操作了,后面的内容才是今天的重点

2.链式二叉树的前中后序遍历

   链式二叉树的这个结构非常适合递归,它每一次函数调用创建的栈帧就可以看做我们要递归的一个节点,在链式二叉树的接口实现中,我们一定要有抽象能力,当然,我们后面也会给大家画图来解释,这里不再多说
   我们今天第一个重点就是完成链式二叉树的遍历,如果不能实现二叉树的遍历,那么我们连里面存放了什么数据都不知道,所以接下来我们就开始介绍二叉树的前中后序遍历
   首先我们来解释一下前中后序遍历,前序遍历就是根左右,也就是先打印当前子树中的根,再把左右孩子当成另外两颗子树,继续递归执行根左右的思想
   中序遍历就是左根右,也就是先递归打印当前根的左孩子这颗子树,然后再打印根节点,最后再递归打印当前根的右孩子这颗子树
   而后序遍历就是左右根,跟上面两种例子是同样的思想,就是先递归打印当前根的左子树,再递归打印当前根的右子树,最后再打印根
   听到这里你是不是懵了呢?这是很正常的,光听概念是很难学会递归的,我们要在实例中去应用才能掌握,所以下面实现前中后序遍历的过程和思想才是我们要重点掌握的,而不是这里前中后序遍历的概念,我们来一起学习吧!

前序遍历

   前序遍历就是左右根,打印当前子树中的根,左右孩子又分别成一颗子树,于是左右孩子又再次执行上面的根左右的遍历思想,最终这样递归下去就可以实现二叉树的前序遍历
   如果不理解可以先不管,现在我们先把代码写出来,等一下根据代码我们来画图才能将它说清楚,这也是一种学习方法,这种抽象的算法先看代码再理解,否则很难想到,之后我们才能根据理解写出类似的算法,如下:

//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL){//走到空节点开始返回return;}//打印根节点printf("%c ", root->data);//递归左孩子这颗子树PreOrder(root->left);//递归右孩子这颗子树PreOrder(root->right);
}

   接着我们来画图按照这段代码的逻辑来走一遍我们的前序遍历,要仔细理解这里的图,后面才能解决我们的中后序遍历,如图:
在这里插入图片描述
   那么我们分析完前序遍历之后,我们来看看我们前序遍历代码能不能实现我们画图所诉的打印,如图:
在这里插入图片描述
   可以看到代码的运行结果和我们分析的一模一样,这就是我们的前序遍历,是我们第一次接触到递归的暴力美学,所以我们讲的很仔细,要仔细理解
   后面的中后序遍历基本上都是一个思想,所以后面的中后序遍历就不再画图了,可以自行画图来理解,思路大致相同

中序遍历

   中序遍历就是先递归打印左孩子这颗子树,然后打印根节点,最后再递归打印右孩子这颗子树,和前序遍历的思想都很接近,只是说根节点的打印不同
   中序遍历的实现和前序遍历的思想差不多,也是通过递归实现,可以自己尝试画画中序遍历的图,看看自己能不能写出来中序遍历,再来看后面的代码,这里就直接给出中序遍历的实现代码了:

//中序遍历:左根右
void InOrder(BTNode* root)
{if (root == NULL){//走到空节点开始返回return;}//递归左孩子这颗子树InOrder(root->left);//打印根节点printf("%c ", root->data);//递归右孩子这颗子树InOrder(root->right);
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到中序遍历和前序遍历代码的实现只有一点点不同,就是打印根节点的位置不同,但是其实结果已经发生了超级多的变化,如果感兴趣可以自行画画图理解,思路和前序遍历差不多

后序遍历

   后序遍历就是先递归打印当前根的左子树,再递归打印当前根的右子树,最后再打印根,跟上面前中序遍历的思路很像
   而现在我们有了前中序遍历思想的启迪,接下来我们实现后序遍历就很简单了,很多同学可能都能直接猜到代码该怎么写,那么在这之前,还是希望大家先画图分析一下,写出代码,再来看看自己写的对不对,代码如下:

//后序遍历:左右根
void PostOrder(BTNode* root)
{if (root == NULL){//走到空节点开始返回return;}//递归左孩子这颗子树PostOrder(root->left);//递归右孩子这颗子树PostOrder(root->right);//打印根节点printf("%c ", root->data);
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到后序遍历和前中序遍历代码的实现基本上也是差别很小,也是打印根节点的位置不同,但是其实也发生了很多的变化,希望可以自己画图理解理解,这里就不再画了,接着学习链式二叉树的下一个接口

3.链式二叉树节点的个数

   在上面我们稍微体会到了一点递归的暴力美学,从现在开始,就是真正开始爽的地方,我们就根据之前前中后序的递归思想实现这些接口,代码量都很小,但是却可以实现我们的要求,我们一起来学习
   求二叉树节点的个数不就是求当前根节点的个数,再加上左右子树节点的个数吗?所以我们可以利用递归的思想,当前根节点算1,然后加上左右子树节点的个数
   当然,我们一旦要递归就要考虑返回条件,这里的返回条件就是根节点为空,如果根节点为空了,就直接返回0,于是代码就水灵灵出来了,如下:

//二叉树节点的个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}int leftsize = BinaryTreeSize(root->left);int rightsize = BinaryTreeSize(root->right);return 1 + leftsize + rightsize;
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到成功算出了我们二叉树中所有节点的个数

4.链式二叉树叶子节点的个数

   求整颗二叉树叶子节点个数可以猜分为,左子树叶子节点个数加右子树叶子节点个数,这样就可以继续使用我们的递归思想了
   求叶子节点个数相当于比前面要求严格一些,不是只要根节点不为空就要算,而是根节点既不为空,并且这个根节点的左右孩子都为空,这样才算一个
   所以我们最后总结为,如果根节点为空直接返回,如果不为空并且左右孩子都为空就返回1,然后我们整颗二叉树叶子节点个数这个问题就可以拆成,左子树叶子节点个数加右子树叶子节点个数,如下:

//二叉树叶子节点的个数
int BinaryTreeLeaveSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}int leftsize = BinaryTreeLeaveSize(root->left);int rightsize = BinaryTreeLeaveSize(root->right);return leftsize + rightsize;
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到得出了我们想要的结果

5.链式二叉树的高度/深度

   在解决这个问题之前,我们要知道如何判断二叉树的高度,就是给我们二叉树,我们要能判断二叉树的高度,如图:
在这里插入图片描述
   我们肉眼可能一下就看出来,二叉树1的高度为4,二叉树2的高度为3,那么它们是怎么来的呢?其实二叉树的高度就是它左右子树中最高的那个,最后再加上根节点,所以我们就还是可以利用递归的算法思想,如下:

//二叉树的高度/深度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDepth = 1 + BinaryTreeDepth(root->left);int rightDepth = 1 + BinaryTreeDepth(root->right);return leftDepth > rightDepth ? leftDepth : rightDepth;
}

   我们来看看代码运行结果:

在这里插入图片描述
   可以看到得出了我们想要的结果

6.链式二叉树第k层节点的个数

   这个需求就和上面求叶子节点个数类似,求叶子节点个数是如果左右孩子为空返回1,这里就是如果当前节点在k层就返回1
   那么如何判断这个节点是否在第k层呢?首先我们还是使用递归的算法思想,将求二叉树第k层节点的个数,转化为求二叉树左右子树的第k层节点个数的和
   具体判断方法就是,我们每递归一层就让k-1,当k变成了1,那么就说明当前节点就在第k层,这个可以自行尝试,这里就不再多说
   那么有了这个思路我们就可以直接写代码了,如下:

//⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k-1) + BinaryTreeLevelKSize(root->right, k-1);
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到程序帮我们算出了对应层次节点的个数

7.链式二叉树的查找

   这个功能我们还是可以使用递归的思想解决,我们在二叉树中查找某个节点,其实就是看当前根节点是不是要查找的节点,如果是就直接返回
   如果不是就去对应的左子树里面找,如果左子树里面找到了,就直接返回,如果左子树也没有找到,就去右子树中找,如果找到了就直接返回,如果最后左右子树都没有找到就直接返回空,如下:

//查找值为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;
}

   我们来测试一下代码能否实现我们的查找功能,首先我们来找找二叉树中有的节点,如图:
在这里插入图片描述
   可以看到程序很好地完成了任务,接下来我们再测试一下如果二叉树没有这个节点会不会被找到,如图:
在这里插入图片描述
   可以看到程序最后没有找到,符合我们的预期,那么我们的查找功能就实现完毕啦

8.链式二叉树层序遍历

   接下来的二叉树的层序遍历和判断是否为完全二叉树相对于之前的结构就会难一些,因为这两个接口不能使用递归完成
   首先我们来介绍一下层序遍历,层序遍历就是按层次来访问各个节点,我们举一个例子:
在这里插入图片描述
   在这颗二叉树中,如果我们按照层序遍历打印节点,那么最后结果一定是ABCDEFGH,每个节点都按照对应的层次依次打印,这就是层序遍历
   那么怎么实现层序遍历呢?这里我也不再卖关子了,实现链式二叉树的层序遍历要使用我们之前学过的一个数据结构—队列,接下来我们就先把队列加入我们的项目
   我们可以直接将队列的头文件和实现文件添加到当前目录下,如果不会的话也可以麻烦一点去把之前写过的队列内容拷贝到我们的项目里,这里就不再多说了
   在讲解原理前,我们先把队列里面的数据类型改成我们的二叉树节点指针类型,这样才能让我们的队列存放我们的二叉树节点
   接着我们就来讲解如何使用队列实现层序遍历,方法就是从先让根节点入队列,随后我们就创建一个循环,只要队列不为空我们就循环执行以下的操作
   我们取出当前队列的头节点,然后打印里面的内容,随后将当前这个节点的左右孩子入队列,让当前节点出队列,当然,我们要注意一点的就是,如果当前节点左孩子或者右孩子为空,那么就没有必要入队列了,所以要进行一下判断
   以上就是我们层序遍历的所有思路,可以自行画图理解一下,这里我们直接根据上面的思路来写代码,如下:


//二叉树层序遍历
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%c ", front->data);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}QueuePop(&q);}QueueDestroy(&q);
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到程序确实帮我们按层次打印了各个节点,那么我们的层序遍历就实现完毕啦

9.判断链式二叉树是否为完全二叉树

   首先我们自己要知道如何判断一颗二叉树是不是完全二叉树,判断方法就是,保证除了最后一层以外,其它层次满了,并且保证最后一层的节点是左右依次排列的,这样才是一颗完全二叉树
   那么我们怎么用程序来判断呢?这里我们需要用到上面我们层序遍历的思想,不同的是层序遍历时不把空节点入队列,而我们这里要把空节点也入队列
   因为我们可以将判断一颗二叉树是否为完全二叉树这个问题转化为,层序遍历时遍历到最后一个节点后,接下来队列中的节点只能是空,换句话说,如果在层序遍历时碰到了空节点,那么队列后面就都只能为空,这样才是完全二叉树
   如果在如果在层序遍历时碰到了空节点,但是队列后面有非空节点,说明这颗树就不是完全二叉树,不信的话可以自己画画图,这里就不再画了,我们直接根据这个思路来写代码,判断层序遍历碰到空节点后,队列后面的节点是否都为空,如下:

//是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front == NULL){break;}QueuePush(&q, front->left);QueuePush(&q, front->right);QueuePop(&q);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front){return false;}QueuePop(&q);}QueueDestroy(&q);return true;
}

   我们来看看代码运行结果:
在这里插入图片描述
   可以看到程序成功帮我们判断出当前二叉树是一颗完全二叉树,也可以自行创建一颗非完全二叉树试试,也没有问题

10.链式二叉树的销毁

   由于我们链式二叉树的节点都是动态申请的,所以我们要将它们进行释放,否则会造成内存泄漏,要一个一个地销毁节点,说明我们要遍历整颗二叉树,我们上面学过的遍历方法就起到作用了
   我们就可以直接选择后序遍历来释放节点,先释放左右子树,最后释放掉我们的根节点,并且将根节点置空,如下:

//二叉树的销毁
void BinaryTreeDestroy(BTNode** proot)
{if (*(proot) == NULL){return;}BinaryTreeDestroy(&(*proot)->left);BinaryTreeDestroy(&(*proot)->right);free(*proot);*proot = NULL;
}

三、源码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "Queue.h"typedef char BTDataType;typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL){return;}printf("%c ", root->data);PreOrder(root->left);PreOrder(root->right);
}//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}//二叉树叶子节点个数
int BinaryTreeLeaveSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return BinaryTreeLeaveSize(root->left) + BinaryTreeLeaveSize(root->right);
}//⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->left, k-1) + BinaryTreeLevelKSize(root->right, k-1);
}//二叉树高度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDepth = 1 + BinaryTreeDepth(root->left);int rightDepth = 1 + BinaryTreeDepth(root->right);return leftDepth > rightDepth ? leftDepth : rightDepth;
}//查找值为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;
}//二叉树的销毁
void BinaryTreeDestroy(BTNode** proot)
{if (*(proot) == NULL){return;}BinaryTreeDestroy(&(*proot)->left);BinaryTreeDestroy(&(*proot)->right);free(*proot);*proot = NULL;
}//二叉树层序遍历
void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%c ", front->data);if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}QueuePop(&q);}QueueDestroy(&q);
}//是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front == NULL){break;}QueuePush(&q, front->left);QueuePush(&q, front->right);QueuePop(&q);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);if (front){return false;}QueuePop(&q);}QueueDestroy(&q);return true;
}

   那么今天二叉树链式结构的定义与实现就结束啦,后面我们讲完链式二叉树的一些OJ题后初阶数据结构就差不多结束了,可以进入我们的排序算法了,敬请期待吧!
   bye~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/63082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Pyhton的人脸识别(Python 3.12+face_recognition库)

使用Python进行人脸编码和比较 简介 在这个教程中&#xff0c;我们将学习如何使用Python和face_recognition库来加载图像、提取人脸编码&#xff0c;并比较两个人脸是否相似。face_recognition库是一个强大的工具&#xff0c;它基于dlib的深度学习模型&#xff0c;可以轻松实…

php简单抽奖算法

注意: probability(概率)之和必须100 public function draw(){$goods_list [["name" > "空奖(0-80)","probability" > "80.00"],["name" > "5积分(80-90)","probability" > "10.00&…

nginx4层限速

Nginx的功能概述 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也可以作为邮件代理服务器等。它主要工作在7层&#xff08;应用层&#xff09;&#xff0c;但在某些场景下也可以实现部分4层&#xff08;传输层&#xff09;的功能。 关于4层限速 Nginx自身的限制&#x…

RabbitMq死信队列延迟交换机

架构图 配置 package com.example.demo.config;import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class DeadLetterConfig {public String …

十、JavaScript的应用的习题

题目一 在网页中显示一个工作中的 “ 数字时钟 ”&#xff0c;如图所示 运行效果 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>动态时钟</title><style>.all{width: 660px;height: 350px;margin: 60p…

leetcode 1853 转换日期格式(postgresql)

需求 表: Days ----------------- | Column Name | Type | ----------------- | day | date | ----------------- day 是这个表的主键。 给定一个Days表&#xff0c;请你编写SQL查询语句&#xff0c;将Days表中的每一个日期转化为"day_name, month_name day, year"…

Day26颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地 对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决…

Spark图书数据分析系统 Springboot协同过滤-余弦函数推荐系统 爬虫1万+数据 大屏数据展示 + [手把手视频教程 和 开发文档]

Spark图书数据分析系统 Springboot协同过滤-余弦函数推荐系统 爬虫1万数据 大屏数据展示 [手把手视频教程 和 开发文档] 【亮点功能】 1.SpringbootVueElement-UIMysql前后端分离 2.Echarts图表统计数据, 直观展示数据情况 3.发表评论后&#xff0c;用户可以回复评论, 回复的评…

sram测试注意讨论

常规测试首先是mbist测试&#xff0c;原理不用多说&#xff0c;自己看&#xff0c;主要是注意点和考虑点&#xff1a; 1、明确测试用的到func_clk的频率的大小&#xff0c;根据经验值一般大于800M的时钟需要特别考虑Timing的问题&#xff1a;由于pr摆放的位置原因&#xff0c;…

2025年河北省职业院校技能大赛云计算应用赛项赛题第1套(容器云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

网络——HTTP与HTTPS三次握手和四次挥手

HTTP协议本身并不直接处理TCP连接的建立和关闭&#xff0c;这些是由底层的TCP协议来完成的。但是&#xff0c;由于HTTP通常运行在TCP之上&#xff0c;因此理解TCP的三次握手&#xff08;用于建立连接&#xff09;和四次挥手&#xff08;用于关闭连接&#xff09;对于理解HTTP通…

使用历史索引监控 Elasticsearch 索引生命周期管理

作者&#xff1a;来自 Elastic Stef Nestor 大家好&#xff01;在之前的一篇博客中&#xff0c;我们概述了常见的索引生命周期管理 (index lifecycle management - ILM) 问题及其解决方案。此后&#xff0c;我们已将这些常见场景添加到我们的 Elasticsearch 文档中&#xff0c;…

第 6 章 Java 并发包中锁原理剖析Part one

目录 6.1 LockSupport 工具类 6.2 独占锁 ReentrantLock 的原理 获取锁 1&#xff0e;void lock() 方法 2&#xff0e;void lockInterruptibly() 方法 3&#xff0e;boolean tryLock() 方法 4&#xff0e;boolean tryLock(long timeout, TimeUnit unit) 方法 释放锁 6.1 Lo…

详尽的oracle sql函数

1&#xff0c;CHR 输入整数&#xff0c;返回对应字符。 用法&#xff1a;select chr(65),chr(78) from dual; 2&#xff0c;ASCII 输入字符&#xff0c;返回对应ASCII码。 用法&#xff1a;select ascii(A),ascii(B) from dual; 3&#xff0c;CONCAT 输入两个字符串&#xff0c…

Android 单元测试断言校验方法 org.junit.Assert

判断布尔值 assertTrue assertFalse 判断对象非空 assertNull(object); 案例&#xff1a; PersistableBundle result Util.getCarrierConfig(mockContext, subId);assertNull(result); 判断是否相等 assertEquals("mocked_string", result.toString()); package or…

Django异步视图adrf解决办法

提问 在Django编写异步视图的时候会出现 AssertionError: Expected a Response, HttpResponse or HttpStreamingResponse to be returned from the view 或者 TypeError: sync_to_async can only be applied to sync functions. 诸如此类的错误的时候一般发生在异步视图中…

Python语法之正则表达式详解以及re模块中的常用函数

正则表达式详解及re模块中的常用函数 概念、作用和步骤 概念&#xff1a; 本身也是一个字符串&#xff0c;其中的字符具有特殊含义&#xff0c;将来我们可以根据这个字符串【正则表达式】去处理其他的字符串&#xff0c;比如可以对其他字符串进行匹配&#xff0c;切分&#xf…

算法训练营day28(回溯算法04:复原IP地址,子集,子集2)

28 第七章 回溯算法 ● 93.复原IP地址 ● 78.子集 ● 90.子集II 详细布置 93.复原IP地址 本期本来是很有难度的&#xff0c;不过 大家做完 分割回文串 之后&#xff0c;本题就容易很多了 题目链接/文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9F…

【机器学习02--模型评估】

机器学习 --- 模型评估 你需要得到更好的模型&#xff0c;怎么判断模型更好呢&#xff1f;你需要先得到训练集和测试集&#xff0c;怎么划分它们呢&#xff1f;训练完模型之后&#xff0c;在验证集上测试的时候&#xff0c;用什么指标衡量好坏呢&#xff1f;云里雾里&#xff0…

NPM镜像详解

NPM镜像详解 什么是NPM镜像 NPM镜像&#xff08;NPM Mirror&#xff09;是一个完整的NPM包的副本服务器。由于npm的官方registry服务器部署在国外&#xff0c;国内访问可能会比较慢&#xff0c;因此使用镜像可以加快包的下载速度。 常用的NPM镜像源 npm官方镜像 https://reg…