【数据结构】--二叉树

news2024/10/7 12:28:50

注:本文树和二叉树的概念及结构部分有部分参考了别的文章,其他的二叉树的实现和性质习题等等都是自己一点点写的,创作不易,希望支持!

—————————————————————

目录

一. 树概念及结构

1、树概念

2、树的表示

二. 二叉树概念及结构

1、概念

2、二叉树的特点

 3、满二叉树和完全二叉树 

4、二叉树的存储结构

三、二叉树的实现

1、二叉树顺序存储的实现

2、二叉树链式存储的实现 

①、四种遍历顺序的实现 

1、前序遍历

2、中序遍历 

3、后序遍历 

4、层序遍历和判断一个树是否为完全二叉树: 

②、获得树节点个数---TreeSize

 ③、获得叶子结点个数---TreeLeafSize

④、二叉树第k层结点个数--BinaryTreeLevelKSize 

⑤、二叉树查找值为x的节点--BinaryTreeFind

⑥、销毁二叉树--DestroyTree

四、二叉树的性质


一. 树概念及结构

1、树概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

根结点:根节点没有前驱结点。
除根节点外,其余结点被分成是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。树是递归定义的。

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为2
叶节点:度为0的节点称为叶节点; 如上图:G、H、I节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:B、D、C、E、F节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为2
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为堂兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m棵互不相交的树的集合称为森林;

2、树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

    typedef int DataType;
    struct Node
    {
         struct Node* firstChild1; 
         struct Node* pNextBrother; 
         DataType data; 
    };

二. 二叉树概念及结构

1、概念

一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成

2、二叉树的特点

 ①每个结点最多有两棵子树,即二叉树不存在度大于2的结点。

②、二叉树的子树有左右之分,其子树的次序不能颠倒。

 3、满二叉树和完全二叉树 

 ①、满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
②、完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

4、二叉树的存储结构

二叉树一般可以使用两种存储结构,一种顺序结构,一种链式结构。

①、顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。(物理结构是真实存在的,而逻辑结构使我们想象出来的)

②、链式存储

 二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链表来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程学到高阶数据结构如红黑树等会用到三叉链

   // 二叉链
   struct BinaryTreeNode
   {
    struct BinTreeNode* _pLeft; // 指向当前节点左孩子
    struct BinTreeNode* _pRight; // 指向当前节点右孩子
    BTDataType _data; // 当前节点值域
   }
   // 三叉链
   struct BinaryTreeNode
   {
    struct BinTreeNode* _pParent; // 指向当前节点的双亲
    struct BinTreeNode* _pLeft; // 指向当前节点左孩子
    struct BinTreeNode* _pRight; // 指向当前节点右孩子
    BTDataType _data; // 当前节点值域
   };

三、二叉树的实现

1、二叉树顺序存储的实现

顺序存储主要实现堆的,这个我另一个博客有写过,想看的可以看下 

2、二叉树链式存储的实现 

遍历:前序 中序 后序 层序 前三个又叫深度优先遍历,层序又叫做广度优先遍历,树的遍历和线性表的遍历是不一样的。在图中的岛屿等问题爱考深度优先问题。

深度优先遍历:指节点均往最深的地方走,无路可走时(即NULL)再往回退,退回来有岔路再往最深的地方走,一般用到递归和栈。

层序优先遍历:以根为中心,一层一层往外走,一般用到队列。

任何一个二叉树都要看做三个部分:根节点 左子树 右子树 (因为左子树又可以分为根,左子树,右子树,左子树又可以分为根,左子树,右子树......直到遇到空的子树,即NULL,那么右子树同理,所以这个问题可以分为子问题,子问题又可分为子问题,直到NULL,即递归)

针对下图,前中后序遍历顺序是如何的?

前序遍历(先序遍历):

根 左(子树)右(子树)

A B D NULL NULL E NULL NULL C NULL NULL (本质的遍历顺序)

我们常说的是:A B D E C

但是一般我们是不会写这个NULL的,但是了解本质的遍历顺序才有利于理解后面讲的递归遍历二叉树。

