1.排序
1.1 选择排序
时间复杂度:
插入排序和冒泡排序:时间复杂度,最好是O(n),最坏是0(n^2),因此空间复杂度为常量O(1).
选择排序:无论是哪种情况,时间复杂度为O(n²),空间复杂度为常量O(1)
快排:平均时间复杂为O(nlog2n),最糟糕时将达到O(n²)的时间复杂度,快排的快速排序使用的空间是O(1),最差的情况下空间复杂度为O( n )

1.2 冒泡排序

1.3 插入排序

1.3 快速排序

1.3 列表中某个元素出现的初始位置和结束位置
import java.util.*;
public class Demo {
public static void main(String[] args) {
int [] nums=new int[]{5,7,7,8,8,10};
ArrayList list=queryNums(nums,8);
System.out.println(list);
}
public static ArrayListqueryNums(int [] nums,int target){
ArrayList arrayList =new ArrayList<>();
ArrayList arrayList1 =new ArrayList<>();
for(int i=0;i<=nums.length-1;i++){
if(nums[i] == target){
arrayList.add(i);
}}
if(arrayList ==null){
arrayList.add(-1);
arrayList.add(-1);
return arrayList;
}else{
int i= arrayList.get(0);
int j= arrayList.get(arrayList.size()-1);
arrayList1.add(i);
arrayList1.add(j);
return arrayList1;
}}}
优化思路:二分查找,指针
1.4 数据库中查询name相同的同学过滤后按分数倒序排7列
1.5 找出字符串中个数最多的元素,返回该元素的第一个出现的index

1.6.n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数

1.6 多进程和多线程
1.7 Tcp和Udp的区别
tcp面向链接的,udp是非链接的
tcp 传输数据稳定可靠,适用于对网络通讯质量较高的场景,需要准确无误地传输给对方。比如传输文件,发送邮件,浏览网页等
udp 优点是速度快但可能产生丢包,适用于对实时性要求较高,但是对少量丢包并没有太大要求的场景,比如域名查询,语音通话,视频直播等。
1.8 kfaka 消费机制
测试点:(正确性,重复性,幂等性,顺序)
1.value值如果超过存储范围,是否可用
2.如果对顺序有要求的应用,测试顺序是否正常
3.重复的消息需要滤重
重复消费的原因:kfaka会为每条数据分配一个offset,消费时按着这个顺序去消费,当消费者准备提交offset时进程被重启或者消费过快时,offset没来得及提交时,就会导致重复消费
常见问题:如何保证顺序
一、一个topic只创建1个partition,生产者将所有数据都发送到一个partition(分区),保证了消息的顺序
二、生产者在发送消息的时候指定要发送到那个partition(分区)
生产时:生产者在写的时候,指定一个key,比如指定某个订单id作为key,那么这个订单相关的数据,一定会被分发到同一个partition中去,而且这个partition中的数据一定是有序的。
消费的时候:写多个queue,将具有相同key的数据都存储再同一个queue里,然后N个线程,每个线程分别消费一个queue即可
幂等性:
拿到数据是写入数据库,需要先判断下,是否已经存在,如果已经存在则更新,否则insert
拿到数据是写redis,则直接set就可以
还可以,让生产者发送每条消息的时候,添加全局唯一id,然后消费的时候,根据这个id查有没有这个消息,再做逻辑处理。
1.7 cookie 存储
1.8 判断字符串小括号是否满足使用习惯,
(())return false ((() return false )( return false

1.9 线程的创建方式
(1)继承Thread类创建线程
(2)实现Runnable接口创建线程
(3)使用Callable和Future创建线程
(4)使用线程池例如用Executor框架
通过继承Thread类来创建并启动多线程
1)定义Thread子类,重写run方法,该方法就是线程要完成的任务,run()方法也成为线程执行体。
2)创建Thread子类的实例,也就是创建线程对象
3)启动线程,调用线程的start()方法

通过实现Runnable接口创建并启动线程
1)定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法时线程的执行体
2)创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象(jvm底层只认 new Thread)
3)第三部依然是通过线程对象的start()方法来启动线程

使用Callable和Future创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
call()方法可以有返回值
call()方法可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
>boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务
>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
>boolean isDone():若Callable任务完成,返回True
>boolean isCancelled():如果在Callable任务正常完成前被取消,返回True
介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:
1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值


使用线程池例如用Executor框架
1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象


