二叉树遍历 和 线索二叉树

文章目录

    • 1.1 二叉树遍历
      • 1.1 前提
      • 问题1: 什么叫二叉树的遍历?
        • 二叉树的三种遍历:
        • 三个概念:遍历 和 访问 和 经过
        • 重要概念:遍历过程中的经过节点,不代表访问节点
      • 问题2:遍历和访问的联系?
      • 问题3:这个有顺序的编号的意义是什么?
      • 问题4:什么是访问?如何对节点进行访问?
    • 1.2 二叉树遍历代码:二叉树的遍历
      • 1.递归类型的二叉树遍历:
        • 1.先序遍历
        • 2.中序遍历
        • 3.后续遍历
        • 概念一:
        • 概念二:visit()函数是访问当前节点,PreOrder()这个函数是按照先序访问树节点(也称作遍历);
      • 2.非递归类型的二叉树遍历
    • 1.3:通过遍历序列构造二叉树
      • 思路: 通过(先序 + 中序) / ( 后序 + 中序) / ( 层次 + 中序) 三种组合构建二叉树;
      • 其中三种组合特点是 : 有一个遍历能很简单的提供根节点,中序能根据根节点划分左右子树,然后根据中序划分的左右子树的长度,再去找提供根节点那个序列中的左右子树,进行迭代;
    • 2.1 线索二叉树
      • 2.1.1 建立线索二叉树
        • 前提1:线索二叉树说的直接前驱(前驱节点),直接后继(后继节点)是 将二叉树遍历后得到的访问顺序中 每个节点在该顺序中的前后继:
        • 前提2:建立二叉线索树的过程本质是在进行一次 二叉树遍历过程中完成的。
        • 代码讲解:王道配套视频讲解的很好,但是有一些细节视频里面没有细讲。我们通过代码讲解
        • 中序建立线索二叉树
          • 解释:关于先序线索二叉树建立createInthread()函数中的代码{ pre->rchild = null; }解释
        • **先序建立线索二叉树**:
          • 解释:关于先序遍历createInthread()函数中在PreThread()函数执行后 { pre->rchild = null; }能够直接这样设定的原因
          • 解释:关于PreThread()函数中,当我们访问完当前节点我们需要判断当前节点左子树是否是空,才能决定是否让其左子树进行遍历而右子树却不需要判定。
            • 疑问点:当访问节点结束时为什么不用判断该节点右指针指向的是线索还是儿子,只用对当前节点左子树进行判空?
            • 疑问点:为什么只有先序用进行这样的判断:
        • 后序建立线索二叉树
          • 解释:关于{ pre->rchild = null; } 这里我们加了 if(pre - > rtag == 1)判断!!!!
        • pre的改进:
      • 2.1.2 遍历线索二叉树
        • 遍历中序线索二叉树
        • 两个概念:
          • 第一个概念:访问中序线索二叉树的中序序列下的第一个节点:
          • 解释:p->ltag == 0,表示p的左子树非空,所以树p中访问的第一个节点是树p中最左的节点,树p中最左的节点的特征就是p的左子树的左子树的左子树..........中第一个出现p->ltag==1的节点,在众多嵌套左子树中第一个没有左子树的节点。
          • 第二个概念:求中序线索二叉树中节点P在中序序列下的后继:
          • 解释:求当前节点的p的后继节点;
        • 遍历给定根节点的中序线索二叉树按照中序线索遍历二叉树:
        • 解释:我们知道一颗树的根节点的时候遍历树的中序

1.1 二叉树遍历

1.1 前提

二叉树遍历我将围绕诸多概念问题进行展开。

问题1: 什么叫二叉树的遍历?

​ 答: 这个问题看似简单,但实际上有很多坑,我们知道二叉树的遍历分为三种分别是“ 先序遍历”,“中序遍历”。“后续遍历”

​ 这里我再次重申什么叫做二叉树的遍历?请认真思考,或者看书找答案。

二叉树的三种遍历:

​ 先序遍历 : 根遍历 、 左子树遍历、右子树遍历

​ 中序遍历 : 左子树遍历、 根遍历 、右子树遍历

​ 后序遍历 : 左子树遍历、右子树遍历、根遍历

注意:我这里为什么要重复三次遍历?

三个概念:遍历 和 访问 和 经过

​ 遍历是相对于树而言的;

​ 访问是相对于节点和树而言的;

