如何理解c/c++和php语言的区别(16)


19. 有千万个string在内存怎么高速查找 , 插入和删除?
对千万个string做hash , 可以实现高速查找 , 找到了 , 插入和删除就很方便了 。关键是如何做hash , 对string做hash , 要减少碰撞频率 。
20.100亿个数 , 求最大的1万个数 , 并说出算法的时间复杂度在内存中维护一个大小为10000的最小堆 , 每次从文件读一个数 , 与最小堆的堆顶元素比较 , 若比堆顶元素大 , 则替换掉堆顶元素 , 然后调整堆 。最后剩下的堆内元素即为最大的1万个数 , 算法复杂度为O(NlogN)
21.设计一个洗牌的算法 , 并说出算法的时间复杂度 。(1)全局洗牌法
a)首先生成一个数组 , 大小为54 , 初始化为1~54
b)按照索引1到54 , 逐步对每一张索引牌进行洗牌 , 首先生成一个余数 value = https://www.isolves.com/it/cxkf/yy/C/2021-12-15/rand %54 , 那么我们的索引牌就和这个余数牌进行交换处理
c)等多索引到54结束后 , 一副牌就洗好了
(2)局部洗牌法:索引牌从1开始 , 到54结束 。这一次索引牌只和剩下还没有洗的牌进行交换 ,  value = https://www.isolves.com/it/cxkf/yy/C/2021-12-15/index + rand() %(54 - index)
算法复杂度是O(n)
22.请分别用递归和非递归方法 , 先序遍历二叉树
24.其他各种排序方法
25.哈希表冲突解决方法?
常见的hash算法如下:

  1. 1. 数字分析法 2.平方取中法 3.分段叠加法4.除留余数法 5.伪随机法
解决冲突的方法:
  1. 1. 开放地址法
也叫散列法 , 主要思想是当出现冲突的时候 , 以关键字的结果值作为key值输入 , 再进行处理 , 依次直到冲突解决
线性地址再散列法
当冲突发生时 , 找到一个空的单元或者全表
二次探测再散列
冲突发生时 , 在表的左右两侧做跳跃式的探测
伪随机探测再散列
  1. 2. 再哈希法
同时构造不同的哈希函数
  1. 3. 链地址法
将同样的哈希地址构造成一个同义词的链表
  1. 4. 建立公共溢出区
建立一个基本表和溢出区 , 凡是和基本元素发生冲突都填入溢出区
六、系统架构
1.设计一个服务 , 提供递增的SessionID服务 , 要求保证服务的高可靠性 , 有哪些方案?集中式/非集中式/分布式
2.多台服务器要执行计划任务 , 但只有拿到锁的任务才能执行 , 有一个中心服务器来负责分配锁 , 但要保证服务的高可靠性 。
3.如何有效的判断服务器是否存活?服务器是否踢出集群的决策如何产生?
4.两个服务器如何在同一时刻获取同一数据的时候保证只有一个服务器能访问到数据?
可以采用队列进行处理 , 写一个队列接口保证同一时间只有一个进程能够访问到数据 , 或者对于存取数据库的来说 , 数据库也是可以加锁处理的
5. 编写高效服务器程序 , 需要考虑的因素
性能对服务器程序来说是至关重要的了 , 毕竟每个客户都期望自己的请求能够快速的得到响应并处理 。那么影响服务器性能的首要因素应该是:
(1)系统的硬件资源 , 比如说CPU个数 , 速度 , 内存大小等 。不过由于硬件技术的飞速发展 , 现代服务器都不缺乏硬件资源 。因此 , 需要考虑的主要问题是如何从“软环境”来提升服务器的性能 。
服务器的”软环境“
(2)一方面是指系统的软件资源 , 比如操作系统允许用户打开的最大文件描述符数量
(3)另一方面指的就是服务器程序本身 , 即如何从编程的角度来确保服务器的性能 。
主要就要考虑大量并发的处理这涉及到使用进程池或线程池实现高效的并发模式(半同步/半异步和领导者/追随者模式) , 以及高效的逻辑处理方式--有限状态机内存的规划使用比如使用内存池 , 以空间换时间 , 被事先创建好 , 避免动态分配 , 减少了服务器对内核的访问频率 , 数据的复制 , 服务器程序还应该避免不必要的数据复制 , 尤其是当数据复制发生在用户空间和内核空间之间时 。如果内核可以直接处理从socket或者文件读入的数据 , 则应用程序就没必要将这些数据从内核缓冲区拷贝到应用程序缓冲区中 。这里所谓的“直接处理” , 是指应用程序不关心这些数据的具体内容是什么 , 不需要对它们作任何分析 。比如说ftp服务器 , 当客户请求一个文件时 , 服务器只需要检测目标文件是否存在 , 以及是否有权限读取就可以了 , 不需要知道这个文件的具体内容 , 这样的话ftp服务器就不需要把目标文件读入应用程序缓冲区然后调用send函数来发送 , 而是直接使用“零拷贝”函数sendfile直接将其发送给客户端 。另外 , 用户代码空间的数据赋值也应该尽可能的避免复制 。当两个工作进程之间需要传递大量的数据时 , 我们就应该考虑使用共享内存来在他们直接直接共享这些数据 , 而不是使用管道或者消息队列来传递 。上下文切换和锁:并发程序必须考虑上下文的切换问题 , 即进程切换或线程切换所导致的系统开销 。即时I/O密集型服务器也不应该使用过多的工作线程(或工作进程) , 否则进程间切换将占用大量的CPU时间 , 服务器真正处理业务逻辑的CPU时间比重就下降了 。因此为每个客户连接都创建一个工作线程是不可取的 。应该使用某种高效的并发模式 。(半同步半异步或者说领导者追随者模式)另一个问题就是共享资源的加锁保护 。锁通常被认为是导致服务器效率低下的一个因素 , 因为由他引入的代码不仅不处理业务逻辑 , 而且需要访问内核资源 , 因此如果服务器有更好的解决方案 , 应该尽量避免使用锁 。或者说服务器一定非要使用锁的话 , 尽量使用细粒度的锁 , 比如读写锁 , 当工作线程都只读一块内存区域时 , 读写锁不会增加系统开销 , 而只有当需要写时才真正需要锁住这块内存区域 。对于高峰和低峰的伸缩处理 , 适度的缓存 。


推荐阅读