数据结构笔记(其七)--树(二叉树)

news2024/11/26 11:13:03

目录

1.知识总览

2.二叉树的基本概念

        (1).满二叉树

        (2).完全二叉树 

        (3).二叉排序树

        (4).平衡二叉树 

3.二叉树常考点

        i.叶子结点与二分支结点的数量关系

        ii.第i 层的最多结点数(i >= 1)

        iii.高度为h 的二叉树的最多结点数

        iiii.完全二叉树:已知有 n 个结点,求高度

        iiiii.完全二叉树:已知有 n 个结点,求度为0、1、2的结点个数(n0、n1、n2)

4.二叉树的存储结构

        (1).顺序存储

        (2).链式存储

5.二叉树的遍历

        (1).前/中/后序遍历

        i.代码

        (2).层次遍历

        i.代码

        (3).由序列推出二叉树原图

6.线索二叉树

        i.手算

        ii.机算(二叉树的线索化) 

7.线索二叉树的后继、前驱检索

        · 中序(左 根 右)

        (1).找后继

        (2).找前驱 

        · 先序(根 左 右)

        (1).找后继

        (2).找前驱

        · 后序(左 右 根)

        (1).找后继

        (2).找前驱


1.知识总览

ca3874327a93472a8c0919d04920d3e1.png

2.二叉树的基本概念

        二叉树是n个结点的有限集合:

        · n == 0时,为空二叉树。

        · n != 0时,由一个根节点和两个互不相交的被称为根的左右子树组成, 左右子树又分别时一棵二叉树。

c8bdd372da274d5196888bf2dc92401f.png

         特点

        · 每个结点至多有两棵子树。

        · 左右子树不可颠倒位置,即二叉树是有序树。(注:与度为2的有序树做区分)

417a7c2ea0544839bd02ac0387900a6b.png

        (1).满二叉树

        · 高度为h,则含有2^h-1个结点。

        · 叶子结点只存在于最后一层。

        · 所有结点的度,要么为2,要么为0.

        · 按层序从1开始编号,结点i 的左孩子为2i,右孩子为2i+1,父节点为 i/2向下取整(如果存在)

952b2d13bb4b42839ea7098d40ae4475.pnga58583f1d4c3497395ad9a2a79cfa9f1.png

        (2).完全二叉树 

        当前树(未必是满的)的编号与对应的满二叉树的编号一一对应。

        假如当前二叉树与上图中的满二叉树对应,但缺少13号结点,按一般编码习惯,14号的名字要修改为13号,15号改为14号,此时,就不能被称为是完全二叉树,因为编号不对应了。完全二叉树,若缺少结点,则只可从最后的结点开始,按序缺失,保证编码统一。

        定义完全二叉树除了最后一层外,其他的每一层都会被填满,即从下往上,按序号由大到小删除,所以,上图的7号结点不能被直接删除,但14、15可以被删除,此时7号结点也成了叶子结点。

        特点·总结

        · 叶子结点只会出现在最后两层。

        · 最多只能有一个度为1的结点。

        · 按层序从1开始编号,结点i 的左孩子为2i,右孩子为2i+1,父节点为 i/2向下取整(如果存在)(与满二叉树相同)

        (3).二叉排序树

        左子树永远小于根节点。

        右子树永远大于根节点。

        (其中的子树也遵循以上原则)

        常用于元素的排序与搜索。

7436d57633c6434b81cc684670fa1b1a.png

        (4).平衡二叉树 

        树上任意结点的左右子树的深度之差不超过1.

        具有更高效的搜索效率。

0146076624af415ea8a75cb95fc8903a.png

3.二叉树常考点

        i.叶子结点与二分支结点的数量关系

        非空二叉树中度为0、1、2 的结点数分别有n0、n1、n2,则有

        n0 = n2 + 1 //叶子结点数 = 二分支结点数 + 1

        ii.第i 层的最多结点数(i >= 1)

        在上一章对一般树的介绍中,就有提到该类问题的公式,m^(i-1).

        二叉树第i 层至多有 2^(i-1) 个结点

        iii.高度为h 的二叉树的最多结点数

        当结点最多时,此二叉树就是满二叉树,结点数为 2^h - 1个。

        iiii.完全二叉树:已知有 n 个结点,求高度