​ 经过也是相对于节点而言的;

重要概念:遍历过程中的经过节点,不代表访问节点

​ 当我用中序来遍历子树时:遍历过程中我正在 访问的是节点是 A节点,但我能不能确定我下一个访问谁?

​ 答:可以,我下一个访问的是A节点的右子树遍历过程中最左的节点!注意是最左的节点,这个概念是不是很少提及,这些过程都是在递归中完成的自然就会被虚化,导致你将遍历中的下一个访问和遍历中当前访问节点的左右子树搞混。 这有点深如探讨了。先不如此深入等我们讲到二叉线索树再继续探讨。

​ 通过这个概念是不是明白了 遍历 访问 经过的概念的细微差别。

问题2:遍历和访问的联系?

​ 遍历二叉树的过程就是访问二叉树每一个节点的过程,且每一个节点只访问一次;

​ 遍历中访问节点的顺序根据二叉树的形态以及你选择怎么样遍历(三种遍历选择)是有关联的;

​ 并且遍历过程中你会得到一串有顺序的二叉树节点的编号。

问题3:这个有顺序的编号的意义是什么?

这个有顺序的编号就是 你访问二叉树节点的先后顺序,注意是访问顺序,要和经过顺序区分开。

问题4:什么是访问?如何对节点进行访问?

其实你获得这串顺序编号就是通过访问操作而来的

假设访问操作是 visit(p) #p是指向被访问的节点

只需要你将visit函数编写为:“输出参数p所指向地址的节点的编号

void visit(二叉树结构的指针类型 p)
{
	printf("%c",p->data)  #我们在遍历中默认:二叉树结构中data存储的是二叉树的编号。
}

通过上述代码你在用遍历过程中根据visit添加位置的不同就能确定二次树的访问顺序

到这里你应该已经能明白大部分的问题了,我们开始通过代码对知识的巩固。

1.2 二叉树遍历代码:二叉树的遍历

1.递归类型的二叉树遍历:

1.先序遍历
void PreOrder(BiTree p)
{
	visit(p);                 # 访问节点p
	PreOrder(p->plchild);     # 遍历节点p的左子树
	PreOrder(p->prchild);     # 遍历节点p的右子树
}
注:BiTree是二叉树的结构体的指针类型,所以p就是二叉树类型的指针。
只有visit(p)操作是在访问p,而其他两个操作相当于在访问p的左右子树。
2.中序遍历
void InOrder(BiTree p)
{
	
	PreOrder(p->plchild);     # 遍历节点p的左子树
	visit(p);                 # 访问节点p
	PreOrder(p->prchild);     # 遍历节点p的右子树
}
注:BiTree是二叉树的结构体的指针类型,所以p就是二叉树类型的指针。
只有visit(p)操作是在访问p,而其他两个操作相当于在访问p的左右子树。
3.后续遍历
void PostOrder(BiTree p)
{
	
	PreOrder(p->plchild);     # 遍历节点p的左子树,也可以理解这一步是在访问该函数p节点之前的那些节点。
	PreOrder(p->prchild);     # 遍历节点p的右子树
	visit(p);                 # 访问节点p
	
}
注:BiTree是二叉树的结构体的指针类型,所以p就是二叉树类型的指针。
只有visit(p)操作是在访问p,而其他两个操作相当于在访问p的左右子树。

通过对三种遍历的代码的书写,是不是就有开始晕递归了!!!!!

哈哈哈,慢慢来,let’s go!!!

概念一:

​ 上述三个遍历如果都没有visit§这个操作是不是都是一样的了

void Order(BiTree p)
{
	
	PreOrder(p->plchild);     # 遍历节点p的左子树
	PreOrder(p->prchild);     # 遍历节点p的右子树	
}

​ 这个过程是不是就是但单纯的**”经过“一遍二叉树,注意这里是经过,单单是经过的顺序是和先序遍历是一样的**。

​ 然而我们的编写 先序、中序、后续其实并没有”节点改变“经过顺序,而是通过对**“节点访问” 进行了限定**,此时访问对象被分为三个对象:当前树的根节点,当前树左子树、当前树的右子树。

​ 当我们想先按照先序访问:我们就把访问根节点放在访问另外两位的前面就会确保当前这颗树的三个访问对象按照先序的设定进行访问,注意此时我们把访问左右子树分别看作一个整体。然而左子树右子树都是我们访问这个根节点所在的二叉树是一个结构,所以让左子树和右子树上的节点进行了相同模式的访问。

