总结
虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。
上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料
有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。
class HuffmanNode implements Comparable {
int value;
HuffmanNode left;
HuffmanNode right;
public HuffmanNode(int value) {
this.value = value;
}
@Override
public String toString() {
return “HuffmanNode{” +
“value=” + value +
‘}’;
}
@Override
public int compareTo(HuffmanNode o) {
//升序
return this.value - o.value;
}
//前序遍历
public static void preOrderList(HuffmanNode node) {
System.out.println(node);
if (node.left != null) {
preOrderList(node.left);
}
if (node.right != null) {
preOrderList(node.right);
}
}
}
复制代码
赫夫曼编码
-
算法,数据文件压缩,可变字长编码(VLC)的一种
-
变长编码,统计每个字符出现的次数,字数越多,对应的二进制位越少,但是会有多义性
-
赫夫曼编码,无损压缩,将字符出现的次数构建成一颗赫夫曼树,次数作为权值;向左为0,向右为1,按照根节点到叶子节点的路径作为这个字符的编码,避免多义性
-
借助了赫夫曼树的特点
权值越大离根节点越近
,那么字符出现次数越多,编码长度越小 -
如果赫夫曼树中有多个相同的权重值,会有可能导致形成的树的结构不一样,但是wpl是一样的,这样生成的赫夫曼编码不一样,但是压缩后的大小/长度是一样的
压缩
- 效果,将一个
字节数组转换成赫夫曼编码字节数组
/**
-
返回赫夫曼编码字节数组
-
@param bytes 原始字节数组
-
@return
*/
private static byte[] huffmanZip(byte[] bytes) {
List nodes = getNodes(bytes);
HuffmanCodeNode node = createHuffmanTree(nodes);
Map<Byte, String> huffmanCodes = getHuffmanCodes(node);
return zip(bytes, huffmanCodes);
}
复制代码
- 先将
bytes转换成list
,便于生成赫夫曼树
public static List getNodes(byte[] bytes) {
ArrayList nodes = new ArrayList<>();
//遍历bytes统计每个出现的次数
HashMap<Byte, Integer> map = new HashMap<>();
Integer count = 0;
//存入字符-字符个数
for (byte b : bytes) {
count = map.get(b);
if (count == null) {
map.put(b, 1);
} else {
map.put(b, count + 1);
}
}
//把map转换成node对象,node对象包括字符
和权重(次数)
map.forEach((b, val) -> nodes.add(new HuffmanCodeNode(b,val)));
return nodes;
}
复制代码
- 生成赫夫曼树,获取根节点
public static HuffmanCodeNode createHuffmanTree(List nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
HuffmanCodeNode left = nodes.get(0);
HuffmanCodeNode right = nodes.get(1);
//创建新的二叉树节点,没有字符,只有值
HuffmanCodeNode parent = new HuffmanCodeNode(null, left.weight + right.weight);
parent.left = left;
parent.right = right;
nodes.remove(left);
nodes.remove(right);
nodes.add(parent);
}
return nodes.get(0);
}
}
复制代码
- 生成赫夫曼编码表
//编码表,放在map<Byte, String>
static Map<Byte, String> huffmanCodes = new HashMap<>();
//负责拼接编码
static StringBuilder stringBuilder = new StringBuilder();
//重载,调用时直接传入根节点
public static Map<Byte, String> getHuffmanCodes(HuffmanCodeNode node) {
if (node == null) {
return null;
}
getHuffmanCodes(node.left, “0”, stringBuilder);
getHuffmanCodes(node.right, “1”, stringBuilder);
return huffmanCodes;
}
/**
-
将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入的huffmanCodes
-
@param node 传入根节点
-
@param code 路径 左子节点为0 右子节点为1
-
@param stringBuilder 拼接路径
*/
public static void getHuffmanCodes(HuffmanCodeNode node, String code, StringBuilder stringBuilder) {
//生成一个新的StringBuilder,因为每次遇到非叶子节点,都会进入递归,相当于进行了分叉
//所以,每次进入递归,都要再次生成一个新的,否则会重复拼接
StringBuilder stringCode = new StringBuilder(stringBuilder);
stringCode.append(code);
if (node != null) {
//判断当前是什么节点
if (node.data == null) {
//非叶子节点,递归处理
//左
getHuffmanCodes(node.left, “0”,stringCode);
//右
getHuffmanCodes(node.right,“1”,stringCode);
} else {
//找到某个叶子节点
huffmanCodes.put(node.data, stringCode.toString());
}
}
}
复制代码
- 生成对应赫夫曼编码字节数组,因为字节数组转换成二进制字符串的时候,末尾如果是0开头的,开头将会被舍去,所以要另外用
endString
记录
//存放结尾的编码
static String endString = “”;
/**
-
将一个字符串对应的byte数组,通过赫夫曼编码表,返回赫夫曼编码压缩后的byte数组
-
@param bytes 原始字符数组
-
@param huffmanCodes 经过赫夫曼编码处理后的字符编码
-
@return 原始字符编码数组
-
java的数字都是以补码的形式出现的,byte要转为数字,也要把补码转换成原码
-
正数三码合一
-
负数补码 = 原码保持符号为不变按位取反 + 1
-
byte[] 一个字节存8位带符号数的二进制 需要-> -1 反码 ->保留符号为,取反转换成原码->十进制
-
10101000(补码) => 10101000 - 1 => 10100111 取反 => 11011000 => -88
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
StringBuilder stringBuilder = new StringBuilder();
//获取字符对应的赫夫曼编码,并拼接
for (byte b : bytes) {
stringBuilder.append(huffmanCodes.get(b));
}
//转换成byte数组
//如果不能被8整除,加上7,一定能被8整除;如果能被8整除,加上7,多出来的部分也不会影响结果
//int len = (stringBuilder.length() + 7) / 8;
int len = stringBuilder.length() % 8 == 0 ? stringBuilder.length() / 8 : stringBuilder.length() / 8 + 1;
if (stringBuilder.length() - (len - 1) * 8 != 0) {
//处理末尾
endString = stringBuilder.substring((len - 1) * 8, stringBuilder.length());
}
//创建存储压缩后的byte数组
byte[] huffmanCodeBytes = new byte[len];
String strByte;
//记录第几个byte
int index = 0;
for (int i = 0; i < stringBuilder.length(); i += 8) {
if (i + 8 > stringBuilder.length() - 1) {
//不够8位
strByte = stringBuilder.substring(i);
} else {
strByte = stringBuilder.substring(i, i + 8);
}
huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte,2);
index ++;
}
return huffmanCodeBytes;
}
复制代码
解压
- 调用
byte[] source = decode(huffmanCodes, res);
复制代码
- 完成对压缩数据的解码,本质就是将压缩完成的字节数组和对应的赫夫曼编码表传入,解码成原来的字节数组
/**
-
@param huffmanCodes 赫夫曼编码表
-
@param huffmanBytes 赫夫曼编码得到的字节数组,被解压的数组
-
@return
*/
private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转换成字符串
for (int i = 0; i < huffmanBytes.length; i++) {
boolean flag = i == huffmanBytes.length - 1;
stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
}
//按照编码表解码
//要将编码表调转
HashMap<String, Byte> map = new HashMap<>();
huffmanCodes.forEach((b, s) -> map.put(s,b));
//创建集合存放byte
ArrayList list = new ArrayList<>();
int count;
for (int i = 0; i < stringBuilder.length(); i += count) {
//扫描对应的二进制字符串
count = 1;
boolean flag = true;
Byte b = null;
while (flag) {
//取出一位
//让count移动,直到取到一个存在的字符
String key = stringBuilder.substring(i, i + count);
b = map.get(key);
if (b == null) {
count ++;
} else {
//匹配到
flag = false;
}
}
list.add(b);
//i移动到count的位置
//i += count;
}
//循环结束后,存放所有的字符
byte[] bytes = new byte[list.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = list.get(i);
}
return bytes;
}
复制代码
- 将一个byte转换成二进制字符串
/**
-
将一个byte转换成二进制字符串
-
@param flag 标志是否需要补高位,如果是true需要补高位,如果false不补;如果是最后一个字节不需要补高位
-
@param b 对应的是一个字节,二进制的字符串,是按补码的形式
-
@return
*/
private static String byteToBitString(boolean flag, byte b) {
//使用变量保存b
//将b转换成int
int temp = b;
if (flag) {
//2^8
temp |= 256; //按位或
}
//11111111111111111111111110101000
//实际上是取后8位
String s = Integer.toBinaryString(temp);
//转换成b的补码,负数时,需要裁剪;正数需要补位
if (flag) {
return s.substring(s.length() - 8);
} else {
//处理末尾的时候,直接拼接保存好的endString
return endString;
}
}
复制代码
针对文件
- 将文件进行压缩
/**
-
@param srcFile 来源
-
@param desFile 目标
*/
private static void zipFile(String srcFile, String desFile) {
FileInputStream fis = null;
FileOutputStream ops = null;
ObjectOutputStream oos = null;
try {
fis = new FileInputStream(srcFile);
//创建与原文件大小一样的数组
byte[] bytes = new byte[fis.available()];
//读取
fis.read(bytes);
//编码
byte[] huffmanBytes = huffmanZip(bytes);
最后
面试题文档来啦,内容很多,485页!
由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。
1111道Java工程师必问面试题
MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:
Elasticsearch 24 题 +Memcached + Redis 40题:
Spring 26 题+ 微服务 27题+ Linux 45题:
Java面试题合集:
485页!**
由于笔记的内容太多,没办法全部展示出来,下面只截取部分内容展示。
1111道Java工程师必问面试题
[外链图片转存中…(img-Cph4oVmB-1715563813705)]
MyBatis 27题 + ZooKeeper 25题 + Dubbo 30题:
[外链图片转存中…(img-1ZF8WYgc-1715563813705)]
Elasticsearch 24 题 +Memcached + Redis 40题:
[外链图片转存中…(img-kUWpCB1L-1715563813706)]
Spring 26 题+ 微服务 27题+ Linux 45题:
[外链图片转存中…(img-iJE847Gt-1715563813706)]
Java面试题合集:
[外链图片转存中…(img-c5MrU2zf-1715563813707)]