8961b91fea79448689245274dde9b71b.png

        iiiii.完全二叉树:已知有 n 个结点,求度为0、1、2的结点个数(n0、n1、n2)

49826d998e1d4b659e7f444a3aed9384.png

4.二叉树的存储结构

        (1).顺序存储

        

#define MaxSize 100
//顺序存储
class TreeNode
{
public:
	int data;			//储存数据
	bool isEmpty;		//标记是否为空	//初始化是需要遍历各结点,将之设为true
};
TreeNode t[MaxSize];	//定义一个长度为MaxSize的数组t,依据二叉树从上至下、从左至右的顺序依次存放数据

        定义一个长度为MaxSize的数组t,依据二叉树从上至下、从左至右的顺序依次存放数据。

        一般,数组的t[0] 是废弃不用的,以保证结点编号于数组编号一致。

7fe6f93f6e3c4f43a0a7f48fab73c2cd.png

        注:如果二叉树的形式不是完全二叉树,那么需要我们人为的将结点的编号转化为对应的完全二叉树的编号,再储存在于编号对应的数组位置。

2eca3b646b524469a23702d2fb7425b5.png

981d9c7aee52489896ffa5203affec2d.png

        在非完全二叉树中,判断是否有左孩子之类的问题,无法只依靠完全二叉树的公式来计算,需要对通过公式得到的结点,进行进一步的判空操作(isEmpty)。

        对于顺序存储,适合存完全二叉树,非完全的存储会浪费大量的空间。实际上,很少用顺序存储来储存二叉树。

        (2).链式存储

//链式存储
class BiTNode
{
public:
	int data;					//数据域
	BiTNode* lchild, * rchild;	//指针域,左右孩子指针
    //BiTNode* parent;          //如果该二叉树需要频繁获取父节点,则可再引入一个父节点的指针
};
using BiTree = BiTNode*;

        设一个二叉树有n个结点,则有2n个指针域,易知,会有n - 1个结点头上有一个指针指向它,所以,会存在n+1(2n-(n-1))个指针置空。 (这些空指针会用来构造线索二叉树)

5.二叉树的遍历

        (1).前/中/后序遍历

        设定一个二叉树(含有一个根节点、一个左子树、一个右子树),其结点间的遍历顺序如下,

        前序:根左右。(或称,先序)

        中序:左根右。

        后序:左右根。

04d1f414ac7d40a48e1811c954b27e6f.png

        对于上图的各结点访问循序,

        前序:A BDE CFG

        中序:DBE A FCG

        后序:DEB FGC A

        在用二叉树对算数表达式的分析中,先序——前缀表达式,中序——中缀表达式(需要认人为的添加部分括号),后序——后缀表达式

        i.代码

        这三序遍历的代码都很简单,都是递归

void visit(BiTNode* T)
{
	cout << T->data << endl;
}
//前序
void PreOrder(BiTree T)
{
	if (T != nullptr)
	{
		visit(T);				//访问根节点			
		PreOrder(T->lchild);	//递归遍历左子树
		PreOrder(T->rchild);	//递归遍历右子树
	}
}
//中序
void InOrder(BiTree T)
{
	if (T != nullptr)
	{
		InOrder(T->lchild);
		visit(T);
		InOrder(T->rchild);
	}
}
//后序
void PostOrder(BiTree T)
{
	if (T != nullptr)
	{
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		visit(T);
	}
}

        (2).层次遍历

        · 初始化一个辅助队列。

        · 先将根节点入队。

        · 若队列非空,则队头出队,并访问出队结点,同时,将其左右孩子(与该结点直接相连的两个结点)插入队列(此时,无需因非空,而发生再次的队头出队)。

        · 再次判断队列是否为空,重复第三步操作,直到队列为空为止。

        i.代码