概念二:visit()函数是访问当前节点,PreOrder()这个函数是按照先序访问树节点(也称作遍历);

两个操作都是访问 一个是对节点的访问,一个是对树的访问(也叫遍历)

2.非递归类型的二叉树遍历

​ 其实非递归类型的二叉树遍历就是线索二叉树,我们从根节点按照先序(中序/后序)一直找当前访问节点的后继的过程,然后将其进行数学归纳后的结果。

这里我感觉王道书上也写的比较笼统,能看懂书上分析的逻辑,但是要按照线索二叉树求节点后继的那种方法去做才能理解更加透彻。

1.3:通过遍历序列构造二叉树

思路: 通过(先序 + 中序) / ( 后序 + 中序) / ( 层次 + 中序) 三种组合构建二叉树;

其中三种组合特点是 : 有一个遍历能很简单的提供根节点,中序能根据根节点划分左右子树,然后根据中序划分的左右子树的长度,再去找提供根节点那个序列中的左右子树,进行迭代;

如下图是先序:

在这里插入图片描述

2.1 线索二叉树

2.1.1 建立线索二叉树

通过学习线索二叉树我得到的一些感悟:

前提1:线索二叉树说的直接前驱(前驱节点),直接后继(后继节点)是 将二叉树遍历后得到的访问顺序中 每个节点在该顺序中的前后继:

例如 中序遍历二叉树得到一个访问顺序 : A S D F G H J

则G的前驱是F,G的后继是H。

这个前驱和后继 就是访问过程中该点之前访问的节点和该店之后要访问的节点。

前提2:建立二叉线索树的过程本质是在进行一次 二叉树遍历过程中完成的。

​ 中序线索二叉树建立就是进行中序遍历节点过程中建立线索

​ 先序线索二叉树建立就是进行先序遍历节点过程中建立线索

​ 后序线索二叉树建立就是进行后序遍历节点过程中建立线索

代码讲解:王道配套视频讲解的很好,但是有一些细节视频里面没有细讲。我们通过代码讲解
中序建立线索二叉树
中序遍历根据二叉树线索化的条件对访问操作的改进

注:这里pre和p传进来的是引用。
void InThread(ThreadTree &p,ThreadTree &pre)
{

	if(P!=null)
	{
		InThread(p-lchild,pre)   这里相当于访问当前节点前的所以节点
		
		{
		#这里面的代码相当于中序遍历的 visit()
		我们在遍历的过程中进行了建立线索的进行的操作如下;
		1:当“真在遍历节点的左子树为空我们将他的左指针指向刚刚访问过的那个节点”
		2:如果正在访问节点的前一个节点不为空,且前一个节点的右子树为空,就让前一个节点的右子树指针指向当前节点。
		3:当前节点访问操作以及实现,但是要将当前节点变成 下一个访问的节点的上一个节点,也就是将p赋值给pre指针。
			if(p->lclild==null)
			{
				p -> lchild = pre;
				p -> ltag = 1;
			}
		
			if(pre->rclild==null && pre!= null )
            {
                pre -> rchild = p;
                pre -> rtag = 1;
            }
            将当前节点设置为前
            pre = p;
		}
		
        Inthread(p->rchild,pre)  这里相当于访问当前节点前的所以节点
	}

}
void createInthread(ThreadTree T)
{	
	ThreadTree pre = null;
	if(t!=null)
	{	
		InThread(T,pre);
		{
			#注意我这是在这里加了代码块{},并没有递进结构
			pre->rchild = null;
			pre - rtag=1;
			#这一块代码有讲究:
				我们这里是中序遍历,InThread结束后 p是指向null的,pre指向最后一个被访问的节点;
				为什么能直接最后一个将其设置为空 而且 将rtag设置为1(表明最后一个几点没有右子树)
                证明:当pre指向最后一个节点时,根据中序遍历的特点,若此节点在树结构中后有右子树,则这个节点一定不是最后一个节点,最后一个访问的节点应该在这个节点的右子树,因为题目中中序已经访问完毕,所以pre指向一定是最后一个节点,所以这个节点的右子树一定是空,也即是说p要指向空,从而根据线索化中的设定,当pre的右子树为空让pre的右子树等于p,所以才有pre->rchild = null(p)		
       }
		
	}


}

