内容分类 | 详情 |
---|---|
Java高频面试题 | 汇总入口 |
JVM | JVM面试题 |
并发 | 并发面试题 |
Spring | Spring面试题 |
分布式 | 分布式面试题 |
SpringBoot | SpringBoot面试题 |
SpringCloud | SpringCloud面试题 |
Dubbo | Dubbo面试题 |
MySQL | MySQL面试题 |
Mybatis | Mybatis面试题 |
Redis | Redis面试题 |
RocketMQ | RocketMQ面试题 |
算法 | 算法面试题 |
遇到的问题 | 遇到的问题 |
面试官的其他问题 | 面试官的其他问题 |
Git | Git面试题 |
文章目录
雪花算法
四个组成部分
不使用 :1bit,最高位是符号位,0 表示正,1 表示负,固定为 0
时间戳 :41bit,毫秒级的时间戳(41 位的长度可以使用 69 年)
标识位 :5bit 数据中心 ID,5bit 工作机器 ID,两个标识位组合起来最多可以支持部署 1024 个节点
序列号 :12bit 递增序列号,表示节点毫秒内生成重复,通过序列号表示唯一,12bit 每毫秒可产生 4096 个 ID
优点:
高性能高可用:生成时不依赖于数据库,完全在内存中生成
高吞吐:每秒钟能生成数百万的自增 ID
ID 自增:存入数据库中,索引效率高
排序算法
冒泡排序
- 外层循环控制几轮对比
- 内层控制每轮对比次数
- 内层相邻两个元素比较,并交换
for (int i = 0; i < list.size() - 1; i++) {
for (int j = 0; j < list.size() - i - 1; j++) {
int max = list.get(j);
if (list.get(j) > list.get(j + 1)) {
list.set(j, list.get(j + 1));
list.set(j + 1, max);
}
}
}
选择排序
每轮循环和剩下的元素对比找到最小值,并替换。
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
int temp = list.get(i);
if (list.get(i) > list.get(j)) {
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
插入排序
/**
* 升序
* 待比较数据:7, 6, 9, 8, 5,1
* 第一轮:指针指向第二个元素6,假设6左面的元素为有序的,将6抽离出来,形成7,_,9,8,5,1,从7开始,6和7比较,发现7>6。将7右移,形成_,7,9,8,5,1,6插入到7前面的空位,结果:6,7,9,8,5,1
* 第二轮:指针指向第三个元素9,此时其左面的元素6,7为有序的,将9抽离出来,形成6,7,_,8,5,1,从7开始,依次与9比较,发现9左侧的元素都比9小,于是无需移动,把9放到空位中,结果仍为:6,7,9,8,5,1
* 第三轮:指针指向第四个元素8,此时其左面的元素6,7,9为有序的,将8抽离出来,形成6,7,9,_,5,1,从9开始,依次与8比较,发现8<9,将9向后移,形成6,7,_,9,5,1,8插入到空位中,结果为:6,7,8,9,5,1
* 第四轮:指针指向第五个元素5,此时其左面的元素6,7,8,9为有序的,将5抽离出来,形成6,7,8,9,_,1,从9开始依次与5比较,发现5比其左侧所有元素都小,5左侧元素全部向右移动,形成_,6,7,8,9,1,将5放入空位,结果5,6,7,8,9,1。
* 第五轮:同上,1被移到最左面,最后结果:1,5,6,7,8,9。
*/
public static void insertSort() {
int[] arr = {38, 65, 97, 76, 13, 27, 49};
// 外层向右的index,即作为比较对象的数据的index
for (int index = 1; index < arr.length; index++) {
// 用作比较的数据
int temp = arr[index];
int leftindex = index - 1;
// 当比到最左边或者遇到比temp小的数据时,结束循环
while (leftindex >= 0 && arr[leftindex] > temp) {
arr[leftindex + 1] = arr[leftindex];
leftindex--;
}
// 把temp放到空位上
arr[leftindex + 1] = temp;
}
System.out.println(Arrays.toString(arr));
}
二分法排序
二分法是在插入排序的基础上进行了优化
优化点:确定插入的位置进行优化。
插入法是把当前数字和已排序的数据一次比较,二分法是通过折中的方式比较插入点是在左边还是右边,直到确定插入的位置。
[4 5 7 10 29]
//从小到大
public void sort() {
for (int i = 1; i < array.length; i++) {
int temp = array[i];
int low = 0, high = i - 1;
int mid = -1;
// low确定要插入的位置
while (low <= high) {
mid = low + (high - low) / 2;
if (array[mid] > temp) {
high = mid - 1;
} else { // 元素相同时,也插入在后面的位置
low = mid + 1;
}
}
// 把需要移动的元素往后移动一位,给low脚标让出位置
for(int j = i - 1; j >= low; j--) {
array[j + 1] = array[j];
}
// 把元素插入到low脚标
array[low] = temp;
}
}
hash算法
哈希函数实际上是关键字到内存单元的映射。哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构。hash算法是不可逆的。
String的hashCode方法,字符对应的二进制码
MD5算法
加密:https://cmd5.com/hash.aspx?s=123456
解密:https://cmd5.com/
算法原理
因为MD5是单向散列函数,输入任意长度的信息,经过处理,输出为128位的信息;不同的输入得到的不同的结果;根据128位的输出结果不可能反推出输入的信息。所以不能从密文(散列值)反过来得到原文,即没有解密算法。
1、数据填充
对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。
填充方法:在消息后面进行填充,填充第一位为1,其余为0。
2、添加消息长度
在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。
在此步骤进行完毕后,最终消息长度就是512的整数倍。
3、数据处理
准备需要用到的数据:
4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
4个函数:F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z)); H(X,Y,Z)=X ^ Y ^ Z; I(X,Y,Z)=Y ^ (X | (~Z));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。
MD5算法可以破解么?为什么?网上有在线破解是怎么回事
还有种破解就是采用彩虹表和字典:通过数据库把常见字符转的MD5存储起来为彩虹表,然后直接反查。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。
算法数据结构模拟网站
https://visualgo.net/zh/sorting
String str = “44r5t6uutij333224”,求最长的连续不重复字符的子串?
/**
* String str = "44r5t6uutij333224",求最长的连续不重复字符的子串
*/
@Test
public void testLongestSubstring() {
String str = "44r5t6uutij333224";
String longestSubstring = longestSubstring(str);
log.info(longestSubstring);
}
private String longestSubstring(String str) {
int[] index = new int[256];
int begin = 0;
int end = 0;
int maxLength = 0;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (index[c] == 0) {
end++;
} else {
if (maxLength < end - begin) {
maxLength = end - begin;
}
begin = index[c] + 1;
}
index[c] = i;
}
if (maxLength < end - begin) {
maxLength = end - begin;
}
return str.substring(begin, begin + maxLength);
}