1.sdk-demo项目最大的难点,如何解决?
2.比赛的思路?
华为2020软件精英挑战赛
初赛赛题:给出一批转账记录,找出其中的循环转账路径,要求转账路径长度大于等于3且小于等于7。
初始思路:根据输入构造每个节点能够到达的节点序列,按序遍历每个节点能到达的路径,直到路径出现第一个与头元素相同或者路径长度大于7,当条路径遍历终止。
https://github.com/eaglesunshine/CodeCraft_2020/blob/master/CodeCraft_2020/main.cpp
优化版本:
由于搜索中存在大量冗余,而且输出中环路按字典序输出,可以联想到去重和排序并不是必须的。考虑1->2->3->4->1的一条环路,在以2为起点时,同样有等价的环路2->3->4->1->2。在搜索中,这两次是冗余的。
同理可推导知:在搜索过程中,邻接点中只需要遍历比起点id大的节点,这样即可省去去重;在搜索前对邻接表排序,即可省去排序。
3.内存碎片是什么,存在那些地方,如何解决?
内存碎片有两种:a.内部碎片 b.外部碎片 。
- 内部碎片的产生:因为所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个 43 字节的内存块时,因为没有适合大小的内存,所以它可能会获得 44字节、48字节等稍大一点的字节,因此由所需大小四舍五入而产生的多余空间就叫内部碎片。
- 外部碎片的产生: 频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。假设有一块一共有100个单位的连续空闲内存空间,范围是0-99。如果你从中申请一块内存,如10个单位,那么申请出来的内存块就为0-9区间。这时候你继续申请一块内存,比如说5个单位大,第二块得到的内存块就应该为10-14区间。如果你把第一块内存块释放,然后再申请一块大于10个单位的内存块,比如说20个单位。因为刚被释放的内存块不能满足新的请求,所以只能从15开始分配出20个单位的内存块。现在整个内存空间的状态是0-9空闲,10-14被占用,15-24被占用,25-99空闲。其中0-9就是一个内存碎片了。如果10-14一直被占用,而以后申请的空间都大于10个单位,那么0-9就永远用不上了,变成外部碎片。
如何解决内存碎片:
采用Slab Allocation机制:整理内存以便重复使用。
Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块(chunk),并把尺寸相同的块分成组(chunk的集合)。
memcached根据收到的数据的大小,选择最适合数据大小的slab。memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。
缺陷在于由于分配的是特定长度的内存,因此无法有效利用分配的内存。例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了。
https://blog.csdn.net/zhouwei1221q/article/details/47955231
4.C++的内存对齐是什么?为什么?
为什么需要内存对齐?
-
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
-
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
如何对齐?
strcut/class/union内存对齐规则:
- 没有#pragma pack宏的对齐规则:
(1). 结构体的起始存储位置必须是能够被该结构体中最大的数据类型所整除。
(2). 每个数据成员存储的起始位置是自身大小的整数倍(比如int在32位机为4字节,则int型成员要从4的整数倍地址开始存储)。
(3). 结构体总大小(也就是sizeof的结果),必须是该结构体成员中最大的对齐模数的整数倍。若不满足,会根据需要自动填充空缺的字节。
(4). 结构体包含另一个结构体成员,则被包含的结构体成员要从其原始结构体内部最大对齐模数的整数倍地址开始存储(比如struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储)。
(5). 结构体包含数组成员,比如char a[3],它的对齐方式和分别写3个char是一样的,也就是说它还是按一个字节对齐。如果写:typedef char Array[3], Array这种类型的对齐方式还是按一个字节对齐,而不是按它的长度3对齐。
(6). 结构体包含共用体成员,则该共用体成员要从其原始共用体内部最大对齐模数的整数倍地址开始存储。
- 存在#pragma pack宏的对齐:
(1). #pragma pack (n) // 编译器将按照n个字节对齐
(2). #pragma pack () //取消自定义字节对齐方式
可以通过C++11中的alignas函数来指定类型、对象或变量按多少字节对齐,可以通过alignof函数来判断类型、对象或变量是按多少字节对齐的。
https://blog.csdn.net/fengbingchun/article/details/81270326#comments_14445579
5.扩容后的unordered_map是如何找到老数据的?
unordered_map 原理
hashtable + bucket
由于 unordered_map 内部采用 hashtable 的数据结构存储,所以,每个特定的 key 会通过一些特定的哈希运算映射到一个特定的位置,我们知道,hashtable 是可能存在冲突的,在同一个位置的元素会按顺序链在后面。所以把这个位置称为一个 bucket 是十分形象的,每个哈希桶中可能没有元素,也可能有多个元素。
其插入过程是:
1、得到 key;
2、通过 hash 函数得到 hash 值;
3、得到桶号(一般都为 hash 值对桶数求模);
4、存放 key 和 value 在桶内(发生冲突,用比较函数解决)。
其取值过程是:
1、得到 key
2、通过 hash 函数得到 hash 值
3、得到桶号(一般都为 hash 值对桶数求模)
4、比较桶的内部元素是否与 key 相等,若都不相等,则没有找到。
5、取出相等的记录的 value。
https://www.jianshu.com/p/56bb01df8ac7
6.什么时候会能用到索引?xxx like xx会使用索引吗?
在sql语句有where条件,group by分组以及order by排序和联合查询的时候会用到,而如果sql语句不加上述那些东西就不会用到索引,也就不能提高查询速度。
1、分类
MySQL索引分为普通索引、唯一索引、主键索引、组合索引、全文索引。索引不会包含有null值的列,索引项可以为null(唯一索引、组合索引等),但是只要列中有null值就不会被包含在索引中。
(1)普通索引:create index index_name on table(column);
或者创建表时指定,create table(..., index index_name column);
(2)唯一索引:类似普通索引,索引列的值必须唯一(可以为空,这点和主键索引不同)
create unique index index_name on table(column);或者创建表时指定unique index_name column
(3)主键索引:特殊的唯一索引,不允许为空,只能有一个,一般是在建表时指定primary key(column)
(4)组合索引:在多个字段上创建索引,遵循最左前缀原则。alter table t add index index_name(a,b,c);
(5)全文索引:主要用来查找文本中的关键字,不是直接与索引中的值相比较,像是一个搜索引擎,配合match against使用,现在只有char,varchar,text上可以创建全文索引。在数据量较大时,先将数据放在一张没有全文索引的表里,然后再利用create index创建全文索引,比先生成全文索引再插入数据快很多。
2、何时使用索引
MySQL每次查询只使用一个索引。与其说是“数据库查询只能用到一个索引”,倒不如说,和全表扫描比起来,去分析两个索引B+树更加耗费时间。所以where A=a and B=b这种查询使用(A,B)的组合索引最佳,B+树根据(A,B)来排序。
(1)主键,unique字段;
(2)和其他表做连接的字段需要加索引;
(3)在where里使用>,≥,=,<,≤,is null和between等字段;
(4)使用不以通配符开始的like,where A like 'China%';
(5)聚集函数MIN(),MAX()中的字段;
(6)order by和group by字段;
3、何时不使用索引
(1)表记录太少;
(2)数据重复且分布平均的字段(只有很少数据值的列);
(3)经常插入、删除、修改的表要减少索引;
(4)text,image等类型不应该建立索引,这些列的数据量大(假如text前10个字符唯一,也可以对text前10个字符建立索引);
(5)MySQL能估计出全表扫描比使用索引更快时,不使用索引;
4、索引何时失效
(1)组合索引未使用最左前缀,例如组合索引(A,B),where B=b不会使用索引;
(2)like未使用最左前缀,where A like '%China';
(3)搜索一个索引而在另一个索引上做order by,where A=a order by B,只使用A上的索引,因为查询只使用一个索引 ;
(4)or会使索引失效。如果查询字段相同,也可以使用索引。例如where A=a1 or A=a2(生效),where A=a or B=b(失效)
(5)如果列类型是字符串,要使用引号。例如where A='China',否则索引失效(会进行类型转换);
(6)在索引列上的操作,函数(upper()等)、or、!=(<>)、not in等;
7.InnoDB使用的数据结构是什么?为什么?
mysql innodb数据结构_【mysql学习】InnoDB数据结构
8.Time Wait的作用?
原因1:为了保证客户端发送的最后一个ack报文段能够到达服务器。因为这最后一个ack确认包可能会丢失,然后服务器就会超时重传第三次挥手的fin信息报,然后客户端再重传一次第四次挥手的ack报文。如果没有这2msl,客户端发送完最后一个ack数据报后直接关闭连接,那么就接收不到服务器超时重传的fin信息报(此处应该是客户端收到一个非法的报文段,而返回一个RST的数据报,表明拒绝此次通信,然后双方就产生异常,而不是收不到。),那么服务器就不能按正常步骤进入close状态。那么就会耗费服务器的资源。当网络中存在大量的timewait状态,那么服务器的压力可想而知。
原因2:在第四次挥手后,经过2msl的时间足以让本次连接产生的所有报文段都从网络中消失,这样下一次新的连接中就肯定不会出现旧连接的报文段了。也就是防止我们上一篇文章 为什么tcp是三次握手而不是两次握手? 中说的:已经失效的连接请求报文段出现在本次连接中。如果没有的话就可能这样:这次连接一挥手完马上就结束了,没有timewait。这次连接中有个迷失在网络中的syn包,然后下次连接又马上开始,下个连接发送syn包,迷失的syn包忽然又到达了对面,所以对面可能同时收到或者不同时间收到请求连接的syn包,然后就出现问题了。
https://zhuanlan.zhihu.com/p/51961509
9.滑动窗口的作用?
10.C++的原子锁?
c++11 atomic原子锁
atomic
原子操作:不会被线程调度机制打断的操作,原子操作基于处理器支持原子操作的实现
std::atomic 对部分数据类型进行原子封装,常用int,char,long。c++20之后将支持对非原子类型的封装
std::atomic 既不可复制亦不可移动
原子锁:使用原子操作实现的锁
原子操作可以大幅减少互斥锁的时间开销,但也会带来调试困难。
atomic_flag
std::atomic_flag 是原子布尔类型。不同于所有 std::atomic 的特化,它保证是免锁的
atomic_flag支持test_and_set(),可以用于实现自旋锁
11.C++线程同步的方法?
- 互斥锁
- 条件变量
- 读写锁
- 信号量
https://www.jianshu.com/p/b86474b204cc
12.uuid的雪花算法?
Twitter的雪花算法SnowFlake,使用Java语言实现。
SnowFlake算法产生的ID是一个64位的整型,结构如下(每一部分用“-”符号分隔):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
1位标识部分,在java中由于long的最高位是符号位,正数是0,负数是1,一般生成的ID为正数,所以为0;
41位时间戳部分,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L 60 60 24 365) = 69年;
10位节点部分,Twitter实现中使用前5位作为数据中心标识,后5位作为机器标识,可以部署1024个节点;
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
SnowFlake算法生成的ID大致上是按照时间递增的,用在分布式系统中时,需要注意数据中心标识和机器标识必须唯一,这样就能保证每个节点生成的ID都是唯一的。或许我们不一定都需要像上面那样使用5位作为数据中心标识,5位作为机器标识,可以根据我们业务的需要,灵活分配节点部分,如:若不需要数据中心,完全可以使用全部10位作为机器标识;若数据中心不多,也可以只使用3位作为数据中心,7位作为机器标识。
snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分), 并且效率较高。据说:snowflake每秒能够产生26万个ID。