这里我们有一个点需要证明解释一下:为什么在createInthread()函数中,InThread()函数执行后我们直接将pre当前指向的节点的右孩子指向了空?

解释:关于先序线索二叉树建立createInthread()函数中的代码{ pre->rchild = null; }解释

问:为什么能直接最后一个节点的右指针设置为空,且将rtag设为1?

​ 第一步:我们从执行的角度分析:当结束InThread()函数执行结束后的pre指针是指向最后一个节点的,这里是无可置疑的,既然pre是指向最后一个遍历的节点所以最后一个节点的后边是空节点,因此 p 也就一定指向null。

​ 第二步:我们分析中序遍历:中序遍历是先遍历左子树,再遍历根节点,再遍历右子树,如果最后一个节点有右子树的情况下,这个节点能成为最后一个被遍历的节点?这里很容易理解当存在右子树是时,该节点是不能成为最后一个节点的,所有我们能够证明中序遍历最后一个节点一定是没有右子树的,所有我们可以直接将pre所指向的节点的右子树指向空。

​ 第三步:我们将pre设置为空,其实就是将他线索到p了,所以将rtag设为1

当我们用先序和后续建立线索二叉树有什么区别?

先序建立线索二叉树

1:则需要更改OrdThread()遍历二叉树函数中 遍历当前节点和左右子树对象的顺序,代码如下

注:这里pre和p传进来的是引用。
void PreThread(ThreadTree &p,ThreadTree &pre)
{

	if(P!=null)
	{
		{
		#这里面的代码相当于先序遍历的 visit()
		我们在遍历的过程中进行了建立线索的进行的操作如下;
		1:当“真在遍历节点的左子树为空我们将他的左指针指向刚刚访问过的那个节点”
		2:如果正在访问节点的前一个节点不为空,且前一个节点的右子树为空,就让前一个节点的右子树指针指向当前节点。
		3:当前节点访问操作以及实现,但是要将当前节点变成 下一个访问的节点的上一个节点,也就是将p赋值给pre指针。
			if(p->lclild==null)
			{
				p -> lchild = pre;
				p -> ltag = 1;
			}
		
			if(pre->rclild==null && pre!= null )
            {
                pre -> rchild = p;
                pre -> rtag = 1;
            }
            将当前节点设置为前
            pre = p;
		}
		#这里我们明白这些子树的访问都是在该节点之后进行的
		*******重点,讲解在下边
		if(p-ltag==0)
		{
			InThread(p-lchild,pre)   这里相当于访问当前节点左子树的所有节点
		}
		
		
        Inthread(p->rchild,pre)  这里相当于访问当前节点右子树的所以节点
	}

}

2:修改createInthread()函数,因为createtthread()中{ pre->rchild = null; }这个步骤是因为中序遍历的特点才能这样直接写,先序遍历我们没有证明,其实先序也是可以这样写,如下证明:

void createInthread(ThreadTree T)
{	
	ThreadTree pre = null;
	if(t!=null)
	{	
		PreThread(T,pre);
		{
			pre->rchild = null;
			pre - rtag=1;
       }
	}
}
解释:关于先序遍历createInthread()函数中在PreThread()函数执行后 { pre->rchild = null; }能够直接这样设定的原因

​ 我们这里是先序遍历,OrdThread结束后 p是指向null的,pre指向最后一个被访问的节点;

问:为什么能直接最后一个节点的右指针设置为空,且将rtag设为1?

​ 第一步:我们从执行的角度分析:当结束OrdThread()函数后的,pre指针是指向最后一个节点的,这里是无可置疑的,既然pre是指向最后一个遍历的节点所以最后一个节点的后边是空节点,因此 p 也就一定指向null。

​ 第二步:我们分析先序遍历:先序遍历是先遍历根节点,在遍历它的两个子树,如果最后一个节点有右子树的情况下,这个节点能成为最后一个被遍历的节点?这里很容易理解是不能的,所有我们能够证明先序遍历最后一个节点一定是没有子孙(包括没有右子树)的,所有我们可以直接将pre所指向的节点的右子树指向空。

​ 第三步:我们将pre设置为空,其实就是将他线索到p了,所以将rtag设为1

解释:关于PreThread()函数中,当我们访问完当前节点我们需要判断当前节点左子树是否是空,才能决定是否让其左子树进行遍历而右子树却不需要判定。