中序遍历:

左(子树) 根 右(子树)

NULL D NULL B NULL E NULL A NULL C NULL(本质的遍历顺序)

我们常说的是:D B E A C

后序遍历:

左(子树) 右(子树)根

NULL NULL D NULL E NULL B NULL NULL C A(本质的遍历顺序)

我们常说的是: D E B C A 

二叉树的链式存储实现

已知二叉树结构:

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;//根的数据
	struct BinaryTreeNode* _left;//左子树
	struct BinaryTreeNode* right;//右子树
}BTNode;

①、四种遍历顺序的实现 

1、前序遍历

 

前序遍历:用递归实现,根左右,

递归过程:先A,打印A的值,再B,B又作为根,打印B的值,再D,D又作为根,打印D的值,访问它的左子树,为NULL,所以终止,访问它的右子树,为NULL,也终止,至此,B的左子树D访问完毕,再访问B的右子树E,它的左右子树均为NULL,故终止,至此,B的左右子树访问完毕,A的左子树访问完毕,再C,先打印C的值,C的左右子树均为NULL,C访问完毕,至此整个二叉树遍历完毕。

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	printf("%c", root->_data);
	PrevOrder(root->_left);
	PrevOrder(root->_right);
}

2、中序遍历 

 

中序遍历:用递归实现,左根右

递归过程:A的左子树B,B的左子树D,①、先D的左子树NULL,故打印D的值,右为NULL,至此D访问完毕②访问B,故打印B的值③、访问E时,要先访问它的左子树,为NULL,故打印E的值,右子树为NULL,至此E访问完毕,B子树访问完毕④、再访问A,故打印A的值⑤、访问C,C的左子树右子树均为NULL,至此C访问完毕,A访问完毕

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	InOrder(root->_left);
	printf("%c", root->_data);
	InOrder(root->_right);
}

3、后序遍历 

 

 后序遍历:用递归实现,左右根

递归过程:①、D的左子树NULL,右也为NULL,再访问D,打印D的值②、访问E之前,因其左右子树为NULL,故打印E的值③、访问B,打印B的值④、访问C之前,先访问其左右子树,均为NULL,故打印C的值⑤、最

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	PostOrder(root->_left);
	PostOrder(root->_right);
	printf("%c", root->_data);
}

 

4、层序遍历和判断一个树是否为完全二叉树: 

层序遍历:

  层序遍历只保存节点的值可以吗?不行,因为比如你存值A,就无法找到它的左右孩子B和C,所以要存这个节点,但存节点又太大了,所以我们一般会用存节点的指针

因为需要用队列,所以我们可以在当前项目下添加以前写的队列的一系列操作,先在写的队列的所在文件路径下,找到写的队列的两个文件

然后打开当前要写的二叉树项目->添加现有项->粘贴刚才复制的两个文件,这样这个二叉树项目就能用队列的两个文件了。

回到刚才所说的,我们要存节点的指针,即要在Queue.h中typedef BTNode* QDataType,但是BTNode在Queue文件中未定义,法一、在Queue.h文件中定义二叉树,这显然有些不妥,在一个队列的实现中竟然定义二叉树,故采用法二、在Queue.h文件中声明外部变量,extern struct BinaryTreeNode  再typedef struct BinaryTreeNode* QDataType 

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);//要初始化,不然就为随机值了
	if (root == NULL)
		return;//如果为空树,直接return;
	QueuePush(&q, root);//先插入根节点到队列中

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);//pop并没有删除这个队头,而是让这个队头出队列

		printf("%d", front->_data);
		if (front->_left)
		{
			QueuePush(&q, front->_left);
		}
		if (front->_right)
		{
			QueuePush(&q, front->_right);
		}
	}
	QueueDestory(&q);
	printf("\n");
}

 判断一个树是否为完全二叉树:

由二叉树层序遍历的结果知: 

若是完全二叉树:有效节点(不是NULL的就是有效节点)的后面应全为NULL,若不是,有效节点后面会插有有效节点,所以利用层序遍历找有效节点后面的区别就可判断二叉树是否为完全二叉树。

