树与二叉树

树的定义及相关概念

是n(n≥0)个结点的有限集合,n = 0时,称为空树,这是一种特殊情况。在任意一棵非空树中应满足:
1)有且仅有一个特定的称为根的结点。
2)当n > 1时,其余结点可分为m(m > 0)个互不相交的有限集合T1, T2,…, Tm,其中每个集合本身又是一棵树,并且称为根结点的子树
3)树的根结点没有前驱结点,其余结点有且仅有一个前驱结点。
4)树中所有结点可以有零个或多个后继结点。

树的基本术语

结点之间的关系描述:则有:祖先结点子孙结点双亲结点孩子节点兄弟结点堂兄弟结点,以上完全可以按照树的结构和亲戚对应的关系很方便的区分。如图所示:

树的结点之间的关系

很明显地可以了解到:B的子孙有EFKL,路径上的ABE都是K的祖先结点,E是K的双亲(父结点),K是E的子孙结点(孩子),K和L具有相同的双亲E,因此K和L是兄弟结点。结点G和EFHIJ互为堂兄弟结点。(排得差不多了,自己领会吧)。

结点、树的属性描述:结点的层次(深度)——从上往下数(从根到该节点的深度,例如B的深度为2),结点的高度——从下往上数,以该结点为根的子树的高度,例如D的高度为3,树的高度(深度)——总共多少层,上图树高度为4,结点的度——有几个孩子(分支),B度为2,D度为3,树的度——各结点的度的最大值。

树中度大于0的结点为分支结点,度为0的结点为叶子结点。在分支结点中,每个结点的分支数就是该结点的度。

有序树——逻辑上看,树中结点的各子树从左至右是有次序的,不能互换。
无序树——逻辑上看,树中结点的各子树从左至右是无次序的,可以互换。
路径——路径是由两个节点之间所经过的结点序列构成的。
路径长度——所经过的边的个数。
森林——森林是m(m≥0)棵互不相交的树的集合。 ^ac8bfc

树的性质

证明我就不写了,留给我自己去推。

  • 树中的结点数等于所有结点的度数加1(加的是根)
  • 度为m的树中第i层至多有mi−1m^{i-1}mi1个结点
  • 高度为h的m叉树至多有(mh−1)/(m−1)(m^h-1)/(m-1)(mh1)/(m1)个结点
  • 具有n个结点的m叉树的最小高度为⌈logm(n(m−1)+1)⌉\lceil log_m(n(m-1)+1)\rceillogm(n(m1)+1)⌉
  • 度为m、具有n个结点的树最大高度为n−m+1n-m+1nm+1
  • 高度为h的m叉树至少有h个结点。高度为h、度为m的树至少有h+m-1个结点。

二叉树的定义及相关概念

二叉树是n(n≥0)个结点的有限集合:
1)或者为空二叉树,即n = 0。
2)或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。
特点:1. 每个结点至多只有两棵子树 2. 左右子树不能颠倒(二叉树是有序树)。

二叉树和度为2的有序树的区别:

  1. 度为2的有序树树至少有3个结点,而二叉树可以为空
  2. 度为2的有序树的左右次序是相对于另一个孩子结点而言的,若某个结点只有一个孩子结点,则这个孩子结点就无须区分左右次序。而二叉树不一样。

几种特殊的二叉树

满二叉树

满二叉树:高度为h,且含有2h−12^h-12h1个结点。每层都含有最多的结点。除了叶子结点度都为2。

特点:

  1. 有最后一层有叶子结点
  2. 存在度为1的结点
  3. 按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父节点为⌊i/2⌋\lfloor i/2 \rfloori/2(如果有的话)

示例图:

满二叉树示例|70%

完全二叉树

完全二叉树:当且仅当其每个结点都与高度为h的满二叉树中编号为1~n的结点一一对应时,称为完全二叉树。

有n个结点。特点如下:

  1. i≤⌈n/2⌉i \leq \lceil n/2 \rceilin/2,则结点 iii 为分支结点,否则为叶子结点。
  2. 只有最后两层才会出现叶子结点。
  3. 最多只有一个度为 1 的结点(这种情况下该结点一定只有左孩子,没有右孩子)。
  4. 按层序编号,若 iii 结点为叶子结点或只有左孩子,则编号大于 iii 的结点均为叶子结点。
  5. 同满二叉树的3.特点。

示例图:

完全二叉树示例|70%