2.0线程池的创建
通常开发者都是利用Executors提供的通用线程池创建方法,去创建不同配置的线程池,主要区别在于不同的
Executors目前提供了5种不同的线程池创建配置:
1、newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。
2、newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads。
3、newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目。
4、newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
5、newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。
2.0 vim中全局替换
替换命令的基本语法
在全局替换中,ex编辑器使用指定的字符模式对文件中的每行进行检查。在所有行中发现模式的位置,ex使用新字符串来替换模式。
全局替换真正使用的是两个ex命令是 :g(global全局)和:s(switch替换)。
替换命令的语法如下:
:s / old / new /
这将把当前中模式old的第一次出现修改为new。 /(斜杆)是命令不同部分之间的分隔符(当斜杆为该行的最后一个字符时,可不写)
下面这种形式的替换命令:
:s / old / new / g
把当前行old的每次出现改为new,而不只是该行的第一个old。:s命令允许替换串后面带有选项,上面语法中的g代表全局(g选项影响一行中的每个模式,不要把它与影响文件中所有行的:g命令混淆)
通过在:s命令前加上地址前缀,可以把它的范围扩展到多行。例如下面命令把50行到100行的old的每次出现都改为new:
:50, 100 s / old / new / g
下面的命令把整个文件中的old的每次出现都改为new:
:1, $s / old / new / g
我们知道%等价于1,$,所以上行命令也可以这样写:
:% s / old / new / g
4.0 Linux接口监听
[root@localhost ~]# netstat -nlp |grep LISTEN //查看当前所有监听端口·
压测方案:
业务架构:
遇到的问题:
登陆问题用例设计:
抽奖问题用例设计:
反射机制:
每人每天限购问题:
shop
事务的问题
事务特点:ACID
Atomicity(原子性):一个事务是不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚
Consistency(一致性):数据库总是从一个一致性状态转换到另一个一致状态。下面的银行列子会说到。
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态
Isolation(隔离性):通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的
Durability(持久性):一旦事务提交,则其所做的修改就会永久保存到数据库中。
事务的隔离级别
第1级别:Read Uncommitted(读取未提交内容):
引发的问题是——脏读(Dirty Read):读取到了未提交的数据
本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
第2级别:Read Committed(读取提交内容):
这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read)
第3级别:Repeatable Read(可重读)
此级别可能出现的问题——幻读(Phantom Read):
第4级别:Serializable(可串行化)
它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
在这个级别,可能导致大量的超时现象和锁竞争
幻读和不可重复读的区别:
不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)
幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)
sql加索引查询快的原因:
kafka顺序如何保证,怎么保证不丢失消息
redis和mysql的问题
1. MySQL和Redis的数据库类型
MySQL是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢.
Redis是NOSQL,即非关系型数据库,也是缓存数据库,即将数据存储在缓存中(内存中),缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限.
大多数公司的存储都是MySQL + Redis,MySQL作为主存储,Redis作为辅助存储被用作缓存,加快访问读取的速度,提高性能.
MySQL支持sql查询,可以实现一些关联的查询以及统计.
MySQL偏向于存数据,Redis偏向于快速取数据,但Redis查询复杂的表关系时不如MySQL,所以可以把热门的数据放Redis,MySQL存基本数据.
2.mysql加索引查询快
mysql加索引快的原因:优化sql的查询速度最常见的就是给表加合适的索引
3.mysql的执行顺序:
开始->FROM子句->WHERE子句->GROUP BY子句->HAVING子句->ORDER BY子句->SELECT子句->LIMIT子句->最终结果
每个子句执行后都会产生一个中间结果,供接下来的子句使用,如果不存在某个子句,就跳过
4.redis支持的数据类型
String,list,hash ,集合,有序集合
List 和Set什么区别呢,有的面试官会深挖下去,这就实在考察大家的基本功了。
Set可以去重,我们如果有这样业务需求,直接将数据给redis,就可以满足数据去重了

缓存测试问题:
1.缓存的类型和过期时间是否合理
2.每个key对应的value值是否存储正确
3.无缓存时,应用是否能正常运行(缓存降级读库)
4.缓存和数据库一致性
(1)初次写入缓存正常
(2)删除缓存后,重新写入正常
(3)缓存过期,重新写入缓存
(4)数据库操作(增删改),缓存时否更新
5.查看缓存的命中率
缓存的命中率= 命中缓存请求个数/总缓存访问请求个数 = hit/(hit+miss)。命中率是衡量缓存有效性的重要指标,命中率越高,表明缓存的使用率越高。
6.发布期间新旧缓存共存问题
7、高并发下缓存风险
缓存的5中数类型

hashmap和hashset区别
list和set:Set的集合里不允许对象有重复的值,List允许有重复
HashSet不允许重复:第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法