思路:

①、在入队列的同时出队列,出队列时,如果出到NULL,说明有效节点判断完毕,直接break,判断有效节点后面的部分

②、如果有效节点后面的部分全为NULL,则为完全二叉树,如果有效节点的后面存在有效节点,则不是完全二叉树。

int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root == NULL)
		return 1;

	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{//出队列时碰到NULL了说明前面有效节点都走完了
        //这时就应该检查队列现存的是不是全是NULL即可
			break;
		}

        //因为NULL也要入队列,所以不用判断是否为NULL
        //入NULL是为了方便去判断是否为完全二叉树
		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}

    //再判断现在队列里面是否全为NULL,是则为完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front)
		{//非空则说明在NULL中存在有效节点,一定不是完全二叉树
			QueueDestroy(&q);
			return 0;
		}
	}
	
	QueueDestroy(&q);
	return 1;//均为NULL,则是完全二叉树
}

如果我们想验证自己写的遍历,可以自己构建一个树,然后再验证

②、获得树节点个数---TreeSize

用前中后序都可以, 下面这个代码表面看是可以的,访问一次根,就相当于有一个节点,size就++,因为每一个节点在它的子树中都可以看做一次根。类似于根左右的遍历方式,只是这下是只统计几个,不是打印根的数据而已。但下面的代码存在错误,因为每次递归调用函数,因为size是局部变量,每次的size++的都不是一个size。

int TreeSize(BTNode* root)
{
	if(root == NULL)
		return 0;

	int size = 0;
	++size;
	TreeSize(root->_left);
	TreeSize(root->_right);

	return size;
}

解决方案考虑:

①、全局变量可以吗(在函数外部定义一个size)?不完全可以。因为调用一次TreeSize会给你正常返回大小,你调用第二次呢?第二次调用会在第一次上面累加,所以就错了。因为我们以后会学多线程,两个程序会同时在访问,那假如两个程序访问同一个全局变量,那不都加在同一个全局变量上了吗,会出问题。

②、static静态变量可以吗(static int size)?全局变量和静态变量区别就在于访问位置不一样,用static int size 后,size出了函数还会在,可加在同一个size上,但是和全局变量同样的问题,第二次调用TreeSize函数就不行了。

③、利用参数解决,每次都传一个指针就可保存下来了,但是不太简洁

void TreeSize(BTNode* root,int* psize)
{
	if(root == NULL)
		return 0;

	(*psize)++;
	TreeSize(root->_left,psize);
	TreeSize(root->_right,psize);
}

④、分治思想,把大问题不断分解为小问题,根,左子树,右子树,而左右子树又可以分为根,左子树,右子树。当前树的节点个数=1+左子树的节点个数+右子树的节点个数(这个1就是当前树的根节点)

递归过程:①、A不为空,return 1+A的左子树,进入A的左子树,B不为空,return 1+B的左子树,D不为空,return 1+D的左子树,D的左子树为NULL,故返回0,右子树也为空,故也返回0,所以return 1 + 0 + 0, 再访问B的右子树E,E不为空,return 1 + E的左子树 + E的右子树,即返回1,所以对于B return 1+1+1,至此A的左子树访问完毕,再访问A的右子树C,同理,C返回1,所以对于A来说return 1+3+1,至此整个二叉树访问完毕,返回5

int TreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return 1 + TreeSize(root->_left) + TreeSize(root->_right);
}

 ③、获得叶子结点个数---TreeLeafSize

法一、利用参数求解


int TreeLeafSize(BTNode* root, int* psize)
{
	if (root == NULL)
	{
		return 0;
	}
	else if (root->_left == NULL && root->_right == NULL)
	{
		(*psize)++;
	}

	TreeLeafSize(root->_left, psize);
	TreeLeafSize(root->_right, psize);
}

法二、分治思想:

当前树的叶子结点个数=左子树叶子节点个数+右子树叶子结点个数,而判断是否为叶子结点,看他的左子树和右子树是否同时为NULL即可。