完全二叉树可以视作从满二叉树中删去若干最底层、最右边的一些连续叶节点所得到的二叉树。

平衡二叉树

平衡二叉树:任意结点的左右子树的深度之差不超过1。详见

二叉排序树

二叉排序树:左子树上所有结点均小于根结点的关键字;右子树所有结点均大于根结点的关键字。左子树和右子树个是一棵二叉排序树。详见

正则二叉树

正则二叉树:树中每个分支都有两个孩子,即树中只有度为0或2的结点。

二叉树的性质

以下推导过程略,自己推。

  • 非空二叉树的叶子结点数等于度为2的结点数加1,即n0=n2+1n_0=n_2+1n0=n2+1
  • 非空二叉树第i层最多有2i−12^{i-1}2i1个结点->m叉树第i层最多有mi−1m^{i-1}mi1个结点
  • 高度为h的二叉树至多有2h−12^h-12h1个结点(为一棵满二叉树)->高度为h的m叉树至多有(mh−1)/(m−1)(m^h-1)/(m-1)(mh1)/(m1)个结点
  • 具有n个结点的完全二叉树的高度⌈log2(n+1)⌉\lceil log_2(n+1)\rceillog2(n+1)⌉⌊log2n⌋+1\lfloor log_2n\rfloor+1log2n+1
  • 若完全二叉树有2k个(偶数)个结点,则必有n1=1n_1=1n1=1n0=kn_0 = kn0=kn2=k−1n_2 = k-1n2=k1,若完全二叉树有2k-1个(奇数)个结点,则必有n1=0n_1=0n1=0n0=kn_0=kn0=kn2=k−1n_2=k-1n2=k1(突破点:完全二叉树最多只会有一个度为1的结点)

二叉树的存储结构

二叉树的顺序存储结构

顺序存储结构:用一组地址连续的存储单元一次自上而下、自左至右存储完全二叉树的结点元素。即完全二叉树编号为i的结点元素存储在某个数组下标为i-1的分量中。该结构对顺序二叉树和完全二叉树比较合适。这种存储结构要从数组下标1开始存储数中的结点。

二叉树的顺序存储中,一定要把二叉树的结点编号与完全二叉树对应起来,所以一定会有大量的空间被浪费掉。

基于以上特点如果用来存储满二叉树完全二叉树可能使用此方法较为适合。

二叉树的顺序存储结构

二叉树的链式存储结构

由于顺序存储的空间利用率较低,所以我们一般都会采用链式存储结构。用链表结点来存储二叉树的每个结点。如图所示一个二叉树链式存储的结点结构。

二叉树链式存储的结点结构

二叉树的链式存储结构描述如下:

typedef struct BiTNode {ElemType data; // 结点数据struct BiTNode *lchild, *rchild; // 左右孩子指针
} BiTNode, *BiTree;

一颗二叉树及其所对应的二叉链表如图所示,展示了链式存储结构:

一颗二叉树及其所对应的二叉链表

当然也很容易知道,含有n个结点的二叉链表中,含有n+1个空链域。这为后面的线索二叉树提供了方便。

二叉树的遍历

先序遍历

先序遍历:先访问根结点,然后先序遍历左子树,最后先序遍历右子树。

实现:

void PreOrderTraverse(BiTree T) {if (T == NULL) return; // 递归终止条件visit(T); // 访问根结点PreOrderTraverse(T->lchild); // 先序遍历左子树PreOrderTraverse(T->rchild); // 先序遍历右子树
}

中序遍历

中序遍历:先中序遍历左子树,然后访问根结点,最后中序遍历右子树。

实现:

void InOrderTraverse(BiTree T) {if (T == NULL) return; // 递归终止条件InOrderTraverse(T->lchild); // 中序遍历左子树visit(T); // 访问根结点InOrderTraverse(T->rchild); // 中序遍历右子树
}

后序遍历

后序遍历:先后序遍历左子树,然后后序遍历右子树,最后访问根结点。

实现:

void PostOrderTraverse(BiTree T) {if (T == NULL) return; // 递归终止条件PostOrderTraverse(T->lchild); // 后序遍历左子树PostOrderTraverse(T->rchild); // 后序遍历右子树visit(T); // 访问根结点
}

层次遍历

层次遍历:从根结点开始,按层次顺序访问结点。

实现:

void LevelOrderTraverse(BiTree T) {if (T == NULL) return; // 递归终止条件Queue<BiTree> q;//这里需要用到一个辅助队列q.push(T);while (!q.empty()) {BiTree node = q.front();q.pop();visit(node);if (node->lchild != NULL) q.push(node->lchild);if (node->rchild != NULL) q.push(node->rchild);}
}

这里用到了之前已经学过的[[栈和队列#队列]]的知识,代码里面用到了C++的队列,如果是书上的,那么 push就是 EnQueuepop就是 DeQueue

算法效率分析与改造

不管是哪种算法,每个结点都访问一次且仅访问一次,故时间复杂度都是O(n)。

算法改造成为非递归算法时,通常需要借用[[栈和队列#栈]]一个来存储结点。如以下中序遍历改造所示:

// 中序遍历非递归算法,需要借用一个栈
void InOrder2(BiTree T){InitStack(S); BiTree p=T;  // p是遍历指针while(p||!isEmpty(S)){if(p){Push(S,p);p=p->lchild;}else{Pop(S,p);visit(p);p=p->rchild;}}
}

利用遍历序列构造一个二叉树

给定先序遍历和中序遍历序列,可以唯一确定一棵二叉树。先序遍历的第一个元素是根结点,然后在中序遍历中找到根结点的位置,左边的部分是左子树,右边的部分是右子树。

同理,给定后序遍历和中序遍历序列,也可以唯一确定一棵二叉树。后序遍历的最后一个元素是根结点,然后在中序遍历中找到根结点的位置,左边的部分是左子树,右边的部分是右子树。

那么,给定层序和中序遍历,也可以唯一确定一棵二叉树。层序遍历的第一个元素是根结点,然后在中序遍历中找到根结点位置,左边的部分是左子树,右边的部分是右子树。

那么很显然,先序、后序、层序俩俩组合无法唯一确定一棵二叉树。

注:此节需要练习才能熟练掌握。

线索二叉树

传统的二叉树,每个结点有两个指针,分别指向左孩子和右孩子。不能直接得到结点在遍历过程中的前驱和后继结点。为了解决这个问题,引入了线索二叉树。

在前面的内容中我们知道:在含有n个结点的二叉树中,含有n+1个空链域。线索二叉树就是利用这些空链域来存储结点的前驱和后继信息。

线索二叉树规定:若无左子树,令lchild指向其前驱结点;若无右子树,令rchild指向其后继结点。如图所示为线索二叉树的结点结构

线索二叉树的结点结构

其中标志域lflagrflag用来标识指针域是指向孩子结点还是前驱/后继结点。

{lflag=0表示 lchild指向孩子结点lflag=1表示 lchild指向前驱结点rflag=0表示 rchild指向孩子结点rflag=1表示 rchild指向后继结点\begin{array}{l} \left\{ \begin{aligned} &\text{lflag}=0 \text{ 表示 } lchild \text{ 指向孩子结点} \\ &\text{lflag}=1 \text{ 表示 } lchild \text{ 指向前驱结点} \\ &\text{rflag}=0 \text{ 表示 } rchild \text{ 指向孩子结点} \\ &\text{rflag}=1 \text{ 表示 } rchild \text{ 指向后继结点} \\ \end{aligned} \right. \end{array} lflag=0 表示 lchild 指向孩子结点lflag=1 表示 lchild 指向前驱结点rflag=0 表示 rchild 指向孩子结点rflag=1 表示 rchild 指向后继结点

那么线索二叉树的存储结构描述如下:

typedef struct ThreadNode {ElemType data; // 结点数据struct ThreadNode *lchild, *rchild; // 左右孩子指针int lflag, rflag; // 标志域
} ThreadNode, *ThreadTree;

线索二叉树的构造:遍历一次二叉树,只是在遍历的过程中,检查当前结点的左右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。代码:

// 创建中序线索二叉树
void CreateInThread(ThreadTree T) {ThreadTree pre = NULL; // pre用于记录当前结点的前驱if (T != NULL) {InThread(T, pre);      // 对二叉树进行中序线索化pre->rchild = NULL;    // 最后一个结点的后继线索置空pre->rtag = 1;         // rtag=1表示rchild为线索}
}// 中序遍历递归线索化
void InThread(ThreadTree &p, ThreadTree &pre) {if (p != NULL) {InThread(p->lchild, pre); // 递归线索化左子树// 若左子树为空,则lchild指向前驱pre,ltag=1if (p->lchild == NULL) {p->lchild = pre;p->ltag = 1;}// 若前驱结点的右子树为空,则rchild指向当前结点,rtag=1if (pre != NULL && pre->rchild == NULL) {pre->rchild = p;pre->rtag = 1;}pre = p; // 更新前驱为当前结点InThread(p->rchild, pre); // 递归线索化右子树}
}

先序线索化:

void CreatePreThread(ThreadTree T) {ThreadTree pre = NULL; // pre用于记录当前结点的前驱if (T != NULL) {PreThread(T, pre);      // 对二叉树进行先序线索化if(pre->rchild == NULL) {//这里我有点疑问,可能他和中序线索化一样也不是不行,因为先和中的pre->rchild一定是NULL。pre->rtag = 1;      // rtag=1表示rchild为线索}}
}
// 先序遍历递归线索化
void PreThread(ThreadTree p, ThreadTree &pre) {if (p != NULL) { //左子树为空,建立线索if(p->lchild == NULL){p->lchild = pre;p->ltag = 1;}if(pre != NULL && pre->rchild == NULL){pre->rchild = p;pre->rtag = 1;}pre = p;//我们前面介绍过,如果p的左子树为空,则建立p为左孩子,所以判断p的左子树代表的是否为线索if(p->ltag == 0) {PreThread(p->lchild, pre); // 递归线索化左子树}PreThread(p->rchild, pre); // 递归线索化右子树}
}

后序线索化:

void CreatePostThread(ThreadTree T) {ThreadTree pre = NULL; // pre用于记录当前结点的前驱if (T != NULL) {PostThread(T, pre);      // 对二叉树进行后序线索化if(pre->rchild == NULL) {pre->rtag = 1;      // rtag=1表示rchild为}}
}
// 后序遍历递归线索化
void PostThread(ThreadTree p, ThreadTree &pre) {if (p != NULL) {PostThread(p->lchild, pre);PostThread(p->rchild, pre);if (p->lchild == NULL) {p->ltag = 1;p->lchild = pre; // 左子树为空,建立线索}if (pre != NULL && pre->rchild == NULL) {pre->rtag = 1;pre->rchild = p;}pre=p;}
}

带有头结点的线索二叉树

如图(此为中序):

带有头结点的线索二叉树

他的指向关系在图中很明确,这样做的好处就是能够很方便的从前往后或者从后往前对线索二叉树进行一个遍历。

线索二叉树的遍历

线索二叉树的遍历:利用线索二叉树,可以实现二叉树遍历的非递归算法。

以中序遍历找后继为例,代码如下:

void Inorder(ThreadNode *T) {for (ThreadNode *p = Firstnode(T); p != NULL; p = Nextnode(p))visit(p); // 访问结点
}
ThreadNode *Firstnode(ThreadNode *p) {while (p->ltag == 0) p = p->lchild; //找到最左下结点return p;
}
ThreadNode *Nextnode(ThreadNode *p) {if (p->rtag == 0) return Firstnode(p->rchild);else return p->rchild; // 返回后继结点
}

对于中序线索二叉树的遍历,可以使用上述的实现方式,只需变化一些条件,具体可以练习。

对于先序和后序线索二叉树的遍历,类似的实现方式也可以使用。

树和森林

树的存储结构

双亲表示法

采用一组连续空间来存储每个结点,同时每个结点中增设一个伪指针,指示其双亲结点在数组中的位置。可以很快得到每个结点的双亲结点,但求结点的孩子时需要遍历整个结构。

树的双亲表示法

存储结构描述:

#define MAX_TREE_SIZE 100 // 定义树的最大结点数
typedef struct {ElemType data; // 结点数据int parent; // 双亲结点的下标
} PTNode; // 双亲结点结构
typedef struct {PTNode nodes[MAX_TREE_SIZE]; // 存储结点的数组int n; // 结点数
} PTree; // 双亲表示法的树结构

双亲表示法表示一个森林:每棵树的根节点双亲指针= -1

孩子表示法

将每个结点的孩子结点都用单链表连接起来形成一个线性结构,n个结点就有n个孩子链表。寻找子女操作非常直接,而寻找双亲需要遍历n个结点中孩子链表指针域所指向的n个孩子链表

树的孩子表示法

孩子表示法表示一个森林:用孩子表示法存储森林,需要记录多个根的位置

孩子兄弟表示法

又称二叉树表示法,即以二叉链表作为树的存储结构。每个结点分为三部分:结点值、指向结点第一个孩子节点的指针,指向结点下一个兄弟结点的指针。优点是可以方便地实现树转换为二叉树的操作,易于查找结点的孩子等。缺点是查找双亲麻烦

孩子兄弟表示法

存储结构描述:

typedef struct CSNode {ElemType data; // 结点数据struct CSNode *firstchild; // 指向第一个孩子结点的指针struct CSNode *nextsibling; // 指向下一个兄弟结点的指针
} CSNode, *CSTree; // 孩子兄弟表示法

树、森林转化成二叉树

树转换为二叉树的规则:每个结点左指针指向它的第一个孩子结点,右指针指向它在树中相邻的兄弟节点,“左孩子右兄弟”,由树转换的二叉树没有右子树。

森林转换为二叉树:先将森林中的每棵树转换为二叉树,把第一棵树的根作为转换后二叉树的根,其左子树作为左子树。第二棵树作为转换后的右子树,第三棵树作为转换后右子树的右子树,即向右拼接。

二叉树转换为森林/树的规则:反过来即可。将右子树挨个拆下来。二叉树转换为树或森林是唯一的。

树和森林的遍历

树和森林遍历与二叉树遍历之前的对应关系

森林二叉树
先根遍历先序遍历先序遍历
后根遍历后序遍历中序遍历

树与二叉树的应用

哈夫曼树和哈夫曼编码

结点的:有某种现实含义的数值(如:表示结点的重要性等)
结点的带权路径长度:从树的根到该结点的路径长度(经过的边数)与该结点上权值的乘积
树的带权路径长度:树中所有叶结点的带权路径长度之和(WPL, Weighted Path Length)
WPL=∑i=1nwi⋅liWPL=\sum_{i=1}^{n} w_i \cdot l_iWPL=i=1nwili

在含有n个带权叶节点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树(Huffman Tree),也称为最优二叉树。

例题:

具有不同带权长度的二叉树

由此算得:

WPLa=7∗2+5∗2+2∗2+4∗2=36WPL_a=7*2+5*2+2*2+4*2=36WPLa=72+52+22+42=36
WPLb=7∗3+5∗3+2∗1+4∗2=46WPL_b=7*3+5*3+2*1+4*2=46WPLb=73+53+21+42=46
WPLc=7∗1+5∗2+2∗3+4∗3=35WPL_c=7*1+5*2+2*3+4*3=35WPLc=71+52+23+43=35(哈夫曼树)

构造方式:简单说,从结点中选出两个最小的结点,构成一个新节点,权为两结点之和,重复直到所有结点都处理完毕。

哈夫曼编码就是左0右1,那么a=0 ;b=10;c=110;d=111。

哈夫曼编码的特点是:没有一个编码是另一个编码的前缀,这样可以保证编码的唯一性。

并查集

并查集的概念及其实现

并查集是一种简单的集合表示,它支持以下操作:Initialize(初始化)、Find(查找)、Union(合并)。

并查集的存储结构

通常用树的双亲表示作为并查集的存储结构,每个子集合以一棵树表示。多说无益,直接看示意图:

假设有一个S={0,1,2,3,4,5,6,7,8,9,10},初始化他们都是一个个的几个,每个子集合的数组值为-1。

初始表示:

初始

经过系列计算以后,他们合并成为了三个集合:S1={0,6,7,8}S_1=\{0,6,7,8\}S1={0,6,7,8}S2={1,4,9}S_2=\{1,4,9\}S2={1,4,9}S3={2,3,5}S_3=\{2,3,5\}S3={2,3,5}

计算1

又计算,想把S1S_1S1S2S_2S2合并

计算2

结束,很一目了然的并查集

并查集操作的基本实现
#define MAX_SIZE 100 // 并查集的最大大小
int UFSets[MAX_SIZE];// 并查集数组(双亲表示法)
//初始化
void InitUFSets()
{for (int i = 0; i < MAX_SIZE; i++) UFSets[i] = -1; // 每个元素初始化为-1,表示每个元素都是一个独立的集合
}
//Find 找到集合的根元素
int Find(int x)
{while (UFSets[x] >= 0) x = UFSets[x];return x;
}
//Union 合并两个集合
void Union(int Root1, int Root2)
{if (Root1 != Root2) {//要求Root1和Root2是不同的集合UFSets[Root2] = Root1; // 将Root2的根元素指向Root1,表示合并}
}

注意:本代码并没有传递数组参数,因为只是一整个代码,已经放在全局变量中,遇到题目需要自行分析。
复杂度分析:Find操作的时间复杂度为O(d)O(d)O(d)(d为树深度),Union操作的时间复杂度为O(1)。

并查集之优化
//改进后的Union
void Union(int Root1, int Root2) {if(Root1 != Root2) { //要求Root1和Root2是不同的集合if (UFSets[Root1] < UFSets[Root2]) { // Root1的集合更大UFSets[Root1] += UFSets[Root2]; // 更新Root1的大小UFSets[Root2] = Root1; // 将Root2的根元素指向Root1} else {UFSets[Root2] += UFSets[Root1]; // 更新Root2的大小UFSets[Root1] = Root2; // 将Root1的根元素指向Root2}}
}
//改进后的Find,目的在于压缩路径,只要是根的元素,就把他挂在根元素上。
int Find(int x) {int root = x;while(UFSets[root] >= 0) {root = UFSets[root]; // 找到根元素}while(x!=root){int temp = UFSets[x]; // 临时存储当前元素的父节点UFSets[x] = root; // 路径压缩,将当前元素直接指向根元素x = temp; // 移动到下一个元素}return root;
}

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

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

相关文章

MacOS:如何利用终端来操作用户

MacOS&#xff1a;如何利用终端来操作用户MacOS&#xff1a;如何利用终端来操作用户1. 创建用户并赋予管理员权限步骤&#xff1a;2. 取消用户的管理员权限解释&#xff1a;3. 查看组成员查看 admin 组成员&#xff1a;查看 users 组成员&#xff1a;4. 其他常见的用户管理命令…

基于SpringBoot+MyBatis+MySQL+VUE实现的医疗挂号管理系统(附源码+数据库+毕业论文+答辩PPT+项目部署视频教程+项目所需软件工具)

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对医疗挂号信息管理的提升&#x…

学成在线项目

黑马程序员学成在线项目学习过程记录 解决跨域问题

Shell脚本-grep工具

一、前言在 Linux/Unix 系统中&#xff0c;grep 是一个非常强大且常用的文本搜索工具&#xff0c;它可以帮助我们快速从文件或标准输入中查找匹配特定模式的内容。无论是查看日志、调试脚本&#xff0c;还是进行自动化数据提取&#xff0c;grep 都扮演着至关重要的角色。本文将…

深入解析Ext2文件系统架构

要在硬盘上存储文件&#xff0c;必须先将硬盘格式化为特定类型的文件系统。文件系统的主要功能就是组织和管硬盘中的文件。在Linux系统中&#xff0c;最常见的文件系统是Ext2系列&#xff0c;其早期版本为Ext2&#xff0c;后续又发展出Ext3和Ext4。虽然Ext3和Ext4对Ext2进行了功…

商业秘密保护:从法律理论到企业实战

作者&#xff1a;邱戈龙、柯坚豪深圳商业秘密律师广东长昊律师事务所在商业竞争中&#xff0c;商业秘密就像企业的"隐形护城河"。从法律角度看&#xff0c;它的保护路径经历了三次重要升级&#xff1a;从最初的"合同约定"到后来的"财产保护"&…

AI产品经理面试宝典第36天:AI+旅游以及行业痛点相关面试题的指导

一、AI如何解决旅游行业核心痛点? 面试官提问: "请结合具体案例说明AI在旅游行业的应用价值,以及它如何解决传统旅游服务的痛点?" 你的回答: 以腾讯"一部手机游云南"为例,AI技术通过四大核心体系重构旅游体验: 数字身份体系:通过人脸识别与用户…

【conda】Linux系统中部署Conda环境

目录 一、安装 Miniconda 1.1 下载 Miniconda 安装脚本 1.2 运行安装脚本 1.3 初始化 Conda&#xff1a; 安装完成后&#xff0c;初始化 Conda 环境 1.4 验证安装 二、设置虚拟环境默认存放路径&#xff08;可选&#xff09; 三、conda创建虚拟环境 3.1 创建 Conda 环境…

Spring Boot 解决跨域问题

在 Spring Boot 中解决跨域问题&#xff08;CORS&#xff09;主要有三种常用方式&#xff0c;下面详细说明每种实现方法&#xff1a; 方案一&#xff1a;全局配置&#xff08;推荐&#xff09; 在配置类中实现 WebMvcConfigurer 接口&#xff0c;统一配置所有接口的跨域规则&am…

Softhub软件下载站实战开发(十九):软件信息展示

上一篇文章中我们上线了软件分离展示&#xff0c;本篇文章我们聚焦软件信息展示 软件列表信息展示 点击一级分类查询该分类下所以软件分类切换要有动画效果分页支持 核心实现 <transition-grouptag"div"class"software-grid"before-enter"before…

[HDLBits] Cs450/gshare

Branch direction predictor 分支方向预测器 A branch direction predictor generates taken/not-taken predictions of the direction of conditional branch instructions. It sits near the front of the processor pipeline, and is responsible for directing instructio…

[学习] 双边带调制 (DSB) 与单边带调制 (SSB) 深度对比

双边带调制 (DSB) 与单边带调制 (SSB) 深度对比 文章目录双边带调制 (DSB) 与单边带调制 (SSB) 深度对比**数学原理****调制表达式与频谱****时域特性****频域特性****Python 仿真代码****仿真结果分析****工程应用建议**数学原理 设基带信号为 m(t)m(t)m(t)&#xff08;带宽为…

Gitee 提交信息的规范

在使用 git push 命令将代码推送到 Gitee&#xff08;或任何 Git 平台&#xff09;时&#xff0c;引号中的信息通常指的是 提交信息&#xff08;Commit Message&#xff09;。提交信息是对本次代码修改的简要描述&#xff0c;规范的提交信息有助于团队协作和版本管理。 Gitee 提…

C 语言经典编程题实战:从基础算法到趣味问题全解析

在 C 语言学习过程中&#xff0c;通过实战编程题巩固知识点是非常有效的方式。本文整理了一系列经典 C 语言编程题&#xff0c;涵盖基础计算、逻辑判断、图形打印等多个维度&#xff0c;并附上完整代码与解析&#xff0c;适合初学者参考学习上机题1.计算n以内所有正奇数的和 ?…

Chapter 3 Design of Switching Power Regulators

Chapter 3 Design of Switching Power Regulators Power Management Techniques for Integrated Circuit Design by Ke-Horng Chen 这本书比较深, 简单介绍基本概念后, 就直接抛出大段公式和结论, 一章讲其他书几章内容, 适合有一定基础, 想进一步做电源系统的人查阅. 优点是不…

算法题(176):three states

审题&#xff1a; 本题需要我们找到最佳铺设道路&#xff0c;将三个国家联通起来&#xff0c;然后输出最佳铺设道路的铺设数量&#xff0c;若没有联通方法则输出-1 思路&#xff1a; 首先我们正面思考&#xff1a;只需从某个点出发然后搜索到三个国家即可&#xff0c;最后对比所…

BIOS+MBR微内核加载loader程序实现过程

上一篇讲到的微内核程序是由BIOS例程自动加载到内存中运行的,而且大小有限,能做的事情有限。我们知道内核程序大小是可以扩展的不能只有512字节,同时在加载运行内核前还需要完成一些必要的实模式下才能做的准备工作。所以单纯在实模式下只使用微内核程序是不太够的,就有了加…

使用Proxy设计模式来增强类的功能:ToastProxy和DesktopToast的设计关系

使用代理模式来增强类的功能&#xff1a;ToastProxy和DesktopToast Documentation: v1.0.0 Specified for Version v1.12.0&#xff0c;First Release in 2025/7/12 Documenation belongs to Projects: Charliechen114514/CCIMXDesktop: This is a Simple Desktop with Common …

瑞芯微2025开发者大会之见闻

序言本人参加了2025年的瑞芯微开发者大会&#xff0c;在展览区看到了很多有意思的音视频产品&#xff0c;下面按照产品类型分类给大家做一下展示。期间并没有将所有展出物进行拍摄&#xff0c;但是基本已经覆盖大部分内容。1、RK3566该芯片内置DSP音频处理器&#xff0c;蓝牙5.…

【最新】Java的几种设计模式详解及适用业务场景

✅ 1. 单例模式&#xff08;Singleton&#xff09; 定义&#xff1a;确保类只有一个实例&#xff0c;并提供全局访问点。优点&#xff1a;节省资源、控制访问。场景&#xff1a;数据库连接池、日志管理器、配置中心。代码要点&#xff1a; 构造方法私有静态变量保存唯一实例公共…