241. 在一个二维01矩阵中找到全为1的最大正方形,返回其面积。
样例 10 10 0 10 111 11111 10 0 10 返回4
public int max Square (int [] [] matrix; { // write your code here int row = matrix, length; /#丁大小 int line = matrix [0]. length. //列大小 //一个与matrix相同大小的辅助数组 int [] [] tmp = new int [row] [line]; W matrix的第一行和第一列元素直接存放到 for(int i=0;i<row;i++){ tmp [i] [0] = matrix [i] [0:; for (int i=0;Kline;i++) { tmp [0] [i] = matrix [0] [i:; for(int i=l;i<row;i++){ for(int j=l;j<line;j++)[ if(matrix[i] [j] == 1){ tmp[i] [j]= Math, min (Math, mm ( tmp[i-l] [j], tmp[i] [j-1]:, tmp[i-l] [j-1]) + 1; if(matrix[i] [j] == 0){ tmp[i] [j] = 0; int max=0; //记录tmp中最大元素的值(tmp中元素值表示正方形的边长) for(int i=0;i<row;i++){ for(int j=0;j<line;j++)[ if (tmp[i] [j] > max)[ max = tmp[i] [j]. return max*max;
242.常见的HTTP状态码
1XX系列响应代码仅在与HTTP服务器沟通时使用。
243. 25匹马,5个赛道,最少赛多少次找岀前三没有计时, 只有先后.
7次。理由如下: 1. 先分开赛5组(A-E), 5次,每组的最后两名肯定会被淘汰,(-10)o 2. 5组第一名赛一次,假设A1>B1>C1> D1>E1,那么Al肯定是总体第一名。则D, E全部被淘汰(-6).现在需要在剩下的里面职2个,那么C2,C3,B3也会被淘汰(-3)。 3. 那么就剩ITA2, A3, Bl, B2, C1 (,冉募一次,取前两名(-3)。 最多7次比呑,前5次总共淘汰10匹,箕6次淘汰9匹,第7次淘汰3匹。总共淘汰 22匹。
244. 计算机为什么能识别二进制机器码/p>
1、计算机的理论基础 布尔代数是计算机的理论基础, Boolean (布尔运算)通过对两个以上的物体进行并集、差集、交集的运算,从而得到 新的物体形态。系统提供了 4种布尔运算方式:Union (并集)’Intersection (交集)和 Subtraction (差集,包括和B-A两种). 1) 与逻辑和乘法 乘法原理中自变量是因变量成立的必要芸件,与逻辑的定义正好和乘法原理的描述一 致,所以与逻辑和乘法对应。 2) 或逻辑和加法 加法原理中自变量是因变量成立的充分芸件,或逻辑的定义正好和加法原理的描述一 致,所以或逻辑和加法对应。 乘法就是广义的与逻辑运算,加法就是广义的或逻辑运算。与逻辑运算可以看作是乘法 的特例。或逻辑运算可以看作是加法的特例。 总之,乘法原理、加法原理可以看作是与逻辑和或逻辑的定量表述;与逻辑和或逻辑可 以看作是乘法原理、加法原理的定性表述。 通俗来讲:这是一门运用”与“”或“” “非〃 “假〃 “真”来描述任意两个量(可以 是任何具体事物的或者抽象概念)的逻辑关系。
2、 逻辑代数与计算机电路 应用于逻辑中,解释。为假,1为真,△为与,V为或,的非。涉及变量和布尔运 算的表达式代表了陈述形式,两个这样的表达式可以使用上面的公理证实为等价的,当且仅 当对应的陈述形式是逻辑等价的。由于逻辑彳燉小的逻辑单元与二进制高度契合,再加上电 路最为简单的开和关恰好也对应。和1,于是就有了依据逻辑代数理论创建一系列的电路在 表达基础的逻辑理论,这就是计算机具有判断、计算能力的基础。
3、 二进制机器识别过程 根据前面两点可以知道,如果选用二进制原理作为计算机的判断计算依据,将会使得电 路制造的实现成为可能,但是自然界是不存在二进制的,为了处理这个问题,统一人为规定 将其他非二进制数据表示成二进制机器码,供计算机读取。然而。随着对数据的处理能力要 求越来越高,处理数据也越来越大,为了解决这个问题,汇编器出现,替代了将非二进制数 据转化为二进制数据,但是这远远不足,为了更好处理,直接将硬件与汇编器组合,单独发 展更高级汇编器(实质就是现在熟知的各类程序),这样,硬件与软件彻底分开。实质上就 是将数据转化与判断和数据的录入、存储、输出彻底分幵,使计算机的使用者可以完全不必 再关注计算机的具体运算。 也就是说,计算机为什么能够识别二进制机器码,是因为有以逻辑代数原理制造的数字 电路,为什么选用二进制已经解释过了。同旺,也应该明白,为何程序会出现假,1为真, A为与,V为或,呐非这五个元素,算法对何而来,指的就是优化数据之间的逻辑代数 关系。
245.如何理解mac寻址/p>
曲C地址是 卡的物理地址,每块 卡都有一个属于自己的独有的MAC地址,如 00-0A-EB-97-5F-65。 IP地址就是我们常见的202.106.46.151 192.168.1.1这样的。 通过MC地址寻找主机是MC地址寻址,通过IP地址寻找主机叫IP地址寻址。它们适 用于不同的协议层。
246. 如何理解IO多路复用的三种机制Select, Poll, Epoll/p>
1. Select 首先先分析一下select函数 int select ( int maxfdpi, fd_set *readset, fd_set *wxiteset, fd_set *exceptset, const struct timeval *timeout ); 【参数说明】 int maxfdpl指定待测试的文件描述字个数,它的值是待测试的最大描述字加1。 fd_set *readset , fd_set *writeset , fd_set *exceptset fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即 文件句柄。中间的三个参数指定我们要让内核测试读、写和异常条件的文件描述符集合。 如果对某一个的条件不感兴趣,就可以把它设为空指针。 const struct timeval *timeout timeout B知内核等待所指定文件描述符集合中的任 何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。 【返回值】 int若有就绪描述符返回其数目,若超旺则为。,若出错则为-1 select运行机制 select ()的机制中提供一种fd.set的数据结构,实际上是一个long类型的数组,每一 个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或 设备句柄)建立联系,建立联系的工作由程序员完成,当调用select ()时,由内核根 据10状态修改fd.set的内容,由此来通知执行了 select ()的进程哪一 Socket或文件 可读。 从流程上来看,使用select函数进行10请求和同步阻塞模型没有太大的区别,甚至还 多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select 以后最大的优势是用户可以在一个线程内同时处理多个socket的10请求。用户可以注 册多个socket,然后不断地调用select读职被激活的socket,即可达到在同一个线程 内同时处理多个I。请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达 到这个目的。 select机制的问题 每次调用select,都需要把fd_set集合从用户态拷贝到内核态,如果fd_set集合很 大时,那这个开销也很大 同时每次调用select都需要在内核遍历传递进来的所有fd.set,如果fd.set集合很 大时,那这个开销也很大 为了减少数据拷贝带来的性能损坏,内核对被监控的f d_set集合大小做了限制,并且 这个是通过宏控制的,大小不可改变(限制为1024). 2. Poll poll的机制与select类似,与select任本质上没有多大差别,管理多个描述符也是 进行轮询,根据描述符的状态进行处理,但是POU没有最大文件描述符数量的限制。 也就是说,Poll只解决了上面的问题3,并没有解决问题1, 2的性能开销问题。 下面是P11的函数原型: int poll(struct pollfd *fds, nfds_t nfds, int timeout); typedef struct pollfd {
/p>
添加图片注释,不超过 140 字(可选)
//需要被检测或选择的文件描述符 //对文件描述符fd上感兴趣的事件 //文件描述符fd上当前实际发生的事件 } pollfd_t; poll改变了文件描述符集合的描述方式,使用了 pollfd结构而不是select的fd_set 结构,使得Poll支持的文件描述符集合限制远大于select的1024 【参数说明】 struct pollfd *fds fds是一个struct pollfd类型的数组,用于存放需要检测其状 态的socket描述符,并且调用poll函数之后fds数组不会被清空;一个pollfd结构 体表示一个被监视的文件描述符,通过传递fds指示pollO监视多个文件描述符。其 中,结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域,结构 体的revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域 nfds_t nfds记录数组fds中描述符的总数量 【返回値】 int函数返回fds集合中就绪的读、写,或出错的描述符数量,返回。表示超时,返回 Epoll epoll在Linux2.6内核正式提出,是基于事件駆动的I/O方式,相对于select来说, epoll没有描述符个数限制,使用一个文件描述符管理多个描述符,将用户关心的文件 描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一 次。 Linux中提供的epoll相关函数如下: int epoll_create(int size); int epoll_ctl( int epf d, int op, int fd, struct epoll_event *event ); int epoll_wait( int epf d, struct epoll_event * events, int maxevents, int timeout 1) . epoll_create函数创建一个epoll句柄,参数size表明内核要监听的描述符数量。 调用成功时返回一个epoll句柄描述符,失败时返回-1。 2) .epoll_ctl函数注册要监听的事件类型。四个参数解释如下: epfd表示epoll句柄 □P表示fd操作类型,有如下3种 EPOLL_CTL_ADD 注册新的 fd 到 epfd 中 EPOLL_CTL_MOD修改已注册的fd的监听事件 EPOLL_CTL_DEL 从 epfd 中删除一个 fd fd是要监听的描述符 event表示要监听的事件 epoll_event结构体定义如下: struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; uint32_t u32; _uint64_t u64; } epoll_data_t; 3). epoll.wait函数等待事件的就绪,成功时返回就绪的事件数目,调用失败时返回 -1,等待超时返回0。 ⑴epfd是epoll句柄 ⑵events表示从内核得到的就绪事件集合 (3)maxevents告诉内核events的大小 ⑷timeout表示等待的超时事件 epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用 10接口 select/poll的増强版本,它能显著提高程序在大量并发连接中只有少量活跃 的情况下的系统CPU利用率。原因就是获职事件的时候,它无须遍历整个被侦听的描述 符集,只要遍历那些被内核10事件异步唤塑而加入Ready队列的描述符集合就行了。 epoll除了提供select/poll那种10事件的水平触发(Level Triggered)夕卜,还提供 了边缘触发(Edge Triggered),这就使得用户空间程序有可能緩存10状态,减少 ep 01 l_wai t /ep o 1 l_pwai t的调用,提高应用程序效率。 ⑴水平触发(LT):默认工作模式,即当epoll-wait检测到某描述符事件就绪并 通知应用程序时,应用程序可以不立即处理该事件;下次调用epoll.wait时,会 再次通知此事件 ⑵边缘触发(ET):当epoll_wait检测到某描述符事件就绪并通知应用程序时, 应用程序必须立即处理该事件。如果不处理,下次调用epoll.wait时,不会再次 通知此事件。(直到你做了某些操作导致该描述符变成未就绪状态了,也就是说边 缘触发只在状态由未就绪变为就绪旺只通知一次)。 LT和ET原本应该是用于脉冲信 的,可能用它来解释更加形象。Level和Edge指的就 是触发点,Level为只要处于水平,那么就一直触发,而Edge则为上升沿和下降沿的 时候触发。比如:0->1就是Edge, 1->1就是Levelo ET模式很大程度上减少了 epoll事件的触发次数,因此效率比LT模式下高。
247. 给定一个数,算岀平均值是整数的算法。例如;5平均2 份,则结果为,2和支8平均为3份,则为2, 3, 3; 11平均4份,则为2, 3, 3, 3; 15平均3份,则为;5, 5, 5o
public class Divide { public static int sum(int[] res)[ int sum=0; for(int i:res) { sum += i; return sum; public static void aveDivide(int n, int n) { if (n <= 0){ return; int res [] = new int[n]; int i = 0; while(true) { if(sum(res) == m) { break; } else { res[i%n] x 1; if public static void main(String f] args) { aveDivide(15,10);
248. linux内核调度详细说一下
i.i、调度策略 定义位于 liniax/include/uapi/linux/sched. h 中 SCHED项ORMAL:普通的分时进程,使用的f air_sched_class调度类 SCHED_FIFO:先进先出的实时进程。当调用程鳶把CPU分配给进程的时候,它把该进程 描述符保留在运行队列链表的当前位置。此调度策略的进程一旦使用CPU则一直运行。如果 没有其他可运行的更高优先级实时进程,进程就继续使用CPU,想用多久就用多久,即使还 有其他具有相同优先级的实时进程处于可运行状态。使用的是rt.sched.class调度类。 SCHED_RR:时间片轮转的实时进程。当调度程序把CPU分配给进程的时候,它把该进程 的描述符放在运行队列链表的末尾。这种策略保证对所有具有相同优先级的SCHED_RR实时 进程进行公平分配CPU时间,使用的rt.sched.class调度类 SCHED_BATCH:是SCHED项0RMAL的分化版本。采用分时策略,根据动态优先级,分配 CPU资源。在有实时进程的时候,实时进程优先调度。但针对呑吐量优化,除了不能抢占外 与常规进程一样,允许任务运行更长时间,更!子使用高速緩存,适合于成批处理的工作,使 用的fair_shed_class调度类 SCHED_IDLE:优先级最低,在系统空闲旺运行,使用的是idle_sched_class调度类, 给。 进程使用 SCHED_DE.WLINE:新支持的实时进程调度策略,针对突发型计算,并且对延迟和完成时 间敏感的任务使用,基于EDF (earliest deadline first),使用的是dl_sched_class调 度类。 1.2、调度触发
/p>
添加图片注释,不超过 140 字(可选)
调度的触发主要有两种方式,一种是本地定时中断触发调用scheduler.tick ,然 后使用当前运行进程的调度类中的task_tick,另外一种则是主动调用schedule,不管是哪 —种最终都会调用到—schedule函数,该函数调用pick_netx_task,通过rq->nr.running ==rq->cfS.h_nz_running判断出如果当前运行队列中的进程都在cfs调度器中,则直接调 用cfs的调度类(内核代码里面这一判断使用)likely说明大部分情况都是滴足该条件的)。 如果运行队列不都在cfs中,贝通过优先级 stop_s ched_c1as > dl_s ched_c1ass”t_s che d_c1ass”air_s ched_class~>idle_s ched_ class遍历选出下一个需要运行的进程。然后进程任务切换。 处于TASK_RUNNIMG状态的进程才会被进程调度器选择,其他状态不会进入调度器。系 统发生调度的时机如下: &调用 cond_r e s che d ()时 &显式调用schedule ()时 &从中断上下文返回时 当内核开启抢占时,会多出几个调度时机如下: d在系统调用或者中断上下文中调用preemJenableO时(多次调用系统只会在最后一 次调用时会调度) d在中断上下文中,从中断处理函数返回到可抢占的上下文时 2、CFS调度 该部分代码位于 1 inux /k erne 1 /s che d/f air. c 中 定义了 const struct sched_c 1 assf air_sched_c 1 ass,这个是 CFS 的调度类定义的对 象。其中基本包含了 CFS调度的所有实现。 CFS实现三个调度策略: 1> SCHED项ORMAL这个调度策略是被常规任务使用 2> SCHED_BATCH这个策略不像常规的任务那样频繁的抢占,以牺牲交互性为代价 下,因而允许任务运行更长的时间以更好的利用緩存,这种策略适合批处理 3> SCHED.IDLE这是nice值甚至比19还弱,但是为了避免陷入优先级导致问题, 这个问题将会死锁这个调度器,因而这不是一个真正空闲定时调度器 CFS调度类: n enqueue-task (-)当任务进入runnable状态,这个回调将把这个任务的调度实体 (entity)放入红黑树并且増加nz.running变量的值 n de queue* ask (…)当任务不再是runnab le状态,这个回调将会把这个任务的调度 实体从红黑树中职出,并且减少nz.running 量的值 n yield_task(-)除非compat_yield sysctl是打开的,这个回调函数基本上就是 一个dequeue后跟一个enqueue,这那种情况下,他将任务的调度实体放入红黑树的最右端 n checkereempt_cuzr (-)这个回调丞数是检查一个任务进入runnable状态是否 应该抢占当前运行的任务 n pick_next_taSk (-)这个回调函数选出下一个最合适运行的任务 n Set_cuzr_taSk (-)当任务改变他的调度类或者改变他的任务组,将调用该回调函 数 n taSk_tick(-)这个回调函数大多数是被time tick调用。他可能引起进程切换。 这就駆动了运行时抢占 2.1、CFS 调度 Tcik中断,主要会更新调度信息,然后调整当前进程在红黑树中的位置。调整完成以 后如果当前进程不再是最左边的叶子,就标记为Need.resched标志,中断返回时就会调用 scheduler ()完成切换、否则当前进程继续占用CPU。从这里可以看出CFS抛弃了传统时间 片概念。Tick中断只需要更新红黑树。 红黑树键值即为runtime,该值通过调用update_cuzr函数进行更新。这个值为 64位的变量,会一直逢増,_enqueue_entity中会将vxuntime作为键值将要入队的实体插 入到红黑树中。_pick_first_entity会将红黑树中最左侧即vxuntime最小的实体取出。
249.如何从10亿数据中找到前1000大的数/p>
public class TopN { //当前节点的父节点
private int parent(int n){ return (n~l)/2; } //当前节点的左子节点 private int left(int n){ return 2*n+l; } //当前节点的右子节点 private int right(int n){ return 2*n+2; } //构建堆 private void buildHeap (int n, int [] data) { for(int i=l;i<n;i++){ intt=1 while(t!=0 && data[parent(t)]>data[t]) { int temp = data[parent(t)]; data [parent (t) ]=data[t]; data[t]=temp; t=parent (t); //调整堆,为小顶堆 private void adjust (int i, int n, int [] data) { if (data[O]>=data[i]) { return; L int temp = data[i]; data[i] = data[0]; data[0] = temp; int t=0; //调整时,堆顶比子节点大,从较小的子节点幵始调整 while((left(t)<n&&data[t]>data[left(t) ]) || (right (t) <n&&data[t: >data [right (t) ])) { if((right(t) < n && data[right(t)] < data[left(t)])){ temp=data[t]; data [t] =data [right (-)]; data[right(t)]=temp. t=right(t); }else{ temp=data[t]; data[t]=data[left (t!]; data[left(t)]=temp; //寻找topN数 public void f indTopN (int n, int.] data) { buildHeap(n, data); for (int i=n; i<data. length; iT{ adjust (i, n, data); //打印 public void print(int[] data){ for (int i=0; i<data. length; L) { System, out. print (data [i. “);
250. 用UDP通讯如何保证对方百分百收到数据/p>
在UDP通讯中,当你的数据包发出去后,至于对方有没有正确收到数据,并不知道,那 么,如何保证你发出去的数据,对方一定能收到呢们可以借签”P协议的做法(回 复+ 重发+ 编 机制) 1) 接收方收到数据后,回复一个确认包,如果你不回复,那么发送端是不会知道接收 方是否成功收到数据的 比如A要发数据” {data}”到B,那B H姪U后,可以回复一个特定的确认包” {0K}” , 表示成功收到。 但是如果只做上面的回复处理,还是有I礪,比如B收到数据后回复给A的数据〃{0K}〃 的包,A没收到,怎么办呢2) 当A没有收到B的〃{0K}〃包后,要做定时重发数据,直到成功接收到确认包为止, 再发下面的数据,当然,重发了一定数量后还是没能收到确认包,可以执行一下姗的流程, 防止对方 卡更换或别的原因。 但是这样的话,B会收到很多重复的数据,假如每次都是B回复确认包A收不到的话。 3) 发送数据的包中加个标识符,比如A要发送的数据〃{标识符|data}〃到B, B收到后, 先回复“{0K} 〃确认包,冉根据丿泉有的标识符进行比较,如果标识符相同,则数据丢失,如 果不相同,则原有的标识符=接收标识符,且处理数据。 当A发送数据包后,没有收到确认包,贝拷隔x秒,把数据重发一次,直到收到确认包 后,更新一下标识符,再进行后一包的数据发送。 经过上面1),2), 3)点的做法,则可以保证数据百分百到达对方,当然,标识符用 ID 来代替更好。
251. 如何初始化一个指针数组/p>
例如:定义f指针瞄*a[5] 让其各个指针头地1 畛另指向 {1,1,1,1,1}, {2,2,2,2,21, {3,3,3,3,31, {4,4,4,4,41, {5,5,5,5,51
int a[5]= {….} int* pt [5]; for(i=0;i<5;i++) [ pt[i] = &a[i]; }
指针数组不可以直接赋值,因为指针本质上是一组地址。但可以通 过“指向”赋值
252. 你所知道的设计模式有哪些/p>
有23种设计模式,不需要所有的回答,但是其中常用的几种设计模式应该去掌握。 总体来说设计模式分为三大类: 创建型模式共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合 模式、享元模式。 行为型模式共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模 式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 这里主要讲解简单工厂模式,代理模式,适配器模式,单例模式4中设计模式. 1、 简单工厂模式 主要特点是需要在工厂类中做判断,从而创造相应的产品,当増加新产品时,需要修改 工厂类。使用简单工厂模式,我们只需要知道具体的产品型 就可以创建一个产品。 缺点:工厂类集中了所有产品类的创建谗辑,如果产品量较大,会使得工厂类变的非常 腕肿。 2、 代理模式 代理模式:为其它对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象 不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作 用。 优点: 职责清晰。真实的角色只负责实现实际依业务逻辑,不用关心其它非本职责的事务,通 过后期的代理完成具体的任务。这样代码会简洁清晰。 代理对象可以在客户端和目标对象之间起到中介的作用,这样就保护了目标对象。 扩展性好。 3、 适配器模式 适配器模式可以将一个类的接口转换成容户端希望的另一个接口,使得原来由于接口不 兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而 这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。 适配器类需要继承或依赖已有的类,实现想要的目标接口。 缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调 用的是A接口,其实内部被适配成了 B接口的实现,一个系统如果太多出现这种情况,无 异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 4、单例模式 单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的 全局接口。实现单例模式必须注意一下几点: 单例类只能由一个实例化对象。 单例类必须自己提供一个实例化对象。 单例类必须提供一个可以访问唯一实例化对象的接口。 单例模式分为懒汉和欲汉两种实现方式。
253. 如何计算struct占用的内存/p>
1. 每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐 2. 确定的对齐参数必须能够整除起始地址(或偏移量) 3. 偏移地址和成员占用大小均需对齐 4. 结构体成员的对齐参数为其所有成员使用的对齐参数的最大值 5. 结构体总长度必须为所有对齐参数的整数倍
#include<stdio. h> struct test [ char a; int b; float c; }; int main(void) { printf(^char=%dn°j sizeof(char):; printf(“int=%dn”, sizeof(int)); printf (“f loat=%dn”, sizeof (floa~)); printf(°struct test=%dn°, sizeof(struct test)); return 0; I 执行结果为1, 4, 4, 12
占用内存空间的计算过程: 对齐参数为4。假设结构体的起始地址为Ox。 a的类型为char,因此所占内存空间大小为1个字节,小于对齐参数4,所以选择1 为对齐数,而地址0x0能够被1整除,所以0x0为a的起始地址,占用空间大小为1个字节: b的类型为int ,所占内存空间大小为4个字节,与对齐参数相同,因此4为对齐数, 0x1不能被4整除,因此不能作为b的起始地址,依次往下推,只能选用0x4作为b的起始 地址,因此中间会空出3个字节的空余空间; c的类型为float,占用4个字节,因此4为对齐数,0x8能被4整除,所以c的起始 地址为0x8 因此整个结构体占用的内存大小为12字节。
254. mysql为什么要使用B+树作为索引呢/p>
b树的特点: 一个M阶的b树具有如下几个特征:(如下图M=3)(下文的关键字可以理解为有效数 据,而不是单纯的索引) 定义任意非叶子结点最多只有M个儿子,且M>2; 根结点的儿子数为2 M]; 除根结点以外的非叶子结点的儿子数为[恥,M],向上取整;(儿子数:[2, 3]) 非叶子结点的关键字个数=儿子数-1 ;(关键字=2) 所有叶子结点位于同一层; k个关键字把节点拆成k+1段,分别指向蚌1个儿子,同时满足查找树的大小关系。(k=2) 有关b树的一些特性,注意与后面的b+树区分: 关键字集合分布在整颗树中; 任何一个关键字出现且只出现在一个结点中; 搜索有可能在非叶子结点结束; 其搜索性能等价于在关键字全集内做一次二分查找:
/p>
添加图片注释,不超过 140 字(可选)
/p>
添加图片注释,不超过 140 字(可选)
b+树,是b树的一种变体,查询性能更好。m阶的b+树的特征:
有n棵子树的非叶子结点中含有n个关键字(b树是n-l个),这些关键字不保存数据, 只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据)。
所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子 结点本身依关键字的大小自小而大顺序链接。
所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。同一个数字会在不同节点中重复出现,根节点的最大元素就是b+树的最大元素。
/p>
添加图片注释,不超过 140 字(可选)
B+树的中间节点不保存数据,是纯索引,但是B树的中间节点是保存数据和索引的,相对来说,B+树磁盘页能容纳更多节点元素,更“搂胖” 5
B+树查询必须查找到叶子节点,B树只要匹配到即可不用管元素位置,因此b+树查找更 稳定(并不慢);
对于范围查找来说,B+树只需遍历叶子节点链表即可,B树却需要重复地中序遍历,在 项目中范围查找又很是常见的
増删文件(节点)时,效率更高,因为E+树的叶子节点包含所有关键字,并以有序的 链表结构存储,这样可很好提高増删效率。
255. 输入ping IP后敲回车,发包前会发生什么/p>
ping目标ip时,先查路由表,确定出接口
如果落在直连接口子 内,此时若为以太 等 多路访问 络 则先查询axp緩存,命 中则直接发出,否则在该接口上发axp询问目标ip的mac地址,职得后发出,若为PPP等 点 对点 络,则直接可以发出;
如果查表落在缺省路由上,此时若为以不 等多路访问 络则先查询 关axp緩 存,命中则直接发出,否则在该接口上发axp询问 关的mac地址,取得后发出,若为ppp 等点对点 络,则直接可以发出;
若查表未命中,则返回不可达。
256. 对于一颗二叉树,如何对此进行层次遍历,并且按行输出。
解法1:
class Solution {
public:
vector<vector<int>> levelOrder(Hode* root) {
BFS (root, 0);
return res;
}
public:
void BFS(Node* node, int level)
[
if(node == nullptr) return;
if (level == res. sizeO)
res. push_back (vector<in*:> ());
res[level]. push_back(node”>Tal);
for(auto n:node~>children)
BFS(n, level+1);
private:
vector<vector<int>^ res;
};
解法2:
class Solution {
public:
vector<vector<int>> levelOrder(Hode* root) { vector〈vectoroutput;
if (Jroot) return output;
queue<Node*> treeTemp; treeTemp. push(root);
auto treeNum = treeTemp. size();
while (treeNum) {
vector<int> valTemp;
for (auto i=0; KtreeNun; ++i) { root = treeTemp. front(); valTemp. push_back (root->val); treeTemp. pop();
for (auto childTemp:root”>children) treeTemp. push(childTemp);
}
output. push_back(valTemp); treeNum = treeTemp. size 3 ;
}
return output;
357. 如何实现一个高效的单向链表逆序输岀/p>
static class Node{
private int data;
private Node next;
Node(int d){
data = d;
next = null;
}
public int getDataO {
return data;
public void setData(int data) { this, data = data;
}
public Node getNext() {
return next;
public void setNext(Node next) { this, next = next;
public static void reverse(Node head)
{
if(null == head || null == head. getNext()){ return;
Node prev=null;
Node pcur = head. getNext();
Node next;
while (pcur i=null){
if (pcur.getNext()==null: {
pcur.setNext(prev);
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!