​ 前提:在线索化过程中 ,每一次访问节点时,只会对当前被访问节点的左子树指针进行线索化 和 被访问节点的前一个节点的右子树指针进行线索化,当前访问的节点的右子树指针是没有发生改变的。

​ 关于这个点王道视频讲的很清楚:我们用图和文字也进行解释如下:

在这里插入图片描述

用图举例,如果我们当前访问的是D节点时:

​ 我们会进行两步操作:第一:判断D节点的左子树是否为空,当D节点左子树为空时,将D节点的左指针线索化到pre所指向的B节点。

​ 第二:判断D节点的前一个节点B的右子树是否为空,若B节点右子树为空时,我们将其右子树线索化到D节点。

​ 当访问完D节点,正常我们该进行PreThread(p->lchild);

​ 于是变引发了问题! 如上图:当我们进行PreThread(p->lchild); 此时D节点的左子树指向B节点,当对B节点进行访问,当我们访问完B节点就开始对他的左子树进行访问,我们就又回到了D节点,从而无线循环下去,所以我们限定:只有当被访问节点左子树不空的时候我们才对其访问也就是ltag==0时。

疑问点:当访问节点结束时为什么不用判断该节点右指针指向的是线索还是儿子,只用对当前节点左子树进行判空?

​ 答:根据前提可知,我们访问当前节点时并不会对右指针进行线索化,所以不会造成这样的问题。

疑问点:为什么只有先序用进行这样的判断:

​ 答:因为只有先序遍历,在根节点访问完成后会去访问左子树,而中序和后序在根节点访问完成后不会对其左子树访问,因此不会造成这种情况。

后序建立线索二叉树

后序建立线索二叉树的遍历过程到这里应该能够很简单是理解

注:这里pre和p传进来的是引用。
void OrdThread(ThreadTree &p,ThreadTree &pre)
{

	if(P!=null)
	{
		#这里我们明白这些子树的访问都是在该节点之前进行的
		InThread(p-lchild,pre)   这里相当于访问当前节点左子树的所有节点
		
        Inthread(p->rchild,pre)  这里相当于访问当前节点右子树的所以节点
        
        {
		#这里面的代码相当于先序遍历的 visit()
		我们在遍历的过程中进行了建立线索的进行的操作如下;
		1:当“真在遍历节点的左子树为空我们将他的左指针指向刚刚访问过的那个节点”
		2:如果正在访问节点的前一个节点不为空,且前一个节点的右子树为空,就让前一个节点的右子树指针指向当前节点。
		3:当前节点访问操作以及实现,但是要将当前节点变成 下一个访问的节点的上一个节点,也就是将p赋值给pre指针。
			if(p->lclild==null)
			{
				p -> lchild = pre;
				p -> ltag = 1;
			}
		
			if(pre->rclild==null && pre!= null )
            {
                pre -> rchild = p;
                pre -> rtag = 1;
            }
            将当前节点设置为前
            pre = p;
		}
	}

}

2:修改createInthread()函数,因为createtthread()中{ pre->rchild = null; }这个步骤在后序中是不能直接使用的了,前边通过先序和中序的证明,也能够大致理解,如下证明后序线索二叉树:

void createInthread(ThreadTree T)
{	
	ThreadTree pre = null;
	if(T!=null)
	{	
		PostThread(T,pre);
		if(pre - > rtag == 1)
   	 	{
            pre->rchild = null;
       	}
	}
}
解释:关于{ pre->rchild = null; } 这里我们加了 if(pre - > rtag == 1)判断!!!!

问:为什么后序不能呢?

​ 第一步:我们从执行的角度分析:当结束PostThread()函数执行后,pre指针是指向最后一个节点的,这里是无可置疑的,既然pre是指向最后一个遍历的节点所以最后一个节点的后边是空节点,因此 p 也就一定指向null。

​ 第二步:我们分析后序遍历:后序遍历是先遍历两个子树,再遍历根节点,如果最后一个节点有右子树的情况下,这个节点能成为最后一个被遍历的节点?这里很容易理解是可以的,无论有没有右子树后序遍历时我都是最后一个,所以我们需要通过最后一个节点的右子树被线索化的标注rtag来进行判断是否根节点右子树为空。

​ 第三步:当右子树为空时,pre->rtag 是等于1的,此时我们将pre指向的最后一个节点设置为空,将他线索化

pre的改进:

​ 我们发现,当我们遍历二叉树的时候pre一直是一个引用的存在,引用的意思可以理解为实参的别名,和实参实际是一个变量,所以这里pre做形参和实参表示相同含义,所以可以直接将pre声明为全局变量并初始化赋值为null。

2.1.2 遍历线索二叉树

​ 这一部分王道讲的很好,我只对中序进行讲解:

遍历中序线索二叉树
两个概念:
第一个概念:访问中序线索二叉树的中序序列下的第一个节点:

            ThreadNode *Firstnode(ThreadNode *p)   
            {	
                while(p->ltag == 0) p=p->lchild;  #
                return p;
            }
解释:p->ltag == 0,表示p的左子树非空,所以树p中访问的第一个节点是树p中最左的节点,树p中最左的节点的特征就是p的左子树的左子树的左子树…中第一个出现p->ltag==1的节点,在众多嵌套左子树中第一个没有左子树的节点。
第二个概念:求中序线索二叉树中节点P在中序序列下的后继:
            ThreadNode *Nextnode(ThreadNode *p)
            {
                if(p->rtag==0) return Firstnode(p->plchild);
                else return p->rchild;
            }
解释:求当前节点的p的后继节点;

​ 当他右子树不空的时候就找他右子树中最左的节点,也就是 他右子树的左子树的左子树的左子树…n层嵌套左子树中第一个左子树为空的左子树,刚好Firstnode()函数就是在做这个操作。

​ 当他右子树空的时候,rchild就是代表指向后继的线索。

遍历给定根节点的中序线索二叉树按照中序线索遍历二叉树:
            void Inorder(ThreadNode *T)
            {
                for(ThreadNode *p = Firstode(t);p!=null;p=Nextnode(p))	
                        visit(p);
            }
解释:我们知道一颗树的根节点的时候遍历树的中序

​ 就是先找到这个树的第一个被访问的节点然后,然后对第一个访问到的节点求该节点后继的过程,以及求该节点后继的后继的过程,从而就可以用for循环加上上边的两个概念实现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用conda进行R的安装

1.miniconda3的安装 官网:Miniconda — Conda documentation 找到对应系统、Python版本下载 wget https://mirrors.ustc.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh #wget -c https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x…

Python项目代码太多if-else? 这样优化才优雅!

前言 代码中不可避免地会出现复杂的if-else条件逻辑,而简化这些条件表达式是一种提高代码可读性极为实用的技巧。 在 Python 中,有多种方法可以避免复杂的 if-else 条件逻辑,使代码更加清晰和易于维护。 筑基期 提前 return,去掉多余的 else 在 Python 中,使用"提…

C++基础编程100题-003 OpenJudge-1.1-05 输出保留12位小数的浮点数

更多资源请关注纽扣编程微信公众号 http://noi.openjudge.cn/ch0101/05/ 描述 读入一个双精度浮点数,保留12位小数,输出这个浮点数。 输入 只有一行,一个双精度浮点数。 输出 也只有一行,保留12位小数的浮点数。 样例输入…

Day 42 LVS四层负载均衡

一:负载均衡简介 1.集群是什么 ​ 集群(cluster)技术是一种较新的技术,通过集群技术,可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益,其任务调度则是集群系统中的核心技术 …

LangChain学习之 Question And Answer的操作

1. 学习背景 在LangChain for LLM应用程序开发中课程中,学习了LangChain框架扩展应用程序开发中语言模型的用例和功能的基本技能,遂做整理为后面的应用做准备。视频地址:基于LangChain的大语言模型应用开发构建和评估。 2. Q&A的作用 …

web刷题记录(2)

[鹤城杯 2021]EasyP 就是php的代码审计 从中可以看出来,就是对四个if语句的绕过,然后过滤了一些语句 代码分析: 通过include utils.php;导入了一个叫做"utils.php"的文件,这意味着在该文件中可能定义了一些与本代码相…

通信协议:常见的芯片间通信协议

相关阅读 通信协议https://blog.csdn.net/weixin_45791458/category_12452508.html?spm1001.2014.3001.5482 本文将简单介绍一些常见的芯片间通信协议,但不会涉及到协议的具体细节。首先说明,芯片间通信方式根据通信时钟的区别可以分为:异步…

计算机网络ppt和课后题总结(上)