//链式存储
class BiTNode
{
public:
	int data;					//数据域
	BiTNode* lchild, * rchild;	//指针域,左右孩子指针
};
using BiTree = BiTNode*;
//访问结点
void visit(BiTree T)
{
	cout << T->data << endl;
}
//链式队列结点
class LinkNode
{
public:
	BiTNode* data;
	LinkNode* next;
};
//链式队列
class LinkQueue
{
public:
	LinkNode* front, * rear;
};

//初始化(带头结点)
void InitQueue(LinkQueue& Q)
{
	Q.front = Q.rear = new LinkNode();
	Q.front->next = nullptr;
}
//判空(带头结点)
bool QueueEmpty(LinkQueue& Q)
{
	if (Q.front == Q.rear)
		return true;
	return false;
}
//入队(带头结点)(只能尾插)
bool EnQueue(LinkQueue& Q, BiTree x)
{
	LinkNode* p = new LinkNode();
	p->data = x;
	p->next = nullptr;
	Q.rear->next = p;
	Q.rear = p;
	return true;
}
//出队(带头结点)
bool DeQueue(LinkQueue& Q, BiTNode* x)
{
	if (Q.front == Q.rear)
		return false;
	LinkNode* p = Q.front->next;		//设置一个临时结点,指向队头
	x = p->data;
	Q.front->next = p->next;			//绕开队头结点
	if (Q.rear == p)					//如果队头结点就是队尾结点,那么还需要更新rear 指针
		Q.rear = Q.front;
	free(p);
	return true;
}

//链式队列不会有队满情况
//打印全队(带头结点)
bool PrintLQueue(LinkQueue Q)
{
	if (QueueEmpty(Q))
		return false;
	LinkNode* p = Q.front->next;
	int i = 1;
	while (1)
	{
		cout << "第" << i << "个元素:" << p->data << endl;
		if (p == Q.rear)
			break;
		p = p->next;
		i++;
	}
}
//层次遍历
void LevelOrder(BiTree T)
{
	LinkQueue Q;
	InitQueue(Q);					//初始化辅助队列
	BiTree p;
	EnQueue(Q, T);					//根节点入队
	while (!QueueEmpty(Q))			//队列不空,就循环
	{
		DeQueue(Q, p);				//队头结点出队
		visit(p);					//访问队头结点
		if (p->lchild != nullptr)
			EnQueue(Q, p->lchild);	//左孩子入队
		if (p->rchild != nullptr)
			EnQueue(Q, p->rchild);	//右孩子入队
	}
}

        (3).由序列推出二叉树原图

        单一序列(前中后序,层序),可以堆出多种二叉树,但两种序列(其中必须有一个是中序序列)作用在一起,就可以推出唯一的序列。

        推到的核心思路,根据前或后或层序,先判断出根节点,再根据中序得到左右子树,。

6.线索二叉树

        线索二叉树,其核心作用就是寻找一个结点的在序列中的前驱、后继。

        在一个正常二叉树中,其叶子结点的左右孩子指针必然是置空的,而线索二叉树就是利用这些置空的指针创建的。

        i.手算

        构造思路:

        首先,对二叉树进行排序(前序、中序、后序)。

        然后,根据序列生成双向链表。

        如此,就得到了相应序列的线索二叉树。(生成的链表叫线索链表)

        存储结构:

        在二叉树中,有些结点其左右孩子不是置空的,其左右孩子指针无法利用,需要进行标记。

        一般引入两个tag,标记左右指针是否指示线索,tag == 0 指向左右孩子;tag == 1 指向线索。

//线索二叉树
class ThreadNode
{
public:
	int data;					//数据域
	ThreadNode* lchild, * rchild;	//指针域,左右孩子指针
	int ltag, rtag;				//标记左右指针是否指示线索,初始化时要进行处理
};
using ThreadTree = ThreadNode*;

        ii.机算(二叉树的线索化) 

        普通方法寻找中序二叉树前驱:

