1.算法:判断一棵树是否是镜像
https://blog.csdn.net/cuit/article/details/78639682
递归解法:首先判断根是否有左右节点,如果左右节点都有,则判断左节点的右子节点和右节点的左子节点是否有相同的值,并进行递归。
非递归解法:用队列来代替递归过程,把要比较的节点成对放入队列中。
public boolean isSymmetric(TreeNode root)
{
Queue<TreeNode> q = new LinkedList<TreeNode>();
if(root == null) return true;
q.add(root.left);
q.add(root.right);
while(q.size() > 1)
{
TreeNode left = q.poll();
TreeNode right = q.poll();
if(left== null && right == null) { continue; }
if(left == null ^ right == null) { return false; }
if(left.val != right.val) { return false; }
q.add(left.left);
q.add(right.right);
q.add(left.right);
q.add(right.left);
}
return true;
}
2.Java:几个访问修饰符的区别
https://blog.csdn.net/zhangbinu/article/details/53736388
主要记忆defalut是只能在同包中访问。protected可以在同包和子类中进行访问
3.Java:synchronized 和 ReentranLock的区别
https://blog.csdn.net/zheng548/article/details/54426947
明显的区别点:
a.synchronized是一个关键字可以直接修饰方法或者修饰代码块,reentranlock需要定义所并设置加锁和解锁。
b.reentranlock的等待可中断。
c.synchronized是非公平锁,而rlock都可以。
d.rlock可以绑定多个条件。
共同点:
a.都是可重入的。
b.现在来讲性能和优化倾向于synchronized。
sychronized实现原理:sychronized关键字经过编译后会在同步块的前后形成monitirenter和monitorexit两个字节码指令。
ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。
4.数据库:一个表 A B C字段 index(A,B,C) select * from table where A= xx and B>0 order by C 走索引嘛?
放弃索引导致全表扫描的情况
https://blog.csdn.net/illusion_you/article/details/79097287
https://www.cnblogs.com/wangxusummer/p/5329813.html
a.like语句,is null语句,非等于语句,in语句,表达式操作,count(*)
b.组合索引查询条件中没有前导列
c.or语句并且有列非索引
d.组合索引排序时应该按照组合索引中各列的顺序进行排序,这个题目就应该写成order by a,b,c
e.where 语句与ORDER BY语句组合满足最左前缀,但where语句中使用了条件查询,所以B>0这个地方会导致filesort
5.Java:常见的IO异常
https://blog.csdn.net/zhufuing/article/details/38312441
文件不存在异常;传入的参数是文件夹异常;权限错误异常;
6.操作系统:临界区
https://blog.csdn.net/nopoppy/article/details/52644397
临界区:并发程序访问共享资源的程序片段。同一时刻只能一个线程进入,其余线程在临界区外等待。
7.数据库:where语句中有两个索引,怎么执行,第二个还执行不
https://blog.csdn.net/gb4215287/article/details/78037835
与其说是“数据库查询只能用到一个索引”,倒不是说是 和全表扫描/只使用一个索引的速度比起来,去分析两个索引二叉树更加耗费时间,所以绝大多数情况下数据库都是是用一个索引。
8.Java:公平锁与非公平锁
https://blog.csdn.net/z69183787/article/details/50971317
reentrantlock有公平锁和非公平锁两种。公平锁上线程按照他们发出请求的顺序来获取锁。非公平锁则允许后来的线程先得到锁,是一种抢占式的机制。非公平锁性能高于公平锁,原因在于在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。
9.Java:TreeMap的底层原理
底层是红黑树数据结构。插入,删除,检索的复杂度都是logn。
10.网络:osi模型,每一层做了什么事情,有什么协议
https://blog.csdn.net/lisa890608/article/details/8231666
https://www.cnblogs.com/wxd0108/p/7597216.html
物理层 | 比特 |
数据链路层 | 不可靠的物理介质上提供可靠的传输,ARP,PPP,帧中继 |
网络层 | 网间的数据包进行路由选择,IP,ICMP |
传输层 | 端口到端口的层次,TCP、UDP |
会话层 | 主机之间的会话进程,即负责建立、管理、终止进程之间的会话。 |
表示层 | 对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。 |
应用层 | 操作系统或网络应用程序提供访问网络服务的接口。FTP、HTTP |
11.网络:cookie
https://www.cnblogs.com/andy-zhou/p/5360107.html
用于做会话跟踪,cookie是客户端的解决方案。存储的是服务器端发送给客户端的特殊信息,以文本的方式存储在客户端,之后客户端向服务器端发起请求的时候都会附上这些信息。一个用户的所有连接和请求都应该是在一个会话中。生存周期与max_age相关,如果max_age大于0会被持久化存储。
session机制是服务器端的解决方案。创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;
在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。正式这样一个过程,用户的状态也就得以保持了。
- cookie数据存放在客户的浏览器上,session数据放在服务器上;
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session;
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE;
- 单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能超过3K;
12.Java:异常处理是具体怎样执行的
https://blog.csdn.net/jakezhang1990/article/details/72880700
try里面捕获异常,然后跳转到catch模块,如果有finally模块就执行finally模块
1. 一旦产生异常,则首先会产生一个异常类的实例化对象。
2. 在try语句中对此异常对象进行捕捉。
3. 产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功则执行catch语句中的代码。
13.JVM:原子性和可见性
https://blog.csdn.net/qq_33689414/article/details/73527438
原子性:由java内存模型来保障原子性变量操作包括read,load,use,store。基本数据类型的访问读写是有原子性的。指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。sychronized可以做到
可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。变量修改后会将新值同步回主内存,在变量读取前从主内存刷新变量值来保证可见性的。sychronized,final,valitile可以做到。
有序性:线程内保持串行,线程外观察是无序的。volatile,sychronized可以做到。
14.程序:手写消费者生产者伪代码
https://blog.csdn.net/qq_25232555/article/details/77856035
消费者一个类,生产者一个类,然后仓库一个类,同步控制是在仓库的put和get中间做的
实现线程的代码
package zyh.data.main;
import java.util.ArrayList;
public class ProAndCus {
static class Box {
static ArrayList<Apple> list = new ArrayList<>();
static int capacity = 50;
public void put(Apple apple) {
synchronized (this) {
while (list.size() >= capacity) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(apple);
notifyAll();
System.out.println("Add success! sizes is " + list.size());
}
}
public Apple get() {
Apple apple = null;
synchronized (this) {
while (list.size() <= 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
apple = list.remove(list.size() - 1);
notifyAll();
System.out.println("Get finished! sizes is " + list.size());
}
return apple;
}
}
static class Apple {
private String name = "apple";
}
static class producer {
private Box box;
public producer(Box box) {
this.box = box;
}
public void put() {
Apple apple = new Apple();
box.put(apple);
}
}
static class customer {
private Box box;
public customer(Box box) {
this.box = box;
}
public Apple get() {
return box.get();
}
}
public static void main(String[] args) {
Box box = new Box();
for (int i = 0; i < 500; i++) {
new Thread(new Runnable() {
@Override
public void run() {
producer pro = new producer(box);
pro.put();
}
}).start();
}
for (int i = 0; i < 500; i++) {
new Thread(new Runnable() {
@Override
public void run() {
customer cus = new customer(box);
cus.get();
}
}).start();
}
}
}
15
16.计网:子网掩码的作用
https://blog.csdn.net/qq1449301756/article/details/48652117
将某个IP地址划分成网络地址和主机地址两部分。
通过ip和子网掩码可以计算出:网络地址(与运算),广播地址,地址范围(网络地址+1,广播地址-1),本网主机数。
17.计网:mac寻址
http://blog.51cto.com/07net01/586888
交换机利用mac地址表进行下一步的转发,若mac地址未在表中,则向出了来的端口外所有端口发送,收到回应后在mac地址表会记录下mac地址
18.算法:01矩阵求最大正方形
https://blog.csdn.net/u011484326/article/details/76474387
a.暴力搜索,以每个位置为左上角能形成的最大矩形
b.动态规划,dij为以(i,j)为右下角的正方形的最大边,dij = min { d[i-1][j-1],min {d[i-1][j],d[i,j-1]}}+1
如果是求最大的子矩形
https://blog.csdn.net/libin56842/article/details/41849769
首先统计每一行连续1的个数,然后按列累加(相邻的才进行累加),记录最大值即可得到面积
19.网络:http的状态码
http://www.runoob.com/http/http-status-codes.html
https://blog.csdn.net/GarfieldEr007/article/details/77984065
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求,权限错误也在这一步 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
401:未授权
403:禁止访问
404:未找到文件
500:服务器内部错误
502:网关错误
503:网关不可用
504:网关超时
20.数据库事务回滚方法
https://blog.csdn.net/nangeali/article/details/75578557
rollback语句,利用的是回滚日志(undo log)
21.算法:判断一个数是不是2的幂次方
https://www.cnblogs.com/haitao-fan/p/3923412.html
由于2的k次方肯定是一个正数,故这个地方不需要考虑负数的情况。2的k次方的二进制表示应该只有1位为1.故可以用m & (m-1) == 0来进行判断。
22.Linux:gdb调试的原理
https://blog.csdn.net/earbao/article/details/53933594
主要功能的实现依赖于一个系统函数ptrace,通过man手册可以了解到,ptrace可以让父进程观察和控制其子进程的检查、执行,改变其寄存器和内存的内容,主要应用于打断点(也是gdb的主要功能)和打印系统调用轨迹。以x86为例,内核向某个地址打入断点,实际上就是往该地址写入断点指令INT 3,即0xCC。
23.算法:二叉树的中序遍历递归及非递归
递归的方法:首先访问左子树,再访问当前节点,然后访问右子树
非递归的方法:一个栈进行辅助,若当前节点有左子树,则当前节点入栈,指针指向左子树,当前节点为空则出栈并访问,之后当前指针指向出栈节点的右子树。
24.算法:输出给定节点到目标节点的路径
a.可以转化为首先求出根节点到给定节点的路径及根节点到目标节点的路径,然后比较这两个路径,如果完全没有重合,则相加,如果部分重合,则找到最近的重合点。如果给定节点就在到目标节点的路径上则选取那段即可。
https://blog.csdn.net/shixiaoguo90/article/details/23759887
计算根节点到任意节点的方法:递归,用ArrayList记录路径走过的路径,注意如果未查找到要进行remove操作。这个地方为了避免多余的查找,可以设置一个查找标志。
b.可以首先查找目标节点是否在给定节点的子树中,第二,是否在根的另一子树中。第三种情况就是在根的同一边子树中,转化为求公共父节点的算法。
https://blog.csdn.net/zjkC050818/article/details/72621487
本质上还是变成了根据根到节点的路径来进行求解的算法。
25.算法: 寻找两个字符串中只有首尾字符相同的所有子串,例如 ABCDE 和 ADCAE中包含(ABC--ADC)以及(CDE--CAE)
a.可以用map结构把开头结尾,子串信息记录下来,然后进行比对。例如 AC:ABC,...,然后匹配过后输出。复杂度是o(n2);
String str = "abccc";
char a = str.charAt(2);
char b = str.charAt(0);
String sub = a + b + "";
26.数据结构:建堆的时间复杂度
https://blog.csdn.net/liliuteng/article/details/8496050
建新堆的时间复杂度应该是o(n),是一个反复筛选的过程。从第一个非根节点一直调整到根。
排序算法的复杂度应该是T(n) <= O(n) + (n - 1)*O(log2(n)) 就是nlogn
27.数据结构:快排的时间复杂度在最好情况和最坏情况
https://www.cnblogs.com/dapeng-bupt/p/7922719.html
最好情况:每次恰能均分,logn
最坏情况:每次只能把序列划分为1个元素与其他元素两部分,退化为冒泡,n2次
平均:nlogn
28.操作系统:共享内存
https://www.cnblogs.com/xcywt/p/5128430.html
https://blog.csdn.net/ljianhui/article/details/10253345
shmget函数,进程通过key申请创建共享内存,然后获得地址,通过对共享内存的读写来进行通信。
但有一点特别要注意:共享内存并未提供同步机制。也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
29.一个有向图用邻接矩阵表示,并且是有权图,现在问怎么判断图中有没有环。
a.邻接矩阵表示:从i到j有边,则d[i][j]的值为该权重,否则为-无穷;核心函数(某个点,已走过的路径,邻接矩阵),如果在已走过的路径中出现了这个点,则说明存在环。否则,根据邻接矩阵继续查找相邻点。外层函数应该覆盖所有点。
拓扑排序
30.僵尸进程和孤儿进程是什么
https://www.cnblogs.com/Anker/p/3271773.html
孤儿进程:父进程退出,子进程还在继续执行的情况。不会有什么危害
僵尸进程:子进程退出,但是父进程没有调用wait和waitpid来获得子进程的状态信息,那么子进程的进程描述符会残留在系统中成为僵尸进程。而进程号资源是有限的,大量的残留会导致不能产生新的进程。解决的办法应该是关闭掉产生大量僵尸进程的父进程。
31.写一个LRU的缓存,需要完成超时淘汰和LRU淘汰。
LRU思想:最近最少使用,如果数据最近被访问过,那么将来被访问的几率也更高。
空间未满,直接写入;空间满,若该点出现过,则置顶;若没出现过,应该删除最底端的点,置顶。
数据结构采用带头结点的链表结构即可;
package zyh.data.main;
public class LRU {
private static int size = 0;
private static int capacity = 3;
private static Node head;
static class Node {
private int data;
private Node next;
public Node(int data) {
this.data = data;
next = null;
}
}
public static void insert(int data) {
Node node = new Node(data);
if (size == 0) {
head = node;
size++;
return;
}
node.next = head;
head = node;
size++;
if (size > capacity) {
remove(size);
}
}
public static void remove(int cursor) {
Node node1 = head;
Node node2 = node1.next;
for (int i = 2; i < cursor; i++) {
node1 = node2;
node2 = node2.next;
}
node1.next = node2.next;
size--;
}
public static int findByData(int data) {
Node node = head;
int i = 0;
while (node != null) {
i++;
if (node.data == data) return i;
node = node.next;
}
return -1;
}
public static void lru(int data) {
int cursor = findByData(data);
if (cursor == -1) {
insert(data);
}else{
remove(cursor);
insert(data);
}
}
public static void display() {
if (size == 0) {
return;
} else {
Node point = head;
for (int i = 0; i < size; i++) {
System.out.print(point.data);
point = point.next;
if (i == size - 1) break;
else System.out.print("->");
}
System.out.println("");
}
}
public static void main(String[] args) {
lru(1);
display();
lru(2);
display();
lru(3);
display();
lru(4);
display();
lru(2);
display();
lru(4);
display();
}
}
32.算法:归并排序
最差的时间复杂度:o(nlogn),不随数据序列中关键字分布的变化而变化
最好的时间复杂度:o(nlogn)
稳定,需要耗费额外的o(n)空间
33.算法:希尔排序
利用插入排序在序列长度小或序列已经基本有序的情况下效率较高的特性,采用缩小增量的办法,即按步长进行分组然后使用插排使得组内有序。然后不断缩小步长,最终达到步长为1。
不稳定。最差复杂度在o(1.5)左右。
34.Java:hashmap在多线程环境下会有什么问题
https://www.cnblogs.com/binyue/p/3726403.html
a.put操作和resize操作后(逆序)可能会导致get死循环,1.8版本这个问题得到了修复。
不难发现循环的产生是因为新链表的顺序跟旧的链表是完全相反的,所以只要保证建新链时还是按照原来的顺序的话就不会产生循环。
JDK8是用 head 和 tail 来保证链表的顺序和之前一样,这样就不会产生循环引用。
https://blog.csdn.net/Hzt_fighting_up/article/details/78737468
b.多线程put可能会导致元素丢失
35.数据结构:红黑树的特点
https://www.cnblogs.com/gofighting/p/5437998.html
红黑树能够以O(log2(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。这一点是AVL所不具备的。
而且实际应用中,很多语言都实现了红黑树的数据结构。比如 TreeMap, TreeSet(Java )、 STL(C++)等。
36.Python:在使用python时,2.7版本和3.6版本相比,针对打印输出函数print有什么不同?
https://blog.csdn.net/freestyle4568world/article/details/48369057
a.变成了函数形式而不是语句形式 print(a)
37.Linux:在linux系统中,如何查看某个端口是否被占用?
https://www.cnblogs.com/hindy/p/7249234.html
netstat -anp| grep 80 :如果出现listen说明被占用
如果查看所有端口使用情况可以 netstat -nultp
38.Linux:在linux系统中,如何查看cpu的占用情况?
https://blog.csdn.net/albenxie/article/details/72885951
top指令应该是最熟悉的了
39.Mysql:mysql中如何限制表X的大小只有两行?
a.如果是限制返回的结果用limit就可以
b.如果是指该表中至多插入两条数据,可以设定一个insert触发器,在insert前检查行数如果大于2,则抛出异常。也可以在客户端程序中进行处理。
https://bbs.csdn.net/topics/391886820
40.ConcurrentHashMap的并发?
http://ifeve.com/concurrenthashmap/
a.为了高效ConcurrentHashMap不会对整个容器进行扩容,而只对某个segment进行扩容。这个时候需要对这个segment加锁(put操作)1.8之前
https://blog.csdn.net/jianghuxiaojin/article/details/52006118
b.支持并发扩容
-
第一部分是构建一个nextTable,它的容量是原来的两倍,这个操作是单线程完成的。
- 第二个部分就是将原来table中的元素复制到nextTable中,这里允许多线程进行操作。
41.JVM:垃圾回收器都有哪些
https://www.cnblogs.com/firstdream/p/5763646.html
https://blog.csdn.net/u013782203/article/details/52222722
串行垃圾回收器(Serial Garbage Collector),垃圾回收工作是单线程的,运行时冻结客户线程
并行垃圾回收器(Parallel Garbage Collector),垃圾回收工作是多线程的,运行时冻结客户线程
并发标记扫描垃圾回收器(CMS Garbage Collector),初始标记,并发标记,重新标记,清除
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。其他动作都是并发的。
G1垃圾回收器(G1 Garbage Collector),标记整理算法,可以精确地控制停顿时间,有实时收集器的特征
42.JVM:说一下G1?
http://blog.jobbole.com/109170/
取消了新生代老年的物理空间划分。划分为E,S,O,H(大型对象)四种块。
初始标记,并发标记,最终标记,清除垃圾
43.Java:读文件用到哪些api?
https://blog.csdn.net/fengxingzhe001/article/details/67640083
a.fileInputStream
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int cnt = 0; while((cnt=fis.read(buffer)) != -1) { // String bluck = new String(buffer, 0, cnt); }
fis.close();
b.bufferedInputStream
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buffer = new byte[1024];
int cnt = 0; while((cnt=bis.read(buffer)) != -1) { // String bluck = new String(buffer, 0, cnt); }
bis.close();
c.BufferdReader
File file = new File("text03.txt");
BufferedReader bReader = new BufferedReader(new FileReader(file));
int cnt = 0; char[] buffer = new char[1024];
while((cnt = bReader.read(buffer)) != -1) { // String lintxt = new String(buffer, 0, cnt); }
bReader.close();
转换:inputStreamReader outputStreamWriter
44.如何打破双亲委派?
https://blog.csdn.net/wxy941011/article/details/81124699
a.继承classLoader,重写classloader中的loadclass方法,重写findclass方法
45.算法:寻找一个数组中最长上升子序列
https://blog.csdn.net/weixin_37373020/article/details/80975723
a.递归的思想可解,思想就是取不取当前数;
public static int hasPathSum(int[] nums ,int n) {
if (nums == null || nums.length == 0 || n<=0){
return 0;
}
int[] max = new int[1];
ArrayList<Integer> arrayList = new ArrayList<>();
for (int x:nums){
arrayList.add(x);
}
core(arrayList,max,0);
return max[0];
}
public static void core(ArrayList<Integer> nums,int[] max,int already){
if (already>max[0]){
max[0] = already;
}
if (nums.size() == 0){
return;
}
int thistime = nums.get(0);
ArrayList<Integer> temp = new ArrayList<>();
for (int x = 1;x<=nums.size()-1;x++){
if (nums.get(x)>thistime){
temp.add(nums.get(x));
}
}
core(temp,max,already+thistime);
nums.remove(0);
core(nums,max,already);
}
b.动态规划可解 http://www.cnblogs.com/wxjor/p/5524447.html
遇到小的就替换,遇到大的就加入o(nlogn)
d(i) = d(j)+1;d(i)表示以i为结尾的序列。其肯定来源于之前比他小的。
46.Java:arraylist和linkedlist在尾部加入一个节点带来的消耗
https://blog.csdn.net/qq_34144916/article/details/81154528
arraylist底层是数组,随着元素增加,会带来扩容问题。当不需要扩容时,arraylist效率还要高于linkedlist因为linkedlist会new一个node节点的开销;
//arraylist
public boolean add(E paramE)
{
// 检查当前集合中是否有空间可以装下新的元素
// 如果空间不够了,会自动扩容
// 注:ArrayList会分配连续的内存片段来储存集合的元素,因为内部其实是数组
ensureCapacityInternal(this.size + 1);
this.elementData[(this.size++)] = paramE;
return true;
}
//linkedlist
public boolean add(E e) {
// 实际上是调用的linkLast(E e)方法,即:在最后面追加一个元素
linkLast(e);
return true;
}
public void linkLast(E e) {
// 取到追加前的最后一个元素
final Node<E> l = last;
// 创建一个新的节点,并且将此节点之前的一个元素,新的元素传入
// 由于是最后一个元素,所以其后面的元素是空的,因此后一个元素为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;// 更新最后一个元素的引用
if (l == null)
first = newNode;
else
l.next = newNode;
size++;// 更新list的总数
modCount++;
}
47.JVM:cms和g1垃圾回收器,垃圾回收的过程。
CMS:初始标记 (单,停)- 并发标记 - 重新标记(多,停) - 并发清除
G1:初始标记 (单,停)- 并发标记 - 最终标记(多,停) - 筛选回收(多,停)
48.大数相加递归实现
http://micahchen.iteye.com/blog/575073
a.将字符串表示的两个数首先需要反转以保证能位对齐;然后按位计算,采用递归,用一个String记录结果。最终这个String也要进行反转;
49.JVM:类加载器的类型
a.启动类加载器:Bootstrap classloader
加载lib目录下能被虚拟机识别的类库,无法直接被引用;
b.扩展类加载器:extension classloader
lib\ext目录中的类库,可以直接使用
c.应用程序加载器:application classloader
classPath上的类库,可以直接使用
50.数据库:悲观锁和乐观锁
https://www.cnblogs.com/zhiqian-ali/p/6200874.html
悲观锁:采用悲观的策略,认为总有线程会修改或读取自己拿走的数据,所以在每次拿数据时都会对数据进行加锁。数据库中很多操作例如行锁,读锁,写锁都是采用了这种策略。
乐观锁:采用乐观的策略,每次拿走数据时都会认为其他人暂不会修改或读取,不采取上锁的形式,但在更新写回时会判断一下是否这个值已经被更新了,如果是就会放弃这次操作再retry。例如JAVA中的CAS就是乐观锁的一种实现。更适用于读操作比例较大的场景。节约了加锁的开销。在数据库中可以使用版本号或者时间戳。
51.操作系统:消息队列
https://blog.csdn.net/u014634338/article/details/45115661
消息队列提供了进程之间发送数据的功能;可以避免通道和命名通道带来的同步和阻塞问题,独立于发送与接收线程。在linux系统中可以使用msgget来调用。
https://www.cnblogs.com/caolicangzhu/p/6985278.html
(1)消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
(2)消息队列允许一个或多个进程向它写入与读取消息.
(3)管道和命名管道都是通信数据都是先进先出的原则。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
https://blog.csdn.net/u013630349/article/details/46823335
消息队列也称为报文队列,消息队列是随内核持续的,只有在内核重起或显示删除一个消息队列时,该消息队列才会真正删除。系统中记录消息队列的数据结构struct ipc_ids msg_ids位于内核中,系统中所有消息队列都可以在结构msg_ids中找到访问入口。
52.设计模式:回调模式
https://blog.csdn.net/c275046758/article/details/51509218
允许底层代码调用在高层定义的子程序,用于实现异步
传统的同步编程是一种请求响应模型,调用一个方法,等待其响应返回.
异步编程就是要重新考虑是否需要响应的问题,也就是缩小需要响应的地方。因为越快获得响应,就是越同步化,顺序化,事务化,性能差化。
异步编程通常是通过fire and forget方式实现,发射事件后即忘记,做别的事情了,无需立即等待刚才发射的响应结果了。(发射事件的地方称为生产者,而将在另外一个地方响应事件的处理者称为消费者).异步编程是一种事件驱动编程
53.Java:如何保证ABC三个线程按顺序执行
https://blog.csdn.net/Evankaka/article/details/80800081
a.join方法:在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B
b.countDownLatch:可以拦截一个或多个线程使其在某个条件成熟后再执行。
c.阻塞队列
public static void main(String[] args) {
//blockingQueue保证顺序
BlockingQueue<Thread> blockingQueue = new LinkedBlockingQueue<Thread>();
Thread t1 = new Thread(new Work());
Thread t2 = new Thread(new Work());
Thread t3 = new Thread(new Work());
blockingQueue.add(t1);
blockingQueue.add(t2);
blockingQueue.add(t3);
for (int i=0;i<3;i++) {
Thread t = null;
try {
t = blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
t.start();
//检测线程是否还活着
while (t.isAlive());
}
}
d.单个线程池
两个线程交替执行
https://www.toutiao.com/i6667436994261418509/
54.数据库:幻读的避免是如何实现的
https://www.jianshu.com/p/2953c64761aa
MYSQL的innodb中是使用mvcc来进行控制。
mvcc全称是multi version concurrent control(多版本并发控制)。mysql把每个操作都定义成一个事务,每开启一个事务,系统的事务版本号自动递增。每行记录都有两个隐藏列:创建版本号和删除版本号
select:事务每次只能读到创建版本号小于等于此次系统版本号的记录,同时行的删除版本号不存在或者大于当前事务的版本号。
update:插入一条新记录,并把当前系统版本号作为行记录的版本号,同时保存当前系统版本号到原有的行作为删除版本号。
delete:把当前系统版本号作为行记录的删除版本号
insert:把当前系统版本号作为行记录的版本号
55.数据库:什么是B+树?为什么选择B+树作为索引结构,不选择用二叉树?
https://blog.csdn.net/weixin_30531261/article/details/79312676
那为什么平衡二叉树不适合作为索引呢?
索引是存在于索引文件中,是存在于磁盘中的。因为索引通常是很大的,因此无法一次将全部索引加载到内存当中,因此每次只能从磁盘中读取一个磁盘页的数据到内存中。而这个磁盘的读取的速度较内存中的读取速度而言是差了好几个级别。
注意,我们说的平衡二叉树结构,指的是逻辑结构上的平衡二叉树,其物理实现是数组。然后由于在逻辑结构上相近的节点在物理结构上可能会差很远。因此,每次读取的磁盘页的数据中有许多是用不上的。因此,查找过程中要进行许多次的磁盘读取操作。
而适合作为索引的结构应该是尽可能少的执行磁盘IO操作,因为执行磁盘IO操作非常的耗时。因此,平衡二叉树并不适合作为索引结构。
局部性原理与磁盘预读:
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
B树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点。也正因每个节点存储着非常多个关键字,树的深度就会非常的小。进而要执行的磁盘读取操作次数就会非常少,更多的是在内存中对读取进来的数据进行查找。
B树的查询,主要发生在内存中,而平衡二叉树的查询,则是发生在磁盘读取中。因此,虽然B树查询查询的次数不比平衡二叉树的次数少,但是相比起磁盘IO速度,内存中比较的耗时就可以忽略不计了。因此,B树更适合作为索引。
B树:有序数组+平衡多叉树;
B+树:有序数组链表+平衡多叉树;B+树的关键字全部存放在叶子节点中,非叶子节点用来做索引,而叶子节点中有一个指针指向一下个叶子节点。做这个优化的目的是为了提高区间访问的性能。而正是这个特性决定了B+树更适合用来存储外部数据。
B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。
56.Java:Java中有哪些锁
https://www.cnblogs.com/lxmyhappy/p/7380073.html
公平锁/非公平锁:是否按申请顺序获取锁,reentrantlock两种都可以,sychronized是非公平的
可重入锁:同一个进程在外层获得锁后进入内层函数时会自动获得锁,两种均是可重入的
独享锁/共享锁
互斥锁/读写锁
乐观锁/悲观锁
分段锁:类似concurrentHashmap
偏向锁/轻量级锁/重量级锁:sychronized优化策略
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
57.Java:synchronized和lock的区别?
https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | api层面 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 必须手动释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 等待可中断 |
绑定条件 | sychronized包含一个隐含条件,多于1个的条件关联的时候就需要多加锁 | 可以同时绑定多个condition对象 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
58.算法:日志文件中有IP和时间。找出10min内出现次数超过800的ip
a.如果时间是乱序的,可以先用hash针对ip分文件,再在超过800的文件中核实10min内是否出现超过800。
b.如果时间是顺序的,可以直接遍历,遇到ip则判断,如果已经在map中,判断时间,如果没超过10分钟,则存下时间并判断个数。如果超过10分钟,则删除value中与当前时间超过10分钟的。如果没出现在map中,则加入map。
59.算法:哪些时候必须采用稳定的排序及稳定的排序有哪些
a.例如在数据库中,我已经按id对于某个表进行了排序,然后想再按某列的值进行一次排序,这个时候如果采用了不稳定的排序,就会导致丢失第一次排序的顺序。
插入排序、冒泡排序、归并排序、分配排序(桶式、基数)都是稳定的排序算法。
直接选择排序、堆排序、shell排序、快速排序都是不稳定的排序算法。
60.算法:能被2,3,5整除的数字的个数
a.维护一个数组,记录下已经出现过的,然后去计算下一个。下一个的规则应该是min{m2,m3,m5},其中m2是来源于已有序列*2超过最后一个元素的最小值,同理m3,m5
61.Java:hashmap的get和put
https://blog.csdn.net/linsongbin1/article/details/54667453
https://www.cnblogs.com/baizhanshi/p/5783770.html
调用put方法时,如果发现目前的bucket占用程度已经超过了loadFactor,就会发生resize。
首先对key做null检查。如果key是null,会被存储到table[0]。
key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引。有时候hash函数可能写的很不好,所以JDK的设计者添加了另一个叫做hash()的方法,它接收刚才计算的hash值作为参数。
indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。
在我们的例子中已经看到,如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表。
如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。
如果索引上有元素,然后会进行迭代,一直到Entry->next是null。当前的Entry对象变成链表的下一个节点。(也有看到说是插入头部)
当你传递一个key从hashmap总获取value的时候:
对key进行null检查。如果key是null,table[0]这个位置的元素将被返回。
key的hashcode()方法被调用,然后计算hash值。
indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。
在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回Entry对象的value,否则,返回null。
62.算法:n个人编号从1->n, 对应n个座位编号从1->n,问每个人都不做在自己的位置上有多少中可能性?
错排问题:
设长度为n的序列的全错位排列一共有f(n)种,假设我们已经解决了f(1)到f(n-1),那么当序列新增了一个元素an,显然全错位排列中该元素不能放在第n个位置上,假设该元素在从1到n-1的第i个位置,那么在新序列中第n个位置上的元素可能有两种情况:
第n个位置上的元素为ai
因为an和ai都不在原位置上,因此只需剩余的元素都是全错位排列,新序列就构成了全错位排列。那么除去ai和an还剩下n-2个元素,则这n-2个元素一共有f(n-2)种全错位排列,因为i的选择共有n-1种,因此该情况下一共有(n-1)*f(n-2)种全错位排列。
第n个位置上的元素不为ai
该种情况相当于,前n-1个元素做好了全错位排列,an与其中任意元素交换位置,新生成的序列也是一个全错位排列。这种情况下i的选择共有n-1种,n-1的元素的全错位排列共有f(n-1)种,因此该情况下一共有(n-1)*f(n-1)种全错位排列。
综合以上两种情况,f(n)=(n-1)f(n-2)+(n-1)*f(n-1)=(n-1)[f(n-2)+f(n-1)]
显然这个公式适用于n>2的情况,而f(1)=0,f(2)=1是之前已经列举得出的。
将n=3代入,得到f(3)=2*(0+1)=2,将n=4代入,得到f(4)=3*(1+2)=9,与列举所得到的结果相同。
63.数据结构:二叉树后续非递归遍历
https://blog.csdn.net/zhuqiuhui/article/details/51319165
a.思想,当一个节点无右子树或右子树已被访问才能访问该节点,否则应该访问右子树
public void postOrder(Node node){
if(node==null)
return;
Stack<Node> s = new Stack<Node>();
Node curNode; //当前访问的结点
Node lastVisitNode; //上次访问的结点
curNode = node;
lastVisitNode = null;
//把currentNode移到左子树的最下边
while(curNode!=null){
s.push(curNode);
curNode = curNode.getLchild();
}
while(!s.empty()){
curNode = s.pop(); //弹出栈顶元素
//一个根节点被访问的前提是:无右子树或右子树已被访问过
if(curNode.getRchild()!=null&&curNode.getRchild()!=lastVisitNode){
//根节点再次入栈
s.push(curNode);
//进入右子树,且可肯定右子树一定不为空
curNode = curNode.getRchild();
while(curNode!=null){
//再走到右子树的最左边
s.push(curNode);
curNode = curNode.getLchild();
}
}else{
//访问
System.out.println(curNode.getData());
//修改最近被访问的节点
lastVisitNode = curNode;
}
} //while
}
b.使用两个栈来排列
64.数据库:turncate和delete的区别
https://blog.csdn.net/gideal_wang/article/details/4789454
1 事务管理:delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发.delele可以回滚。 truncate是ddl, 操作立即生效,原数据不放到rollback segment中,不能回滚. 操作不触发trigger.
2 速度:turncate table比delete速度快,且使用的系统和事务日志资源少。
3 对于由FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。
65.Linux:硬链接和软链接的区别
https://www.cnblogs.com/ylan2009/p/4287929.html
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html
硬链接(hard link):A是B的硬链接(A和B都是文件名),则A的目录项中的inode节点号与B的目录项中的inode节点号相同,即一个inode节点对应两个不同的文件名,两个文件名指向同一个文件,A和B对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode节点上的链接数增加一,每删除一个对应的文件名,inode节点上的链接数减一,直到为0,inode节点和对应的数据块被回收。注:文件和文件名是不同的东西,rm A删除的只是A这个文件名,而A对应的数据块(文件)只有在inode节点链接数减少为0的时候才会被系统回收。
软链接(soft link):A是B的软链接(A和B都是文件名),A的目录项中的inode节点号与B的目录项中的inode节点号不相同,A和B指向的是两个不同的inode,继而指向两块不同的数据块。但是A的数据块中存放的只是B的路径名(可以根据这个找到B的目录项)。A和B之间是“主从”关系,如果B被删除了,A仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。
硬链接:
a.不能对目录创建硬链接,原因有几种,最重要的是:文件系统不能存在链接环(目录创建时的".."除外,这个系统可以识别出来),存在环的后果会导致例如文件遍历等操作的混乱(du,pwd等命令的运作原理就是基于文件硬链接,顺便一提,ls -l结果的第二列也是文件的硬链接数,即inode节点的链接数)
b:不能对不同的文件系统创建硬链接,即两个文件名要在相同的文件系统下。
c:不能对不存在的文件创建硬链接,由原理即可知原因。
软链接:a.可以对目录创建软链接,遍历操作会忽略目录的软链接。
b:可以跨文件系统
c:可以对不存在的文件创建软链接,因为放的只是一个字符串,至于这个字符串是不是对于一个实际的文件,就是另外一回事了
66.操作系统:并发和并行
https://blog.csdn.net/weixin_30363263/article/details/80732156
英文单词
并发:Concurrency 并行:Parallelism并发
一个并发程序是具备处理多个任务的能力。并发并不需要有多个CPU,单个CPU通过时间片的方式,不同时间片处理不同任务,可以让程序“看起来”是都在执行的。并行
并行表示在同一个时间点,有多个任务都在进行当中。并行是需要多个CPU才可以完成的。如果说每一个CPU都在运行一个程序,那么4个CPU就可以让4个程序“并行”运算。
67.Java:static的用法
https://www.cnblogs.com/dolphin0520/p/3799052.html
a.修饰方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。
b.修饰变量
static是不允许用来修饰局部变量
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
c.static代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。
68.Java为什么需要反射
http://www.cnblogs.com/buoge/p/9285142.html
提供给java动态编译的能力:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也即是说JVM会执行该类的静态代码段。并不会初始化。可以修改类的属性。
https://www.cnblogs.com/zhengchunhao/p/4886734.html
动态链接库:也就是说编译的时候不会真的把你引用到的库给编到你的执行程序里,而是在执行时候才会去加载相关的库,所有用到此库的程序可以共享一份代码,这样带来的好处是可执行程序所占的空间变小了,同时,如果库需要升级,你并不需要重新编译你的程序,只要把相关的库升级即可。
又有隐式加载和显式加载两种方式,隐式会在主程序载入内存时进行加载;显式会在运行过程中需要dll才加载。
静态链接库:你把写好的代码编译的时候,就把你引用的库一起给编进去了,从此后你编出来的执行程序跟外面都不再有任何关系,即使这个库更新了,你也搭不上边儿,其次,如果系统中许多类似的程序都需要用到这个库,那么各自在编译的时候都需要把这个库给编进去,浪费存储空间(加载到内存里应该也是浪费内存空间的)
69.操作系统:用户态和内核态
https://www.jianshu.com/p/85e931636f27
https://blog.csdn.net/handsomehong/article/details/73862907
用户态:上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源
内核态:为了使上层应用能够访问到这些资源,内核为上层应用提供访问的接口。
用户态转化成内核态的三种情况
a.系统调用:。系统中的各种共享资源都由操作系统统一掌管,因此在用户程序中,凡是与资源有关的操作(如存储分配、进行I/0传输以及管理文件等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。
b.异常
c.中断
70.段表和页表:都是用于逻辑地址到物理地址的一个转化
https://www.cnblogs.com/worldisimple/articles/2447577.html
https://www.cnblogs.com/zjzsky/p/3528526.html
段表:段开始地址及段长度
页表:页号对应的物理块号
71.路由器和交换机的区别
https://blog.csdn.net/liebao_han/article/details/52859814
路由器:工作在网络层,转发分组,使用的是ip地址,实现了一些算法,维护路由表,连接到路由器上的所有端口不在属于同一个广播域,所以不会产生类似的数据拥堵问题。
交换机:工作在数据链路层,转发数据帧,使用的mac地址(设备唯一),由交换机连接的所有端口仍然属于同一个广播域,所以极有可能会产生数据拥堵;
72.字符串的全排列
输入:char[] chars ,int start,int end
输出条件:start == end,当需要对相同字符进行处理的时候在逻辑上加上判断,本质是一个递归。先排序后面的字符,再加上本位。
public static void test(char[] chars,int from ,int to){
if (to<=0){
return;
}else if (from == to){
System.out.println(chars);
}else {
for (int x = from;x<=to;x++){
if (x == from) {
swap(chars, x, from);
test(chars, from + 1, to);
swap(chars, x, from);
}else {
if (chars[x]!=chars[from]){
swap(chars, x, from);
test(chars, from + 1, to);
swap(chars, x, from);
}
}
}
}
}
public static void swap(char[] chars,int x,int y){
char temp = chars[x];
chars[x] = chars[y];
chars[y] = temp;
}
73.Mysql中的hash索引
https://www.cnblogs.com/heiming/p/5865101.html
底层的数据结构是bucket结合链表的结构,主流的innoDB提到是支持的
但是
innodb引擎有一个特殊的功能叫做自适应哈希索引,当innodb注意到某些索引值被使用的非常频繁时,它会在内存中基于btree索引之上再创建一个哈希索引,这样就让btree索引也具有哈希索引的一些优点,比如:快速的哈希查找,这是一个全自动的,内部的行为,用户无法控制或者配置。
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
从示意图中也能看到,如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
74.Java:Java如何做到跨平台性
https://www.cnblogs.com/028fly/archive/2010/04/04/1704248.html
因为Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一种“中间码”——字节码。然后不同的硬件平台上安装有不同的Java虚拟机(JVM),由JVM来把字节码再“翻译”成所对应的硬件平台能够执行的代码。因此对于Java编程者来说,不需要考虑硬件平台是什么。java是先把java文件编译成二进制字节码的class文件,jvm就解释执行class文件
75.Java:arraylist和linkedlist的遍历效率问题
https://blog.csdn.net/zhzzhz123456/article/details/53323093
看到ArrayList的get方法只是从数组里面拿一个位置上的元素罢了。我们有结论,ArrayList的get方法的时间复杂度是O(1)。计算机做的只是算出起始地址-->去该地址中取数据而已,因此我们看到使用普通for循环遍历ArrayList的速度很快,也很稳定。
LinkedList在get任何一个位置的数据的时候,都会把前面的数据走一遍。使用迭代器或者foreach循环(foreach循环的原理就是迭代器)去遍历LinkedList即可,这种方式是直接按照地址去找数据的,将会大大提升遍历LinkedList的效率。注意在linkedlist是一个双向链表。
76.Java:内部类和静态内部类
https://www.cnblogs.com/aademeng/articles/6192954.html
静态内部类要创建嵌套类的对象,并不需要其外围类的对象;不能从嵌套类的对象中访问非静态的外围类对象(不能够从静态内部类的对象中访问外部类的非静态成员);
普通的内部类不能有static数据和static字段, 也不能包含嵌套类。但是在嵌套类里可以包含所有这些东西。
实例化一个非静态的内部类需要借助外部类对象;
77.Java:threadlocal
https://blog.csdn.net/javadhh/article/details/60604739
https://www.jianshu.com/p/98b68c97df9b
https://blog.csdn.net/xlgen157387/article/details/78297568
线程局部变量,是一种多线程间并发访问变量的解决方案。与其synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的手段,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
thread中有一个threadlocalmap的结构,存下它使用的threadlocal对象及值。
threadlocal对象统一维护thread及thread对应的值。当调用get方法的时候就会从thread中读取变量值。当调用set方法事就会把值放入thread中
78.算法:蓄水池抽样
https://blog.csdn.net/yinjunshishui/article/details/14647805
类比下即可得到答案,即先把前k个数放入蓄水池,对第k+1,我们以k/(k+1)概率决定是否要把它换入蓄水池,换入时随机的选取一个作为替换项,这样一直做下去,对于任意的样本空间n,对每个数的选取概率都为k/n。也就是说对每个数选取概率相等。
79.Java:继承和组合
https://blog.csdn.net/zh15732621679/article/details/79443757
组合是一种较弱的关系,是has-a的关系;
继承是一种is a 的关系;
80.JVM:空间分配担保机制
在发生minor gc前,虚拟机会先检查老年代最大可用连续空间是否大于新生代所有对象总空间。如果是,则肯定是安全的,否则会查看是否允许担保失败。如果允许,继续检查是否大于历次晋升到老年代的平均大小,如果大于,会尝试进行。否则会改为进行一次full gc。
81.把1-n打印成如下图形式,暂时写了个n为平方数的版本(看到别人的面试题,所以题目要求不是很清楚)
1 2 3
8 9 4
7 6 5
public static void test2(int n){
if (n<=0){
return;
}else if (n == 1){
System.out.println(1);
}
int[][] nums = new int[n][n];
int level = 1;
int k = n;
int level_num = 4*k-4;
if (n%2 == 0){
for (int x = 0;x<n/2;x++){
give(nums,x,n-x-1,level);
level = level+level_num;
k = k-2;
level_num = 4*k-4;
}
}else {
for (int x = 0;x<=n/2;x++){
give(nums,x,n-x-1,level);
level = level+level_num;
k = k-2;
level_num = 4*k-4;
}
}
for (int x = 0;x<=n-1;x++){
for (int y = 0;y<=n-1;y++){
System.out.print(nums[x][y]);
System.out.print(" ");
}
System.out.println();
}
}
public static void give(int[][] nums,int start,int end,int numstart){
if (start == end){
nums[start][start] = numstart;
return;
}
for (int x = start;x<=end;x++){
nums[start][x] = numstart;
numstart++;
}
for (int x = start+1;x<=end;x++){
nums[x][end] = numstart;
numstart++;
}
for (int x = end-1;x>=start;x--){
nums[end][x] = numstart;
numstart++;
}
for (int x = end-1;x>=start+1;x--){
nums[x][start] = numstart;
numstart++;
}
}
82.100的阶乘后有多少个0
https://blog.csdn.net/qq_36238595/article/details/53339238
https://blog.csdn.net/hongkangwl/article/details/39666991
a.遇到5就会产生1个0, 10本身有1个0,照这样到100就是有5,10,15,20,25,30,35...95,100,共产生21个0,但25=5*5 ,50=5*5*2 , 75=5*5*3,都含有2个5所以还要加3次就是24个0
public static void test2(int n){
if (n<=4){
System.out.println(0);
}
int sum = 0;
for (int x = 5;x<=n;x++){
int temp = x;
while (temp%5 == 0 && temp>0){
sum++;
temp = temp/5;
}
}
System.out.println(sum);
}
b.分类讨论,含有1个5的应该有100/5 = 20个,含有两个5 = 100/25 = 4,含有3个5 = 100/125 = 0,所以一共有24个。
83.聚集索引和非聚集索引
https://www.cnblogs.com/s-b-b/p/8334593.html
SQL的主流索引结构有B+树以及Hash结构,聚集索引以及非聚集索引用的是B+树索引。
MySQL 索引类型有:唯一索引,主键(聚集)索引,非聚集索引,全文索引。
聚集索引:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。MySQL里主键就是聚集索引,索引的叶子节点就是对应的数据节点
非聚集索引:索引的逻辑顺序与磁盘上行的物理存储顺序不同 , 再细分成普通索引,唯一索引,全文索引。非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据。解决二次查询的办法就是对于查询多列的情况建立复合索引。
84.count(*)和count(1)的区别
https://blog.csdn.net/ifumi/article/details/77920767
https://www.cnblogs.com/sueris/p/6650301.html
从执行计划上看是相同的,会快于count(col),因为count(col)会去检查每行是否为null;执行效果是相等的。
https://blog.csdn.net/qq_17099727/article/details/78521127
85.Object类的常用方法
https://www.cnblogs.com/wxywxy/p/6740277.html
https://www.cnblogs.com/zyj-bozhou/p/6733309.html equals()和==
toString() : 字符串化方法
equals():用于判断对象实例是否相同,默认比较地址
hashcode():返回的是物理地址,主要是在map中使用,且要配合equals方法。
clone():clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间
getClass():配合反射使用,返回Class对象
wait() notify() notifyall():线程间协作 wait():调用此方法所在的当前线程等待,直到在其他线程上调用此方法的主调(某一对象)的notify()/notifyAll()方法。
finalize()方法和对象回收有关
86.生产者消费者模型
https://blog.csdn.net/luohuacanyue/article/details/16359777
大量的实现ArrayBlockingQueue已经做掉了,包括判空,线程挂起等操作都封装在ArrayBlockingQueue中。这样具体的应用者的代码会少很多。生产者只需要关心生产,消费者只需要关心消费
public class Producer implements Runnable{
//容器
private final ArrayBlockingQueue<Bread> queue;
public Producer(ArrayBlockingQueue<Bread> queue){
this.queue = queue;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while(true){
produce();
}
}
public void produce(){
/**
* put()方法是如果容器满了的话就会把当前线程挂起
* offer()方法是容器如果满的话就会返回false,也正是我在前一篇中实现的那种策略。
*/
try {
Bread bread = new Bread();
queue.put(bread);
System.out.println("Producer:"+bread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Consumer implements Runnable{
//容器
private final ArrayBlockingQueue<Bread> queue;
public Consumer(ArrayBlockingQueue<Bread> queue){
this.queue = queue;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while(true){
consume();
}
}
public void consume(){
/**
* take()方法和put()方法是对应的,从中拿一个数据,如果拿不到线程挂起
* poll()方法和offer()方法是对应的,从中拿一个数据,如果没有直接返回null
*/
try {
Bread bread = queue.take();
System.out.println("consumer:"+bread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int capacity = 10;
ArrayBlockingQueue<Bread> queue = new ArrayBlockingQueue<Bread>(capacity);
new Thread(new Producer(queue)).start();
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
new Thread(new Consumer(queue)).start();
new Thread(new Consumer(queue)).start();
}
87.Java中同一个类中不同的synchronized方法是否可以并发执行
https://blog.csdn.net/niusi1288/article/details/76143276
不能同时执行,因为synchronized方法锁定的是方法所属的对象本身。
88.notify和notifyall的区别
https://blog.csdn.net/u014658905/article/details/81035870
notify和notifyAll之间的主要区别在于notify方法只通知一个Thread,notifyAll方法将通知在该监视器上等待的所有线程或锁定。
当你调用notify时,只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器,ThreadScheduler将从等待该监视器上的线程的池中选择一个随机线程。保证只有一个线程会被通知。。虽然如果你调用notifyAll方法,那么等待该锁的所有线程都会被唤醒,但是在执行剩余的代码之前,所有被唤醒的线程都将争夺锁定。
89.arraylist的扩容,缩容
https://blog.csdn.net/zymx14/article/details/78324464
无参数的构造方法是指向了一个空数组,最开始一次add方法会Math.max(DEFAULT_CAPACITY, minCapacity)进行选择一个较大的,其中,DEFAULT_CAPACITY是ArrayList定义的静态常量10;扩容容量的计算:int newCapacity = oldCapacity + (oldCapacity >> 1),其中oldCapacity是原来的容量大小,oldCapacity >> 1 为位运算的右移操作,右移一位相当于除以2,所以这句代码就等于int newCapacity = oldCapacity + oldCapacity / 2;即容量扩大为原来的1.5倍;
ArrayList不会自动缩小容积,可以调用trimTosize方法进行,把elementdata调整到size;
90.银行家算法
https://blog.csdn.net/wyf2017/article/details/80068608
https://www.cnblogs.com/chuxiuhong/p/6103928.html
安全序列:是指一个进程序列{P1,…,Pn}是安全的,即对于每一个进程Pi(1≤i≤n),它以后尚需要的资源量不超过系统当前剩余资源量与所有进程Pj (j < i )当前占有资源量之和。找到安全序列就可以进行分配。其实就是给了他过后,收回来的时候能够满足其他的线程的运行。
安全状态:如果存在一个由系统中所有进程构成的安全序列P1,…,Pn,则系统处于安全状态。安全状态一定是没有死锁发生。
不安全状态:不存在一个安全序列。不安全状态不一定导致死锁。
91.两个字符串最长公共子序列与子串
子序列:
https://www.cnblogs.com/hapjin/p/5572483.html
xi = yi 的情况 f(x,y) = f(x-1,y-1)+1;
不相等的情况 f(x,y) = max { f(x-1,y),f(x,y-1) }
子串:
https://www.cnblogs.com/hushunfeng/articles/4030213.html
建立一个比较矩阵来比较两个字符串str1和str2, lcs(i,j) ,当str1[i] = str2[j]时lcs(i,j)=1,否则等于0。最长公共子串肯定就是斜线“1”最长的那个串。
采用DP的思想,如果str1[i] = str2[j],那么此处的包含str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]和str2[j-1]的公共子串的长度加1,那么现在我们可以重新定义lcs(i,j),即是lcs(i,j) = lcs(i-1,j-1) + 1,反之,lcs(i,j) = 0
92.线程状态图
https://www.cnblogs.com/wangle1001986/p/3593674.html
新建状态(New):新创建了一个线程对象。
就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
93.数据链路层主要功能
https://www.cnblogs.com/xiaofeng666/p/6718955.html
最基本的服务是将源计算机网络层来的数据传输到相邻节点的目标计算机的网络层。以及提供了成帧,差错控制,流量控制,链路管理,mac寻址等功能;
94.mac寻址
http://blog.51cto.com/07net01/586888
交换机使用 MAC 地址通过指向相应端口的交换结构将网络通信转向目的节点,交换机使用其 MAC 地址表来确定如何处理传入的数据帧。交换机通过记录与其每一个端口相连的节点的 MAC 地址来构建其 MAC 地址表。当某个特定端口上的某个特定节点的 MAC 地址记录到地址表之后,交换机就可以知道在后续传输中,应将目的地为该特定节点的流量从与该节点对应的端口上发出。当交换机收到传入的数据帧,而地址表中没有该帧的目的 MAC 地址时,交换机将把该帧从除接收该帧的端口之外的所有端口转发出去
95.客户端性能测试工具
https://www.cnblogs.com/puresoul/p/5503134.html
96.W模型
https://blog.csdn.net/v_forget/article/details/48977315
http://blog.sina.com.cn/s/blog_67a0fdd90100j69w.html
增加了软件个开发阶段的中的应用同步进行的验证和确认活动,基于“尽早地和不断地进行软件测试”的原则
97.V模型的缺点
http://blog.sina.com.cn/s/blog_67a0fdd90100j69w.html
把测试流程放在了编码流程的后面
由于它的顺序性,当编码完成之后,正式进入测试时,这时发现的一些bug可能不容易找到其根源,并且代码修改起来很困难。
实际中,由于需求变更较大,导致要重复变更需求、设计、编码、测试。返工量大
98.url重定向
https://blog.csdn.net/sdta25196/article/details/79104542
https://www.cnblogs.com/wuguanglin/p/redirect.html
301重定向(永久移动)是用户或搜索引擎蜘蛛向网站服务器发出访问请求时,服务返回的HTTP数据流中头信息(header)部分状态码的一种,表示本网址永久性转移到另一个地址。能使页面实现自动跳转。新的url放在了location中。
302临时重定向;
99.Class类的意义
https://www.cnblogs.com/yepei/p/5649276.html
Class类的对象用于表示被加载类的类型信息的对象。Class里面存储了对应类的几乎所有的信息。在object这个类中有一个方法:getclass().这个方法返回的是Class类的对象。我们自己无法生成一个Class对象,而这个Class类的对象是在当各类被调入时,由 Java 虚拟机自动创建 Class 对象
100.TCP中的time-wait
https://blog.csdn.net/hyman_c/article/details/53055466
之所以设置Time-wait状态,主要是考虑:万一由主机A发送的最后一次握手的数据包丢失时,主机B会再次发送第三次握手的数据包,若此时主机A关闭了Socket就会出现主机B永远等不到主机A最后一次握手数据包的情况,B也就永远无法关闭。所以在主动发起断开连接请求的主机上设置了这个Time-wait状态。
参考:https://blog.csdn.net/u011010851/article/details/81937742