试在下列条件下比较电路交换和分组交换。要传送的报文共 x(bit)。从源点到终点共经过 k 段链路,每段链路的传播时延为 d(s),数据率为 b(b/s)。在电路交换时电路的建立时间为 s(s)。在分组交换时分组长度为 p(bit),且各结点的排队等待时间可忽…

基于YOLOv7的口罩检测

目录 1. 作者介绍2. YOLOv7网络模型2.1 算法简介2.2 数据集介绍2.3 YOLO格式数据集制作 3. 代码实现3.1 分割数据集3.2 修改数据配置文件3.3 修改训练代码,进行训练3.4 修改测试代码,进行测试3.5 检测结果 1. 作者介绍 曹宇欢,女&#xff0c…

跨越国界, 纷享销客助力企业全球业务增长

出海,已不再是企业的“备胎”,而是必须面对的“大考”!在这个全球化的大潮中,有的企业乘风破浪,勇攀高峰,也有的企业在异国他乡遭遇了“水土不服”。 面对“要么出海,要么出局”的抉择&#xff…

盲盒风尚:拆盒吧引领新潮消费趋势

在当下这个快速变化的消费时代,拆盒吧以其独特的盲盒经济模式,成为了新潮文化消费的引领者。不同于传统的购物方式,拆盒吧通过创新的玩法和多元化的产品线,为消费者带来了前所未有的购物体验。 一、拆盒吧:解锁盲盒新玩…

现代密码学-国密算法

商用密码算法种类 商用密码算法 密码学概念、协议与算法之间的依赖关系 数字签名、证书-公钥密码、散列类算法 消息验证码-对称密码 ,散列类 安全目标与算法之间的关系 机密性--对称密码、公钥密码 完整性--散列类算法 可用性--散列类、公钥密码 真实性--公…

数据结构之初始泛型

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:数据结构(Java版) 目录 深入了解包装类 包装类的由来 装箱与拆箱 面试题 泛型 泛型的语法与使用…

可长期操作的赚钱项目,时间自由,但不适合大学生

如何评价现在的csgo市场? 可长期操作的赚钱项目,时间自由,但不适合大学生。 都在问,有哪些可以长期做下去的赚钱项目,童话就不拐弯抹角了,csgo/steam游戏搬砖一定是最适合长期做下去的赚钱项目。 不说别人…

CondaSSLError: OpenSSL appears to be unavailable on this machine.

conda create -n x1 python3.7报错 PS C:\Users\Richardo.M.Song\Desktop\lele_seg\x1> conda create -n x1 python3.7 Collecting package metadata (current_repodata.json): failed CondaSSLError: OpenSSL appears to be unavailable on this machine. OpenSSL is requ…

Varnish讲解文章、缓存代理配置、核心功能、优势、Varnish在什么情况下会选择缓存哪些类型的内容、Varnish如何实现负载均衡功能?

varnish官网链接 Varnish文章概览 Varnish是一款高性能的HTTP加速器(web应用加速器),是一款开源软件,它能够显著提高网站的响应速度和减少服务器的负载。Varnish的设计理念是利用缓存技术,将频繁访问的静态内容存储在…

【Python】 探索Pytz库中的时区列表

基本原理 在Python中,处理时区是一个常见但复杂的问题。pytz是一个Python库,它提供了对时区的精确和丰富的支持。pytz库是datetime模块的补充,它允许更准确地处理时区信息。pytz库包括了IANA时区数据库,这个数据库包含了全球的时…

13-至少有5名直接下属的经理(高频 SQL 50 题基础版)

13-至少有5名直接下属的经理 select name from Employee where id in (select managerId -- 查找大于5的经理idfrom Employeegroup by managerId -- 根据id分组having count(*)>5); -- 根据分组的数据进行求个数

小白级教程—安装Ubuntu 20.04 LTS服务器

下载 本教程将使用20.04版进行教学 由于官方速度可能有点慢,可以下方的使用清华镜像下载 https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/ 点击20.24版本 选择 ubuntu-20.04.6-live-server-amd64.iso 新建虚拟机 下载好后 我们使用 VMware 打开它 这里选…

使用Python和wxPython将PNG文件转换为JPEG文件

简介: 在图像处理中,有时候我们需要将PNG格式的图像文件转换为JPEG格式。本篇博客将介绍如何使用Python编程语言和wxPython图形用户界面库,以及Pillow图像处理库来实现这一转换过程。通过本文的指导,您将学习如何快速将指定文件夹…