//二叉树
class BiTNode
{
public:
	int data;					//数据域
	BiTNode* lchild, * rchild;	//指针域,左右孩子指针
};
using BiTree = BiTNode*;

//辅助全局变量
BiTNode* p;					//指向指定的结点
BiTNode* pre = nullptr;		//指向当前访问结点的前驱
BiTNode* final = nullptr;	//指向最终需要的前驱结点

//访问结点
void visit(BiTNode* q)
{
	if (q == p)				//当访问节点为目标结点时
		final = pre;		//直接获得前驱
	else
		pre = q;			//否则,将前驱移动到下一位
}
//中序
void InOrder(BiTree T)
{
	if (T != nullptr)
	{
		InOrder(T->lchild);
		visit(T);
		InOrder(T->rchild);
	}
}

        中序线索化:

//线索二叉树
class ThreadNode
{
public:
	int data;					    //数据域
	ThreadNode* lchild, * rchild;	//指针域,左右孩子指针
	int ltag, rtag;				    //标记左右孩子是否存在,初始化时要进行处理
};
using ThreadTree = ThreadNode*;

ThreadNode* pre = nullptr;		    //指向当前访问结点的前驱

//访问结点
void visitThread(ThreadNode* q)
{
	if (q->lchild == nullptr)						//左子树为空,建立前驱线索
	{
		q->lchild = pre;
		q->ltag = 1;
	}
	if (pre != nullptr && pre->rchild == nullptr)	//建立前驱结点的后继
	{
		pre->rchild = q;
		pre->rtag = 1;
	}
	pre = q;
}

//中序遍历
void InThread(ThreadTree T)
{
	if (T != nullptr)
	{
		InThread(T->lchild);
		visitThread(T);
		InThread(T->rchild);
	}
}

//中序线索化
void CreatInThread(ThreadTree T)
{
	pre = nullptr;					//pre初始化为null
	if (T != nullptr)				//非空二叉树才能线索化
	{
		InThread(T);				//中序线索化二叉树
		if (pre->rchild == nullptr)
		{
			pre->rtag = 1;			//最后一个结点的rtag 要设置为1
		}
	}
}

        注:在先序线索化中,需要对先序遍历进行一点处理,如下

//先序遍历
void InThread(ThreadTree T)
{
	if (T != nullptr)
	{
		visitThread(T);
        //需要确保左孩子不是线索,因为visit之后,当前节点的左孩子可能指向其父节点,若无该判断可能会出现循环
		if(T->ltag == 0)
			InThread(T->lchild);
		InThread(T->rchild);
	}
}

7.线索二叉树的后继、前驱检索

        在下文的前驱、后继的搜索中,会由于序列排序的特性,使得在遍历中需要用到三叉树(或是土方法,全部遍历一次找到所需节点)。

        检索时,默认p 作为子树根节点,观察p 为首的子树以及其兄弟子树。

        · 中序(左 根 右)

        (1).找后继

        以中序为例,对于一个结点,找其后继,

        · 先看右孩子,若右孩子为线索(rtag == 1) ,则可直接根据线索找到后继;若为结点(rtag == 0),考虑中序排列的特性左 根 (左 根 右),很显然,其后继就是右子树的左分支中最左下角的结点。

        · 代码如下,

//寻找以p 为根的子树中,第一个被中序遍历的结点(即,左分支最左下角的结点)
ThreadNode* Firstnode(ThreadNode* p)
{
	while (p->ltag == 0)	//一直为0,说明一直有左孩子结点,故一直遍历下去
		p = p->lchild;
	return p;
}

//找到中序线索二叉树p结点的后继
ThreadNode* Nextnode(ThreadNode* p)
{
	if (p->rtag == 0)
		return Firstnode(p->rchild);
	else
		return p->rchild;			//tag为1,说明含有线索,直接根据线索就可以找到后继
}