①、如果是空树,直接返回0

②、如果是叶子结点,返回1,作为递归出口

③、如果不是叶子结点,则不断访问它的左右子树,不断逼近递归出口

int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;//空树的叶子结点是0个
	}
	if (root->_left == NULL && root->_right == NULL)//如果是叶子结点
		return 1;
	
	//如果不是叶子结点,则看左右子树,直到遇到叶子结点
	return TreeLeafSize(root->_left) + TreeLeafSize(root->_right);

}

④、二叉树第k层结点个数--BinaryTreeLevelKSize 

思路:当前树的第k层可以转换成当前树的左子树和右子树的第k-1层,而左子树的第k-1层又=它的左子树和右子树的第k-2层,直到层数=1时就无需再分解

当k==1成立时,有两种情况,一种情况是该层数有节点,另一种情况是该层数无节点,所以应先判断当前是否为NULL,不是再又因k==1成立,return 1即可

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);
}

⑤、二叉树查找值为x的节点--BinaryTreeFind

思路:先序遍历,假设要找x,先判断当前树的根节点是不是x,是->返回该节点,不是则看左子树和右子树,但这里注意接收一下节点,如果非空,说明找到了。

如图:假如我们想找E(即x=E)

①、A不是E,故判断A的左子树,B不是,故判断B的左子树,D不是,故判断D的左子树,为NULL故返回NULL,再判断D的右子树也返回NULL,故以D为根节点的整棵树不存在E,故返回NULL,再判断B的右子树,B的右子树==E成立,所以return E(此时E是根),node为真,所以函数返回E,对于以A为根来说,左子树返回的node为真,故返回node,递归结束。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{	//按照根左右的遍历顺序来
	if (root == NULL)
	{
		return NULL;
	}
	//先判断当前树的根是否满足
	if (root->_data == x)
	{
		return root;
	}
	//再判断当前树的左右子树是否有满足的
	BTNode* node = BinaryTreeFind(root->_left, x);
	if (node)
		return node;//如果左子树存在满足的就无需判断右子树了
	BTNode* node = BinaryTreeFind(root->_right, x);
	if (node)
		return node;
	//如果上述都不满足就返回NULL即可
	return NULL;
}

⑥、销毁二叉树--DestroyTree

思路:采用后序遍历,因为如果采取先序遍历,你先释放根节点,左右节点就找不到了,所以应该先看左右子树,再释放根 

实现方式一、参数用一级指针

有时我们为了接口型的一致性,可能就采用一级指针 

void DestroyTree(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	DestroyTree(root->_left);
	DestroyTree(root->_right);
	free(root);
}

 实现方式二、参数用二级指针

有时我们为了防止野指针问题,会传二级指针,以便对传入的参数置为NULL

void DestroyTree(BTNode** root)
{
	if (root == NULL)
	{
		return;
	}
	DestroyTree((*root)->_left);
	DestroyTree((*root)->_right);
	free(*root);
	*root = NULL;
}

四、二叉树的性质

1、若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.

2、若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1.

3、对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1

下面是针对满二叉树和完全二叉树的:

4、若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=Log2(n+1). (ps:Log2(n+1)是log以2为底,n+1为对数)

5、对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有: 

   1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
   2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
   3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

部分例题如下:

①、某二叉树共有399个结点,其中度为2的节点有199个,则此二叉树中叶子节点数为(200个)

分析:因为n0 = n2 + 1

②、若有2n个结点的完全二叉树中,叶子结点个数为(n个) 

分析:关于完全二叉树给节点数让你求叶子结点数的问题

对于二叉树,度数只有三种情况,度为0、1,2个,总结点数=度为0、1,2的各节点数相加。又因为是完全二叉树,因为其特殊性,度为1的最多只有1个(即要么1个,要么没有,具体有没有看题中给的节点个数)。故假设度数为0的节点(即叶子结点)有x个,则度为2的节点个数为x-1个(n0=n2+1),x+x-1=2x-1,又因为题中已给节点数为2n,即偶数个,所以度为1的节点有1个,所以2x-1+1=2n,故x=n  =>   叶子结点数有n个

