FLOPS
FLOPS 是 Floating Point Operations Per Second 的缩写,意为每秒浮点运算次数。它是衡量计算机性能的指标,特别是用于衡量计算机每秒能够执行多少浮点运算。在高性能计算领域,FLOPS 被广泛用来评估超级计算机、CPU、GPU 和其他处理器的计算能力。FLOPS 数值越高,计算机执行涉及浮点数的计算能力越强。
SQL
- 子查询
可以用在FROM子句的数据源
SELECT * FROM (SELECT column1, column2 FROM table1 WHERE condition) AS subquery_table;
可以用作WHERE和HAVING的条件
SELECT column1 FROM table1 WHERE column2 = (SELECT column2 FROM table2 WHERE condition);
SELECT column1 FROM table1 GROUP BY column1 HAVING COUNT(*) > (SELECT COUNT(*) FROM table2);
不能在ORDER BY 和 GROUP BY中使用
- 查看所有表
SHOW TABLES;
- 删除所有数据
DELETE FROM table_name;
- 存储引擎:
存储引擎是数据库系统中负责管理数据存储和检索的组件。不同的数据库管理系统可能支持多种存储引擎,每种存储引擎都有自己的特点和适用场景。常见的存储引擎包括 InnoDB、MyISAM、PostgreSQL 的 pg_tablespace 等。选择适合的存储引擎可以根据应用的特点和需求,平衡性能、事务支持、数据完整性等方面的考量。 - 事务处理:
事务是一组数据库操作,要么全部执行成功,要么全部失败。事务处理提供了一种机制,用于确保数据库的一致性和完整性。事务通常具有四个特性,即 ACID 特性:
原子性(Atomicity):事务是不可分割的工作单元,要么全部执行成功,要么全部回滚。
一致性(Consistency):事务执行前后,数据库状态必须保持一致。
隔离性(Isolation):并发执行的事务之间应该互相隔离,一个事务的执行不应影响其他事务。
持久性(Durability):事务一旦提交,对数据库的修改应该是永久性的,即使系统崩溃也不应该丢失。 - 行级锁定:
行级锁定是数据库管理系统提供的一种锁定机制,用于控制并发访问时的数据完整性和一致性。**行级锁定可以确保同时只有一个事务可以修改某行数据,从而避免了多个事务同时修改同一行数据时的竞争和冲突。**相比表级锁定或页面级锁定,行级锁定可以提高并发性和系统的吞吐量,但也可能会增加系统开销和复杂度。
软件系统设计阶段
需求分析阶段:
在此阶段,团队与客户合作,收集、分析和定义软件系统的需求和功能。
目标是确保对系统需求的充分理解,并建立一个准确的需求规格说明书。
概要设计阶段:
在此阶段,设计团队制定系统的高层结构和模块化方案,定义系统的整体架构。
重点是确定系统的组成部分、模块之间的交互和接口,并制定系统的基本框架。
详细设计阶段:
在此阶段,设计团队对概要设计阶段的结果进行详细的设计,包括数据结构、算法、接口和数据流等。
目标是定义系统的具体实现细节,为编码和实施提供详细的指导。
体系结构设计:
在大型系统中,可能会有一个专门的阶段来设计系统的整体架构,包括硬件、软件、通信和数据存储等方面。
这个阶段重点是确定系统的分层结构、组件和服务,以及它们之间的交互和通信机制。
界面设计:
界面设计阶段专注于用户界面的设计,包括图形用户界面 (GUI)、命令行界面 (CLI)、应用程序编程接口 (API) 等。
目标是设计用户友好的界面,使用户能够轻松使用系统并进行交互。
数据库设计:
数据库设计阶段涉及设计数据库的结构、表和关系,确定数据模型和存储方案。
目标是设计一个能够有效存储和管理数据的数据库系统,满足系统的数据管理需求。
安全设计:
安全设计阶段关注系统的安全需求和防御措施,包括身份验证、访问控制、数据加密和漏洞修复等。
目标是确保系统能够抵御各种安全威胁和攻击,保护用户的数据和系统的机密性、完整性和可用性。
性能设计:
性能设计阶段关注系统的性能需求和优化策略,包括系统响应时间、吞吐量、并发性和可伸缩性等方面。
目标是设计一个高性能的系统,以满足用户的性能期望并提供良好的用户体验。
缓存一致性协议
分布式系统是由多台计算机或节点组成的网络系统,这些计算机分布在不同的地理位置,并通过网络连接起来,共同工作以完成特定的任务或提供特定的服务。分布式系统的设计目标是通过将计算、存储和通信任务分布到多个节点上,实现高性能、高可用性和可扩展性。
缓存一致性协议是一组规则和算法,用于确保分布式系统中的多个缓存副本保持一致。在分布式系统中,多个节点可能会缓存相同的数据副本,为了提高性能和减少网络开销。
一些常见的缓存一致性协议包括:
- 写回策略:
写回策略允许缓存副本在被修改后,不立即将数据更新到其他节点的缓存中,而是在某个时机进行批量更新。这样可以减少网络通信开销,但会增加数据一致性的维护成本。 - 写通过策略:
写通过策略要求在更新缓存时,必须先更新主节点的缓存,然后再更新其他节点的缓存。这确保了数据更新的顺序性,但可能会增加延迟和网络负载。 - 发布-订阅模式:
发布-订阅模式通过发布者和订阅者之间的消息传递来维护缓存一致性。当数据更新时,发布者向所有订阅者发送通知,订阅者根据通知来更新自己的缓存。 - 基于时间戳的方法:
基于时间戳的方法使用版本号或时间戳来标识数据的更新顺序,确保较新的数据会覆盖旧的数据。这样可以保证数据的一致性,但需要额外的时间戳管理和冲突解决机制。 - 基于一致性哈希的分布式缓存:
在分布式缓存系统中,使用一致性哈希算法来将数据分布到不同的节点上,确保每个节点上的数据量大致相等。这样可以提高系统的性能和可伸缩性,但需要考虑数据分布不均和节点失效的情况。
里氏替换
里氏替换原则是面向对象编程中的一个重要原则,由计算机科学家Barbara Liskov提出,该原则表明:如果S是T的一个子类型,那么程序中任意可以使用类型T的地方,都可以替换为类型S而不影响程序的正确性。
换句话说,子类型必须能够替换掉其父类型,而不会引起不良后果。这意味着子类在继承父类时,应该保持与父类一致的行为,以确保程序的正确性和稳定性。通过遵循里氏替换原则,可以提高代码的可扩展性和可维护性,并减少软件系统的错误和意外行为。
跳表 SkipList
跳表(Skip List) 是一种数据结构,类似于有序链表,但具有额外的高效性能。跳表利用了多层索引来加速查找操作,跳表的搜索、删除、添加的平均时间复杂度是 O(logn)
跳表由很多层结构组成,level是通过一定的概率随机产生的;
每一层都是一个有序的链表,默认是升序 ;
最底层(Level 1)的链表包含所有元素;
每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。
Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。
Redis是一个开源的基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis支持多种数据结构,如字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等,并提供了丰富的操作命令来对这些数据结构进行操作。Redis常被用于缓存、消息队列、会话存储等场景,它的高性能、丰富的数据结构和灵活的应用场景使得它成为了广泛应用于各种Web应用和分布式系统中的重要组件。
消息队列是一种用于在应用程序之间传递消息的通信方式。它可以在不同的组件、服务或系统之间传递异步消息,提供了一种解耦和解决通信延迟的方式。能够在分布式系统中解决各种通信问题,并提高系统的可伸缩性、可靠性和性能。
-
图示
-
搜索操作O(logn)
- 从顶层链表的首元素开始,从左往右搜索,直至找到一个大于或等于目标的元素,或者到达当前层链表的尾部
- 如果该元素等于目标元素,则表明该元素已被找到
- 如果该元素大于目标元素或已到达链表的尾部,则退回到当前层的前一个元素,然后转入下一层进行搜索
-
插入操作O(logn)
-
删除操作O(logn)
在跳表中删除某个结点时,如果这个结点在索引中也出现了,我们除了要删除原始链表中的结点,还要删除索引中的。因为单链表中的删除操作需要拿到删除结点的前驱结点,然后再通过指针操作完成删除。所以在查找要删除的结点的时候,一定要获取前驱结点(双向链表除外)。因此跳表的删除操作时间复杂度即为O(logn)。 -
索引动态更新
- 跳表是通过随机函数来维护“平衡性”。
- 当我们不断地往跳表中插入数据时,我们如果不更新索引,就有可能出现某2个索引节点之间的数据非常多的情况,在极端情况下,跳表还会退化成单链表
- 当我们在跳表中插入数据的时候,我们通过选择同时将这个数据插入到部分索引层中,如何选择索引层,可以通过一个随机函数来决定这个节点插入到哪几级索引中,比如随机生成了k,那么就将这个索引加入到,第一级到第k级索引中。
python 数组
a={1,2,3,4,5,6,7,8,9} b = a[ : : 2]
在 Python 中,a[::2] 中的两个冒号 :: 分别表示切片操作的起始位置、结束位置和步长。在这种情况下,第一个冒号前面没有数字,表示从列表的第一个元素开始切片;第二个冒号后面也没有数字,表示切片一直到列表的最后一个元素。步长为 2 表示每隔一个元素取一个元素。
a[::2] 将取出索引为偶数位置上的元素,即取出 1, 3, 5, 7, 9,并组成一个新的列表。因此,b 将是 {1, 3, 5, 7, 9}。
标识符命名规则
标识符可以由字母(大小写均可)、数字和下划线 _ 组成,但不能以数字开头。
Java: Java语言允许标识符以 $ 开头,但通常不建议在代码中使用。在Java中,$ 开头的标识符通常用于编译器生成的特殊标识符,例如用于内部类或者生成的匿名类。
继承类型
单继承(Single Inheritance): 即一个子类只能继承自一个父类。这种继承类型下,子类可以覆盖父类的方法,并在此基础上进行扩展。单继承是许多编程语言(如Java、Python等)的默认继承方式。
多继承(Multiple Inheritance): 一个子类可以同时继承自多个父类。在多继承中,子类可以从多个父类中继承方法,并覆盖其中的方法,从而实现对多个父类的功能进行扩展。多继承的实现方式在不同的编程语言中有所不同,例如在Python中支持多继承,但在一些其他编程语言中,如Java,则不直接支持多继承。
数据库概念
主键(Primary Key): 主键是表中用于唯一标识每个记录的字段或字段组合。主键必须保证唯一性,并且不允许为空值。在关系型数据库中,主键通常用于建立表之间的关系,并且可以作为其他表的外键参考。主键必须是最小化的。
超码(Super Key): 超码是可以唯一标识表中每条记录的字段或字段组合。与主键不同的是,超码可以包含冗余的字段,即包含比最小化的主键更多的字段。(但不一定在所有数据中唯一)
候选码(Candidate Key):候选码通常作为候选主键,用于确定主键的选择范围。候选码具有唯一性和最小性的特点,即它们可以唯一标识每条记录,且没有冗余字段。
外键(Foreign Key): 外键是表中的一个字段或字段组合,它引用了另一个表中的主键,用于建立表之间的关联关系。外键确保了数据的完整性,可以用来维护表之间的一致性和关联性
内码(Index Key): 内码是用于加速数据库查询的一种技术,它是对表中的某个字段或字段组合创建的索引。内码可以提高查询性能,减少查询时间,特别是对于大型数据库来说,具有重要意义。
数据库范式
第一、第二和第三范式以及BCNF(Boyce-Codd范式)是数据库设计中用来规范关系数据库模式设计的一组规范化理论。它们的主要目的是消除数据中的冗余,并确保数据库结构的良好设计。
第一范式(1NF):
第一范式要求数据库表中的每个列都是原子性的,即每个列都不可再分。换句话说,表中的每个属性都应该是不可再分的基本数据类型,不允许有多个值或多个属性组合在同一列中。如果存在多个值,应该将其拆分为独立的列。
例如,一个包含学生信息的表,如果一个字段包含了多个电话号码,那么这个字段就不符合第一范式。
第二范式(2NF):
第二范式要求数据库表中的每个非主属性完全依赖于候选键(Candidate Key)。换句话说,表中的每个非主属性都应该完全依赖于表中的主键,而不依赖于部分主键。
例如,在一个订单信息表中,如果订单号和产品号构成了联合主键,那么产品价格应该依赖于产品号,而不是仅依赖于订单号。
第三范式(3NF):
第三范式要求数据库表中的每个非主属性不依赖于其他非主属性,即不存在传递依赖。换句话说,表中的每个非主属性应该直接依赖于主键,而不依赖于其他非主属性。
例如,在一个员工信息表中,如果存在员工部门和部门经理之间的关系,那么员工表中的部门经理字段应该直接依赖于员工的部门字段,而不是间接依赖于员工的编号。
BCNF(Boyce-Codd范式):
BCNF是对第三范式的一种扩展和更严格的要求。它要求数据库表中的每个非平凡函数依赖(即非主属性完全函数依赖于候选键)都是基于候选键的超码的一个属性集合。换句话说,表中的每个非平凡函数依赖都必须是候选键的超码。
候选码的超码就是包含了候选码的任何超集。
BCNF消除了第三范式中可能存在的对候选键的部分依赖。
调度方法
先来先服务调度(First Come, First Served,FCFS):
场景:适用于对任务执行顺序不做要求的情况。所有到达的任务按照到达的顺序排队执行。
优点:简单易实现。
缺点:不考虑任务的执行时间长短或优先级,可能导致长任务阻塞短任务。
短作业优先调度(Shortest Job First,SJF):
场景:适用于需要最小化平均等待时间的场景。执行时间最短的任务首先得到执行。
优点:最小化平均等待时间,提高系统吞吐量。
缺点:需要准确预测任务执行时间,难以应对变化频繁的任务情况。
优先级调度(Priority Scheduling):
场景:适用于需要根据任务的优先级分配CPU时间的场景。每个任务有一个优先级,优先级高的任务优先执行。
优点:能够根据任务的重要性灵活调配资源。
缺点:可能导致低优先级任务长时间等待,产生饥饿问题。
时间片轮转调度(Round Robin Scheduling):
场景:适用于多用户系统,要求公平性和实时性的场景。每个任务被分配一个时间片,轮流执行。
优点:公平地分配CPU时间,避免长任务阻塞其他任务。
缺点:可能导致任务切换开销增加,响应时间不稳定。
多级反馈队列调度(Multilevel Feedback Queue Scheduling):
场景:适用于混合了交互式和批处理任务的场景。任务根据优先级分配到不同的队列,每个队列采用不同的调度算法。
优点:灵活地适应不同类型的任务。
缺点:配置和管理较为复杂,需要合理设置多个队列的参数。
TCP/IP
传输控制协议(Transmission Control Protocol,TCP):TCP是一种面向连接的、可靠的、基于字节流的传输层协议。它负责在网络中建立可靠的数据传输通道,确保数据的可靠性、完整性和顺序性。TCP通过使用确认、重传、拥塞控制等机制来实现可靠传输。TCP常用于需要高可靠性和顺序传输的应用,如网页浏览、电子邮件传输等。
互联网协议(Internet Protocol,IP):IP是一种网络层协议,负责在网络中传输数据包。它提供了一种不可靠的、无连接的服务,主要用于将数据包从源主机传输到目标主机。IP负责数据包的路由、分组和转发,但不关心数据包的完整性、顺序或传输是否成功。IP是TCP/IP协议族中最基础的协议,为实现网络互联提供了基础。
路由
IP地址和MAC地址的映射表通常存储在设备的ARP(Address Resolution Protocol)缓存中,包括路由器。ARP是一种用于获取网络层地址(如IPv4地址)和链路层地址(如MAC地址)之间映射关系的协议。
操作系统内存管理
- 分页(Paging)
将进程的地址空间分割成固定大小的单元,称为页Page。相应的,物理内存分割成相同大小的页帧Page Frame,每个这样的页帧包含一个虚拟内存页(匹配)。
通过页表(Page Table)来映射逻辑地址到物理地址,当程序访问某个逻辑地址时,操作系统会根据页表找到对应的物理地址。
virtual address - 页号VPN+偏移量offset
灵活性:操作系统能够高效的提供地址空间的抽象,不管进程如何使用地址空间
简单性:空闲空间管理只用找空闲页。 - 分段(Segmentation)
分段是将进程地址空间划分为逻辑上独立的段(Segment)的技术,每个段具有不同的长度和属性。
每个段包含不同类型的数据或代码,例如代码段、数据段、堆栈段等。
分段技术通过段表(Segment Table)来映射逻辑地址到物理地址,每个段表项包含了段的起始地址(基址寄存器)和长度(界限寄存器)等信息。
分段的机制使得操作系统能够将不同的段放到不同的物理内存区域,从而避免了虚拟地址空间中的未使用部分占用物理内存,并且不需要物理内存提供一大块连续区域来放置完整的地址空间,会更加灵活。
存在碎片化的问题。
程序运行时栈
栈是一种后进先出(LIFO)的数据结构,它以自底向上的方式存储这些数据。在典型的实现中,栈的底部是内存的较高地址,栈的顶部是内存的较低地址。当函数调用时,栈会向下增长;当函数返回时,栈会向上收缩,恢复到之前的状态。
在程序运行时,栈自底向上分别存放着先进入的数据,比如函数调用相关信息(如上下文寄存器的值、旧的栈指针值)、参数,然后是局部变量(栈帧(Stack Frame)的数据结构),最后是最近进入的数据,如返回地址等。
CAP定理
CAP定理是分布式系统中的一个基本原理,它指出在设计分布式系统时,无法同时满足以下三个属性:
一致性(Consistency):所有节点看到的数据是一致的。在任何时刻,所有节点都具有相同的数据视图,即如果某个节点修改了数据,那么其他节点读取该数据时应该能够立即看到修改后的结果。
可用性(Availability):系统在任何时刻都能够处理用户请求,即系统保证了服务的可用性,不会因为节点故障或其他原因而导致服务不可用。
分区容错性(Partition Tolerance):系统在遇到网络分区(网络故障)时仍能够继续运行。即使网络出现分区,系统仍然能够保持部分节点的通信和工作。
CAP定理的核心思想是,在分布式系统中,由于网络的不确定性,无法同时保证一致性、可用性和分区容错性这三个属性。因此,系统设计者需要在这三个属性之间进行权衡和取舍,根据具体的应用场景和需求来选择适当的设计方案。
图形学
在图形渲染中,将2D图形呈现为3D效果通常涉及使用透视投影(Perspective Projection)算法。
透视投影的基本原理是根据物体距离观察者的远近关系,将三维空间中的物体投影到二维平面上。具体来说,透视投影通过将物体的每个顶点(或每个像素)从三维空间映射到二维平面,然后根据其在视点处的距离进行缩放,使得远处的物体映射得更小,近处的物体映射得更大,从而产生近大远小的视觉效果。
Z缓冲(Z-buffer)是图形学中用于实现三维场景渲染的一种技术,也称为深度缓冲(Depth Buffer)。它是一种用于处理透视投影的技术,用于解决遮挡关系,确保在复杂场景中正确渲染物体的显示顺序。
Z缓冲是一个与屏幕像素大小相同的二维数组,用于记录每个像素的深度值(Z值)。在渲染三维场景时,对于每个像素,都会计算其在视野中的深度值(Z值),即该像素对应的物体距离观察者的距离。这些深度值被存储在Z缓冲中。
当渲染三维场景时,对于每个像素,渲染引擎都会检查其深度值和Z缓冲中对应位置的值。如果当前像素的深度值比Z缓冲中对应位置的值更小(即更靠近观察者),那么当前像素将被渲染,同时更新Z缓冲中的深度值。反之,如果当前像素的深度值比Z缓冲中对应位置的值更大(即更远离观察者),那么当前像素将被丢弃,不进行渲染。
Z缓冲则是在进行透视投影后,用于解决深度排序和遮挡关系的一种技术。
OSI模型(7)
1 物理层
2 数据链路层
3 网络层
4 传输层
5 会话层
6 表示层
7 应用层
n节点完全二叉树的非叶子节点数量
完全二叉树(Complete Binary Tree)是一种特殊的二叉树,具有以下特点:
每一层的节点都是从左向右依次填充的,即在同一层上,从左到右填满节点,不存在间隙。
叶子节点(最底层的节点)全部集中在树的最左侧。
最后一层的节点可以从左到右不完全填满,但是缺失的节点只能集中在最右侧。
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
最后一层的节点数等于该层的索引,即 【n/2】
非叶子节点的数量为 n−⌊n/2⌋ 或 n−最后一层节点数。
关于树
https://blog.csdn.net/weixin_74059671/article/details/127621518?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171385798716800226574031%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=171385798716800226574031&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-127621518-null-null.142v100pc_search_result_base6&utm_term=B%E6%A0%91&spm=1018.2226.3001.4187
二叉搜索树BST
**二叉搜索树(Binary Search Tree,BST)**是一种二叉树的特殊形式,它满足以下性质:
对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值。
对于树中的每个节点,其右子树中的所有节点的值都大于该节点的值。
左子树和右子树也都是二叉搜索树。
因此,BST具有一种有序的结构,使得在BST中进行查找、插入和删除等操作具有较高的效率O(logn),n 是树中节点的数量。
AVL树
AVL树是一种自平衡的二叉搜索树,由Soviet inventors Adelson-Velsky和Landis在1962年提出。AVL树在添加或删除节点时会通过旋转操作来保持树的平衡,以确保树的高度在任何时候都是尽可能小的,从而保证了查找、插入和删除操作的时间复杂度为 O(log n)。
在AVL树中,任意节点的左子树和右子树的高度差不超过1。即对于任意节点,其左子树的高度和右子树的高度相差不超过1。
红黑树
为了保证AVL树的平衡性,我们在插入删除时频繁地调整全树整体的拓扑结构,代价较大。因此,我们对二叉排序树不再进行高度平衡的限制(AVL树),而进行适度平衡的限制(红黑树)。这样在保证查找效率的同时我们进行插入删除操作所付出的代价也更小,因此红黑树的实际应用更广泛,C++中的map和set(Java中的TreeMap和TreeSet)就是用红黑树实现的。
一棵红黑树是满足如下红黑性质的二叉排序树:每个结点或是红色,或是黑色的。根结点是黑色的。叶结点(虚构的外部结点,NULL结点)都是黑色的。如果一个结点是红色的,则它的两个子结点都是黑色的。对每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数量相同。
B树
B树,又称多路平衡查找树(或B-树、B_树)。
B树种所有结点的孩子个数的最大值称为B树的阶,通常用m表示
所有非叶结点的结构如下:
(i=1,2,…,n)为结点的关键字,且满足K1<K2<…<Kn;Pi(i=1,2,…,n)为指向子树根结点的指针,且指针Pi−1所指子树中所有结点的关键字均小于Ki,Pi所指子树中所有结点的关键字均大于Ki,n(⌈m/2⌉-1≤n≤m-1)为结点中关键字的个数。
B树是应用于文件系统的一种平衡的多路查找树,B树的查找操作涉及外存的存取,而磁盘IO的时间代价昂贵,因此我们需要降低树的深度选择多路查找树。
* 节点分裂
B+树
B+树是应文件系统所需而出的B树的变种,实际应用于文件索引和数据库索引。B+树单一结点由于不含有关键字对应记录的存储地址,因此可以存储更多的关键字,使得查询的IO次数更少,同时B+树的查找路径从根结点到叶结点,查询效率更加稳定。此外B+树在范围查找上的优势更大,因为叶结点形成有序链表。
在B+树中,叶结点包含全部信息,所以非叶结点仅起索引作用,非叶结点中每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
在B+树中,叶结点包含了全部关键字,非叶结点中出现的关键字也会出现在叶结点中;而在B树中,内部结点的关键字都是不重复的。
并查集
原文链接:https://blog.csdn.net/the_ZED/article/details/105126583
定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。
主要构成:
并查集主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。
数组 pre[ ] 记录了每个点的前驱节点是谁,函数 find(x) 用于查找指定节点 x 属于哪个集合,函数 join(x,y) 用于合并两个节点 x 和 y 。
作用:
并查集的主要作用是求连通分支数
**路径压缩(Path Compression)**是并查集中一种用于优化查找操作的技术。它通过在查找操作中将节点的父节点直接指向根节点,从而减少后续查找操作的路径长度,提高了并查集的效率。
临界资源访问
信号量(Semaphore)是一种用于控制多个进程或线程对共享资源的访问的同步机制。信号量维护一个整数值,通常表示为 S,它可以被多个进程或线程同时访问和修改。
信号量的基本操作包括:
初始化:在使用信号量之前,需要对其进行初始化,确定其初始值。
等待(Wait):当一个进程或线程需要使用共享资源时,首先尝试对信号量进行等待操作。如果信号量的值大于 0,则可以继续访问资源;否则,进程或线程会被阻塞,直到信号量的值大于 0 为止。
信号(Signal):当一个进程或线程使用完共享资源后,需要对信号量进行信号操作,即增加信号量的值。这样可以通知其他等待资源的进程或线程,资源已经释放,可以继续访问。