//对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void Inorder(ThreadNode* T)
{
	for (ThreadNode* p = Firstnode(T); p != nullptr; p = Nextnode(T))
		visitThread(p);
}

        (2).找前驱 

        与上文中,找后继的原理相同,

        以中序为例,找前驱,(左 中 右)中 右,即找左孩子的最右下角的节点。

        代码如下,

//获取p子树的最右下角节点
ThreadNode* Lastnode(ThreadNode* p)
{
	while (p->ltag == 0)	//一直为0,说明一直有右孩子结点,故一直遍历下去
		p = p->rchild;
	return p;
}

//找到中序线索二叉树p结点的前驱
ThreadNode* Prenode(ThreadNode* p)
{
	if (p->ltag == 0)
		return Lastnode(p->lchild);
	else
		return p->lchild;			//tag为1,说明含有线索,直接根据线索就可以找到后继
}

//对中序线索二叉树进行逆向中序遍历(利用线索实现的非递归算法)
void RevInorder(ThreadNode* T)
{
	for (ThreadNode* p = Lastnode(T); p != nullptr; p = Prenode(T))
		visitThread(p);
}

        · 先序(根 左 右)

        (1).找后继

        对于先序,根 左 右,在找后继中,都将p 节点当作序列中的根(rtag标记后继线索)

        · rtag == 1(右孩子指针被线索化)(没有右孩子),next = p->rchild

        · rtag == 0(右孩子存在),

                假设有左孩子(根(根 左 右)右),next = p->lchild(后继就是左子树的根节点)

                假设没有左孩子(根(根 左 右)),next = p->rchild(后继就是右子树的根节点)

        (2).找前驱

        找前驱需要知道对应节点的父节点,而获取父节点的操作一般是写一个遍历整个树获取对应节点父节点的函数,也可以将二叉树改为一个含有父节点指针的三叉树。

        · p->ltag == 1,则 preNode = p->lchild

        · p->ltag == 0,

        首先,要找到它的父节点,

        i.p 为其父节点的左孩子(pre->rchild==p),则p 的前驱就是其父节点。

       

        ii.p 为其右孩子,且p 的左兄弟为空,则p 的前驱就是其父节点。

        iii.p 为其右孩子,且p 的左兄弟非空,则p 的前驱就复杂了,是p 的左兄弟最后遍历的节点。

        寻找思路:

        从其父节点的左孩子出发(以下是在该左子树中进行的操作),直接一直向右寻找直到可以直接找到的最后一个右孩子(part1)(while:q = q->rchild),判别这个右子树是否有左孩子(part2)(if:q->lchild != nullptr),如果有,则向左找,向左移动一位(q = q->lchild),再判别该节点是否有右孩子,若有,则向右持续移动,若没有,判断是否有左孩子,即重复上文part2往后的操作,直到遍历到叶子节点,此时的这个节点就是p 的前驱。如果没有,则该节点就是左子树先序遍历的最后一个节点。

        总之,就是能往右走,就往右走,走不通,再看看是否能左走,以及左走之后,还能再右走不。

        iiii.p 为这个树的根节点,则没有前驱。

        · 后序(左 右 根)

        (1).找后继

         · rtag == 1,则next = p->rchild

        · rtag == 0(右孩子存在),

        由于p 只能向下找孩子,在后序序列中,根节点居于末尾,后继难以查询,需要引入三叉树或用土方法获得其父节点。

左 右 根

> (左 右 ) (左 右 ) 根

        i.p 作为父节点的右孩子,其后继就是其父节点。

        ii.p 作为父节点的左孩子,其后继又麻烦了,需要观察兄弟节点,如果没有兄弟节点,那后继就是父节点;如果,后继就是兄弟节点在兄弟子树中,后序遍历最先访问的节点(最左),其内的逻辑与上文那些,不能说完全相同吧,也就是一摸一样,到最左末端,看看这个末端是否有右孩子,有就往右一个,再看看有左有右,直到遇到叶子节点

        iii.p 作为整个树的根节点,则没有后继。

        (2).找前驱

        · p->ltag == 1,则pre = p->lchild

        · p->ltag == 0,

        此时,p 必有左孩子,根据后继序列的特性,可以很轻易的知道,当p 的右孩子存在时,其前驱就是它的右孩子,右孩子不存在时,前驱就是左孩子。