③、若一颗完全二叉树的节点数为531个,此树的深度为(10)

分析:公式log2(n+1) = 深度(又称高度),其中n=531,所以解得h约等于9.几,深度为整数,所以h=10

④、一个具有767个结点的完全二叉树,其叶子结点的个数为(384)个

分析:同理第②题,假设叶子结点x个,x+x-1 +1或者+0 = 767,本题度为1的节点数应为0,故x=384个

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

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

相关文章

springboot家具商城系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

Spring Boot 系列2 -- 配置文件

目录 1. 配置文件的作用 2. 配置文件的格式 3. properties 配置文件说明 3.1 properties 基本语法 3.2 读取配置文件 3.3 properties 缺点 4.yml 配置文件说明 4.1 yml 基本语法 4.2 yml 使用进阶 4.2.1 yml 配置不同数据类型及 null 4.2.2 yml 配置读取 4.2.3 注意…

FPGA学习——点亮流水灯

文章目录 一、前言二、源码三、ModelSim仿真3.1 tb文件源码&#xff1a;3.2 创建项目3.3 ModelSim仿真 一、前言 在FPGA开发板中&#xff0c;一般板载LED默认高电平点亮&#xff0c;因此我们只需要将想要亮起的LED赋值为1即可。 本入门实验要求为每隔1s开发板上的LED轮流亮起&…

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 Proteus仿真小实验&#xff1a; STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD1602显示器DHT11温度湿度电位器模拟…

高分卫星影像及GIS技术在甘南泥石流灾害中的应用

本文使用的甘南夏河县泥石流灾情专题数据如下&#xff08;来源于高分甘肃中心&#xff09;&#xff1a; &#xff08;1&#xff09;灾前遥感影像 &#xff08;2&#xff09;灾害位置 &#xff08;3&#xff09;基础地理数据&#xff1a;行政区划、交通路网、河流水系、湖泊水库…

前端Vue自定义精美steps步骤条进度条插件 物流信息跟踪展示组件 流程审批跟进组件

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

怎么用PDF派工具将Word转成PDF

Word是我们最常用的一种格式文件&#xff0c;它易于编辑&#xff0c;但是安全性和稳定性较差&#xff0c;有时候我们发送给别人的Word文件&#xff0c;接收到打开内容已经乱码。遇到这种情况&#xff0c;我们可以优先将Word文件转换成稳定性好的PDF文件。那么如何进行文件格式转…

如何使用伪元素 ::before 实现 Antd 表单一模一样的 required 红色 * 号

如何使用伪元素 ::before 实现 Antd 表单一模一样的 required 红色 * 号 背景 以一个简单的 Form.Item 包裹 Select 为例 我们去实现它的 * 号 操作 F12 打开控制台选中这个元素上面查看 CSS 属性 仿照这个写在 .less 文件里 // .less .ruleTable::before {display: inlin…

RiProV2主题一级分类显示包含子分类的数量Ritheme主题美化WordPress美化类似的步骤

美化-RiProV2主题一级分类显示包含子分类的数量 WordPress主题一级分类页面显示包含子分类的数量 一级分类显示子分类相加的数量 原主题配置项 原来的RiProV2主题,虽然有个配置用来显示分类下的数量。 但是该数量有个问题,就是一级分类的数量显示不包含该一级分类下二级…

操作系统14:缓冲区和磁盘调度算法

目录 1、缓冲区管理 &#xff08;1&#xff09;单缓冲区和双缓冲区 1.1 - 单缓冲区 1.2 - 双缓冲区 &#xff08;2&#xff09;环形缓冲区/多缓冲区 &#xff08;3&#xff09;缓冲池(Buffer Pool) 3.1 - 缓冲池的组成 3.2 - 缓冲池的工作方式 2、磁盘存储器的性能和调…

面向对象进阶一(static,继承,多态)