hashmap的底层是什么
1. HashMap的内部数据结构
数组 + 链表/红黑树
HashMap 的底层实现是数组+链表/红黑树的形式的,同时它的数组的默认初始容量是16、扩容因子为0.75,每次采用2 倍的扩容。 也就是说,每当我们数组中的存储容量达到75%的时候,就需要对数组容量进行2 倍的扩容。
在HashMap底层使用数组加(链表或红黑树)的结构完美的解决了数组和链表的问题,使得查询和插入,删除的效率都很高。
每个数据单元都是一个Node结构,Node结构有key字段,有value字段,还有next字段和hash字段。
链表转红黑树,主要有两个指标,其中一个是链表长度达到8,当前散列表数组长度达到64
jdk1.8是红黑树
链表想快速查找中间的元素怎么查
解法1:遍历法。 先遍历一遍统计链表长度,然后再遍历长度的1/2便即是中间节点。 时间复杂度O(3n/2)解法2:快慢指针。 (推荐) 设置两个指向链表头结点的指针,一个指针的遍历速度是另一个指针的2倍,当快的指针遍历完毕,慢指针即是中间节点
单测的常见框架:
java的TestNG和junit(白盒测试、API自动化、UI自动化)
【官网】
https://testng.org/doc/index.html
【简介】
TestNG 是 Java中一个很流行实用的单元测试框架。
它的灵感来源于 Junit ( java 的单元测试框架) 和 Nunit ( .net 的单元测试框架)。
但是它又在此基础上引入了新的东西,使得它更加强大。
关于注解特性方面,可以参考下表:
特性JUnit 4TestNG
测试注解@Test@Test
测试套件在执行之前需要执行的–@BeforeSuite
测试套件在执行之后需要执行的–@AfterSuite
在测试之前需要执行的–@BeforeTest
在测试之后需要执行的–@AfterTest
在一个测试方法所属于的任意一个组的第一个方法被调用之前执行–@BeforeGroups
在一个测试方法所属于的任意一个组的最后一个方法被调用之后执行–@AfterGroups
在当前类的第一个测试方法调用之前执行@BeforeClass@BeforeClass
在当前类的最后一个测试方法调用之后执行@AfterClass@AfterClass
每个测试方法之前需要执行@Before@BeforeMethod
每个测试方法之后需要执行@After@AfterMethod
忽略@ignore@Test(enbale=false)
预期异常@Test(expected = ArithmeticException.class)@Test(expectedExceptions = ArithmeticException.class)
超时@Test(timeout = 1000)@Test(timeout = 1000)
TestNG 和 JUnit 还有两个比较明显的区别:
在Junit 4 中,如果我们需要在方法前面使用 @BeforeClass 和 @AfterClass ,那么该测试方法则必须是静态方法。TestNG 在方法定义部分则更加的灵活,它不需要类似的约束。
TestNG 中子类不会运行父类中的 @BeforeClass 和 @AfterClass, 而在Junit中会先运行父类的@BeforeClass,再运行自己的 @BeforeClass;而 @AfterClass 是先运行自己的,再运行父类的。
经过一番对比之后,TestNG 在参数化测试、依赖测试以及套件测试(组)方面功能比 Junit 更加强大,并且包含了几乎 Junit 的所有功能,所以建议优先选择 TestNG。
【使用场景】
常用于单元测试(白盒测试)
自动化测试的用例管理和用例执行框架(API自动化、UI自动化)。
单链表中间值
创建两个指针,一个指针pslow每次往后移动1个位置,另一个pquick移动两个位置,当pquick移动到最后一个元素时,pslow指向的元素就是中间位置。
压测问题
压测步骤:
1.压测目标的制定
2.压测链路的梳理
3.压测环境的准备
4.压测数据的构造
5.发压测试
6.瓶颈定位及容量微调
7.压测总结
参考文章:
https://juejin.cn/post/6844904147729252366
压测指标:
1.QPS
2.tp90或者tp99
3.cpu 一般 CPU使用率 < 75% 比较合适(美团60%)
4.数据库耗时:数据库就是业务的生命,很多时候业务崩掉是因为数据库挂了
5.内存使用率:内存占用情况,一般观察内存是否有尖刺或泄露
内存泄漏:指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。

6.网络带宽:带宽是否瓶颈
7.接口响应错误率 or 错误日志量
压测场景:
1.高频业务场景(今日头条首页下拉刷新)
2.关键业务场景,使用频率低,一旦出问题就很严重(微信账号登录)
3.性能高消耗场景(淘宝下单)
4.曾经出现过问题的场景
压测数据:
影子表:如果是在线上操作,一般将数据写入影子表(与原数据表在schema上一致的不同名表)而非原数据表,实现压测数据与线上数据隔离
压测过程:
单接口单机->单接口1/4资源->场景化1/4资源->全量资源压测->拨测。
常见问题
举例一些可能会发现的典型问题:
存在多余的http header,导致额外带宽占用
spin_lock对RT影响大,优化锁的方式
调整nginx worker数量可提高性能
不恰当的长链接数
代码实现上对象没有较好复用
cache命中率不符预期
业务流程上存在冗余
缺少一层cache
响应码or错误码可能要继续规范
下游服务资源不足(其他监控、存储)
内部系统对压测的限流,需要变更配置或者协商解除限制
一般监控项包括(也就是压测指标):
核心接口和核心依赖的流量、响应耗时、成功率
消息队列、缓存、数据库
机器物理资源
素数个数统计

二分查找求x的平方根

————————————————
版权声明:本文为CSDN博主「愿好」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_37840000/article/details/79756932
————————————————
版权声明:本文为CSDN博主「愿好」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_37840000/article/details/79756932