左 右 根

> (左 右 ) (左 右 ) 根

        

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

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

相关文章

如何禁止上班期间浏览无关网站?

禁止员工在上班期间浏览无关网页主要是为了提升工作效率和生产力&#xff0c;确保员工能够专注于工作任务。同时&#xff0c;这种做法有助于降低网络安全风险&#xff0c;防止恶意软件和钓鱼攻击&#xff0c;减少数据泄露和法律风险&#xff0c;维护公司的专业形象&#xff0c;…

【前端开发入门】JavaScript快速入门--js变量

目录 引言一、为什么要定义变量二、定义变量的一些技巧1. 解构赋值1.1 Object解构赋值1.2 Array解构赋值1.3 总结规律 2. 字符串拼接 三、变量作用域四、总结 引言 本系列教程旨在帮助一些零基础的玩家快速上手前端开发。基于我自学的经验会删减部分使用频率不高的内容&#xf…

Unity引擎:游戏开发的核心力量

目录 引言 Unity引擎的发展历程 早期发展 跨平台支持 Unity引擎的核心特性 易用性 社区支持 跨平台能力 Unity在游戏开发中的应用 移动游戏 独立游戏 3A游戏 Unity的未来展望 高级图形和渲染技术 扩展现实&#xff08;XR&#xff09;支持 云服务和多人游戏 结论…

C#判断点是否在多边形内

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff01;人工智能学习网站 前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任…

在不支持AVX的linux上使用PaddleOCR

背景 公司的虚拟机CPU居然不支持avx, 默认的paddlepaddle的cpu版本又需要有支持avx才行,还想用PaddleOCR有啥办法呢? 是否支持avx lscpu | grep avx 支持avx的话,会显示相关信息 如果不支持的话,python运行时导入paddle会报错 怎么办呢 方案一 找公司it,看看虚拟机为什么…

logdata-anomaly-miner:一款安全日志解析与异常检测工具

关于logdata-anomaly-miner logdata-anomaly-miner是一款安全日志解析与异常检测工具&#xff0c;该工具旨在以有限的资源和尽可能低的权限运行分析&#xff0c;以使其适合生产服务器使用。 为了确保 logdata-anomaly-miner的正常运行&#xff0c;推荐安装了python > 3.6的…

通过异地组网工具+RustDesk实现虚拟局域网使用远程桌面RDP

通过异地组网工具RustDesk实现虚拟局域网使用远程桌面RDP 预期效果 常见的远程桌面工具就不多说&#xff0c;麻烦而且不好用 QQ 使用普及率高 卡顿、延迟高 TeamViewer 功能强大、兼容性好 官方查询商业用途频繁 向日葵 安全性高、支持多种设备 强制登录、免费用户限速、限…

10. mapreduce实现wordcount

一. mapreduce 概述 mapreduce是一个并行计算框架&#xff0c;它起源于Google的MapReduce论文&#xff0c;它主要用于离线海量数据计算。 优点&#xff1a;海量数据离线处理&#xff0c;开发简单&#xff0c;部署方便缺点&#xff1a;仅适用于批处理&#xff0c;不支持实时数…

二极管那些事儿

一.发光二极管&#xff08;LED&#xff09; 1.压降很大&#xff08;2~3v&#xff09; 二.普通二极管&#xff08;eg:1N4007&#xff09; 1.一般用于整流和续流 2.比较廉价 3.一般压降0.7v 4.可用于防反接电路如下&#xff1a; 三&#xff1a;肖特基 1.开关速度第一&#…

UE5之5.4 第一人称示例代码阅读1 FirstPersonProjectile

既然如此&#xff0c;这几个文件都看看 先看看FirstPersonProjectile头文件 定义了几个函数 然后是两个component 这个projectilemovement应该是控制物理运动的 看看CPP文件 sphere那个就创建了一个subobject&#xff0c;初始化了一下&#xff0c;然后这里 CollisionComp-&g…