面向对象进阶一 一、static二、继承2.1 继承的定义和特点2.2 继承内容、成员变量和成员方法的访问特点2.2.1继承内容2.2.2 成员变量的访问特点2.2.3 成员方法的访问方法、方法的重写 2.3 继承中构造方法的访问特点 三、this、super使用总结四、多态4.1 多态的基本概念4.2 多态调…

MySQL 学习笔记 2:触发器

MySQL 学习笔记 2&#xff1a;触发器 图源&#xff1a;ubiq.co 触发器&#xff0c;就像字面意思那样&#xff0c;它会在数据库某些事件发生时执行一些操作。 具体来说&#xff0c;触发器会在特定表的INSERT、UPDATE、DELETE这些类型的 SQL 语句执行时被“触发”&#xff0c;并…

CopyOnWriteArrayList使用以及原理分析

文章目录 一、CopyOnWriteArrayList的简介二、CopyOnWriteArrayList类的继承关系1、Iterable接口&#xff1a;2、Collection接口&#xff1a;3、List接口&#xff1a;4、Cloneable接口&#xff1a;5、Serializable接口&#xff1a;6、RandomAccess接口&#xff1a; 三、CopyOnW…

模糊图片怎么修复清晰度?这几个方法分享给你~

在我们的日常生活和工作中&#xff0c;经常会遇到图片模糊的问题&#xff0c;这可能是由于拍摄时的手抖、对焦不准确或者图片压缩过度等原因造成的。那么&#xff0c;如何修复模糊的图片&#xff0c;提高其清晰度呢&#xff1f;本文将为您介绍几种方法。 方法一&#xff1a;使…

ABAP调用阿里云接口-短信服务-HTTP协议及签名(abap版本)<转载>

原文链接&#xff1a;https://blog.csdn.net/xiefireworks/article/details/113037650 阿里云接口文档请参考官网地址 https://help.aliyun.com/document_detail/59210.html?spm5176.8195934.J_5834642020.5.11ba4378DLVi4O 此处仅介绍使用ABAP完成阿里云短信服务签名请求的…

树莓派4B的串口UART配置

1 安装串口&#xff1a; 如果没有更换pip源会报错&#xff0c;所以指定安装源 pip install pyserial -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 修改uart配置&#xff1a; vim /boot/firmware/config.txt 在末尾添加&#xff1a; dtoverlayuart2…

X86架构的Linux(Ubuntu版本)上离线安装CUnit来解决Could not find CUnit(missing:CUNIT_LIBRARY)问题

前言1 下载cunit压缩安装包&#xff1a;CUint-2.1-3.tar.bz2&#xff08;为了安装成功请下载对应版本&#xff09;2 解压安装压缩包3 sudo ./bootstrap --prefix/usr/local/cunit 生成可执行文件configure*4 sudo ./configure --prefix/usr/local/cunit5 sudo make . 编译 &…

Centos安装指定docker版本和docker-compose

目录 一. 直接安装Docker最新镜像源 1. 卸载旧版本的Docker&#xff1a; 2. 安装依赖包&#xff1a; 3. 添加Docker源&#xff1a; 4. 安装Docker&#xff1a; 5. 启动Docker服务&#xff1a; 6. 验证Docker是否安装成功&#xff1a; 二、指定Docker版本安装 1. 查看…

已解决‘mongo‘ 不是内部或外部命令,也不是可运行的程序

已解决&#xff08;MongoDB安装报错&#xff09;‘mongo’ 不是内部或外部命令,也不是可运行的程序 报错代码 粉丝群里的一个小伙伴安装完MongoDB后&#xff0c;在cmd中启动&#xff0c;却说不是可运行的命令&#xff1f; 报错原因 报错原因&#xff1a;由于没有配置环境变量的…

【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单

目录 一、利用 Redis 实现全局唯一 ID 生成(1) 为啥要用全局唯一 ID 生成(2) 全局唯一 ID 生成器(3) 全局 ID 的结构(4) 代码实现① RedisIdWorker② Test (5) 全局唯一 ID 其他生成策略 二、添加优惠券(1) 数据库(2) 添加优惠券接口 三、优惠券秒杀下单功能(1) 超卖问题(2) 乐…