【C++差分数组】P10903 商品库存管理

本文涉及知识 C差分数组 洛谷 P10903 商品库存管理 题目简述&#xff1a; 有n中商品&#xff0c;编号[1,n]。有m中操作 ope[i]{LI,RI}&#xff0c;将编号LI到LR的商品都加1。 有m个查询&#xff0c;第i个查询 &#xff0c;执行所有ope[i],i ≠ \neq  i 后为0的商品数。 1…

基于PID控制器和四象限DC-DC功率转换器的永磁直流电机速度控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于PID控制器和四象限DC-DC功率转换器的永磁直流电机速度控制系统simulink建模与仿真。系统包括电流PI控制器&#xff0c;速度PI控制器&#xff0c;四象限DC-DC功率转换器&am…

Leetcode3. 无重复字符的最长子串

问题描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb&q…

Ubuntu 24.04 系统上配置 Node.js 运行环境

本文我们重点介绍两种安装 Node.js 的方法。第一种方法使用 NVM (Node VersionManager)&#xff0c;这是安装和管理多个 Node.js 版本的最好和最快的方法。第二种方法使用官方包存储库在 Ubuntu 上安装 Node.js&#xff0c;一次只允许安装一个版本。 必备条件 A running Ubun…

Java学习Day52:金酬外护遭魔毒,圣显幽魂救本原(验证码补全,新增预约逻辑)

1.验证码补全 //发送验证码sendValidateCode() {/*** 1.手机号不能为空* 2.手机号符合规范* 3.发送一分钟后才可再次发送*///获取手机号let telephone this.orderInfo.telephone;//验证手机号不能空if (telephone undefined){this.$message.error("请输入手机号");…

CentOS 8在Linux虚拟机修改IP地址,出现:错误:“ens160“ 不是活动的连接。错误:未提供活动连接。

问题&#xff1a;错误&#xff1a;"ens160" 不是活动的连接。错误&#xff1a;未提供活动连接。 1.查看网络服务运行状态&#xff1a; 1)CentOS 7执行命令&#xff1a;systemctl status network 2)CentOS 8执行命令&#xff1a;systemctl status NetworkManager&a…

【深度学习基础】详解Pytorch搭建CNN卷积神经网络实现手写数字识别

MNIST 数据集,其包含70000 个2828 的手写数字的数据集,其中又分为60000 个训练样本与10000 个测试样本。 安装实验用到的包 anaconda promt 安装python包, 首先在开始界面打开prompt 进入到相应的虚拟环境中,下面的python38你自己创建的虚拟环境名称。 # 激活虚拟环境,v…

微信小程序中关闭默认的 `navigationBar`,并使用自定义的 `nav-bar` 组件

要在微信小程序中关闭默认的 navigationBar&#xff0c;并使用自定义的 nav-bar 组件&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 关闭默认的 navigationBar 在你的页面的配置文件 *.json 中设置 navigationBar 为 false。你需要在页面的 JSON 配置文件中添加以下代码…

如何在Debian操作系统上安装Doker

本章教程&#xff0c;主要介绍如何在Debian 11 系统上安装Docker。主要使用一键安装Docker脚本和一键卸载脚本来完成。 一、安装Docker #!/bin/bashRED\033[0;31m GREEN\033[0;32m YELLOW\033[0;33m BLUE\033[0;34m NC\033[0mCURRENT_DIR$(cd "$(dirname "$0")…

微知-Linux内核自带的模块被压缩为ko.xz后如何恢复成不压缩版本?(xz -d xxx.ko.xz)

背景 在使用crash定位问题的时候需要使用ko使用对应的符号信息。直接用ko.xz无法正确加载。需要恢复成ko文件。本文介绍如何解压缩。 恢复步骤 在Linux系统中&#xff0c;.xz文件是一种使用LZMA2算法压缩的文件格式。要解压ko.xz文件&#xff0c;你可以使用xz命令行工具。 …