0%

阅文笔试2021笔试题复习

阅文笔试复习

1.详细描述ThreadPoolExecutor的各个参数的含义,介绍一个任务提交到线程池后的执行流程

  • corePoolSize : 线程池的核心大小,也可以理解为最小的线程池大小。

  • maximinPoolSize : 最大线程池大小

  • keepAliveTime:空余线程存活时间,指的是超过corePoolSize的空余线程达到多长时间才进行销毁。

  • util:销毁时间单位

  • WorkQueue:存储等待执行线程的工作队列。

  • threadFactory:创建线程的工厂,一般用默认即可。

  • handle:拒绝策略,当工厂队列,线程池全满时如何拒绝新任务,默认抛出异常

2.请简要说明Servlet中的生命周期

1.Servlet初始化后调用Init()方法

2.Servlet调用service()方法来处理客户端的请求。

3.Servlet销毁前调用destroy()方法终止

3.开启两个线程A,B,打印1到10 线程A打印奇数(1,3,5,7,9),线程B打印偶数(2,4,6,8,10).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yuewen;

import java.util.Scanner;
import java.util.concurrent.locks.LockSupport;

public class ABXianC {
static Thread thread1;
static Thread thread2;
public static void main(String[] args) {
thread1 = new Thread(() -> {
for (int i = 1; i <= 9; i += 2) {
System.out.println(i);
LockSupport.unpark(thread2);
LockSupport.park();
}
});
thread2 = new Thread(() -> {
for (int i = 2; i <= 10; i = i + 2) {
LockSupport.park();
System.out.println(i);
LockSupport.unpark(thread1);
}
});
thread1.start();
thread2.start();
}

}

4.请编写代码实现单例模式,类名为Singletion

1.饿汉模式

1
2
3
4
5
6
public class Singleton{
static private Singleton instance = new Singleton();//因为无法实例化,所以必须是静态的
static public Singleton getInstance(){
return instance;
}
}

2.懒汉线程安全

1
2
3
4
5
6
7
8
9
10
11
12
package com.yuewen;

import java.util.Scanner;

public class Singletion {
private static Singleton instance;
private Singletion(){};
public static synchronized Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}

5.写一个Map转换成JavaBean的工具类方法,实现如下mapToObject方法(使用Java反射,不允许使用第三方库)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static Object mapToObject(Map<String,Object> map,Class<?> beanClass){
if(map == null) return null;
Object obj = null;
try{
obj = beanClass.newInstance();
Field[] fields = obj.getClass().getDeclaredFields();
for(Field field : fields)
{
int mod = field.getModifiers();
if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
continue;
}
field.setAccessible(true);
field.set(obj,map.get(field.getName()));
}
catch(Exception e)
e.printStackTrace();
}
return obj;
}
}

6.数据库操作是我们经常使用的一个技能, 请你完成一个简单的用户密码验证过程 ,给定的条件如下:

数据库中存在个用户表:users ,表结构如下:

1
2
3
4
5
6
7
CREATE TABLE `users` (
`uid` bigint(20) NOT NULL COMMENT '用户ID',
`user_name` varchar(32) NOT NULL COMMENT '用户账号',
`password` varchar(64) NOT NULL COMMENT '用户混淆密码',
PRIMARY KEY (`uid`),
UNIQUE KEY `u_user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

完善以下方法

public boolean verifyPassword(String username,String password) {
Connection con = getConnection () ;// getConnection() 方法是个已有的方法可以获取到数据库连接 ,

// here is your code
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public boolean verifyPassword(String username,String password){
Connection con=getConnection;
String sql="SELECT password FROM users WHERE user_name=?";
PreparedStatement pst=null;
ResultSet rs=null;
boolean flag=false;
try{
pst=con.prepareStatement(sql);
pst.setObject(1,username);
rs=pst.executeQuery();
while(rs.next()){
if(rs.getString("password").equals(password)){
flag=true;
}
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch (SQLException e){
e.printStackTrace();
}finally {
try{
if(rs!=null) rs.close();
if(pst!=null) pst.close();
if(con!=null) con.close();
}catch(SQLException e){
e.printStackTrace();
}
}
return flag;
}

7.介绍HashMap的数据结构、扩容机制,HashMap与Hashtable的区别,是否是线程安全的,并介绍ConcurrentHashMap的实现机制。

HashMap JDK1.8之前 数组+链表

JDK1.8之后 数组+链表+红黑树

1.数组结构

数组用于存储内容,链表(红黑树)用于解决hash冲突。如果链表长度大于阈值8,但是当前数组长度小于树化阈值64,则进行数组扩容操作;如果数组长度大于树化阈值64,则进行链表树化操作,将单向链表转化为红黑树结构。

2.扩容机制:

如果不指定容量,则初始容量默认为16。如果指定容量,则初始容量设置为大于指定容量的最小2的幂数。当当前容量大于容量*负载因子(默认为0.75)时进行扩容操作,扩容为原容量的2倍。

3.HashMap与HashTable的区别

1)数据结构区别:HashMap为数组+链表(红黑树),HashTable为数组+链表,HashTable没有树化操作。

2)扩容机制区别:未指定容量情况下,HashMap容量默认16,每次扩容为2n(n:原容量)。HashTable容量默认为11,每次扩容为2n+1(n:原容量)。指定容量情况下,HashMap将保证容量为2的幂数,HashTable将直接使用指定容量。

3)数据插入方式的区别:当发生hash冲突时,HashMap使用尾插法插入链表,HashTable使用头插法插入链表。

4)线程安全区别:HashMap是非线程安全的,HashTable因为使用synchronized修饰方法,所以HashTable是线程安全的。

ConcurrentHashMap的实现机制

1)ConcurrentHashMap通过synchronized关键字和CAS操作实现线程安全,若插入的槽没有数据,使用CAS操作执行插入操作,若插入的槽有数据,通过synchronized锁住链表的头节点,从而实现效率与线程安全的平衡

8.介绍数据库连接池的实现方式。如何从连接池中获取连接、将连接放回连接池?使用连接池的优势是什么?列举一下自己用过的连接池。

连接池实现原理:

1.用户给servlet发送请求,请求Dao要Connection

2.Dao从“连接池”中取出Connection资源,与DB的通讯

3.当用户离开之后,释放该Connection,那么该Connection被释放到连接池中,等待下一个用户来

Demo目标:

通过简单的增删改查来做到下面几个关于连接池的方式,让我们更了解几种优化的方式

1.自定义一个Pool,来实现类似于现在开源连接池为我们做的一些操作

2.使用Tomcat内置的连接池(apache dbcp)

3.使用DBCP数据库连接池

4.使用C3P0数据库连接池(推荐)

数据库连接池技术带来的优势

1. 资源重用

由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。

2. 更快的系统响应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3. 新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。

4. 统一的连接管理,避免数据库连接泄漏

在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现:

9.什么是死锁?JAVA程序中什么情况下回出现死锁?如何避免出现死锁?

死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅会发生在线程之间,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。

基本上死锁的发生是因为:互斥条件,类似Java中Monitor都是独占的,要么是我用,要么是你用。互斥条件是长期持有的,在使用结束之前,自己不会释放,也不能被其它线程抢占。循环依赖关系,两个或者多个个体之间出现了锁的链条环。免死锁的思路和方法。****1、如果可能的话,尽量避免使用多个锁,并且只有需要时才持有锁。2、如果必须使用多个锁,尽量设计好锁的获取顺序。

3、使用带超时的方法,为程序带来更多可控性

10. 分布式锁有几种实现方式,并介绍每种方式的优缺点。

分布式锁一般有三种实现方式:
1、 数据库锁
2、基于Redis的分布式锁
3、基于ZooKeeper的分布式锁

11. 什么是TCP粘包拆包?为什么会出现粘包拆包?如何在应用层面解决此问题?

​ 如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题。

1、TCP是基于字节流的,虽然应用层和传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;

2、在TCP的首部没有表示数据长度的字段,

基于上面两点,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。

解决

1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
2、发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

12 请大致描述一下BIO,AIO和NIO的区别?

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO:异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

13 在JAVA语法中加载类的的方式有哪些?

1、创建类的实例(开辟地址空间)

2、访问某个静态类或接口的静态常量,或者对该静态变量赋值(类初始化)

3、调用类的静态访问(new,也会占用空间)

4、反射(类初始化)

5、初始化一个类的子类(继承)

6、JAVA虚拟机启动被称标明为启动类的类

7、调用某个 ClassLoader 实例的 loadClass() 方法(类不会初始化)

14 建立三个线程A、B、C,A线程打印10次字母A,B线程打印10次字母B,C线程打印10次字母C,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yuewen;

import java.util.Scanner;
import java.util.concurrent.locks.LockSupport;
public class ABC {
static Thread A, B, C;

public static void main(String[] args) {
A = new Thread(() -> {
for (int i = 0; i < 10; i++) {
LockSupport.park();
System.out.print("A");
LockSupport.unpark(B);
}
});
B = new Thread(() -> {
for (int i = 0; i < 10; i++) {
LockSupport.park();
System.out.print("B");
LockSupport.unpark(C);
}
});
C = new Thread(() -> {
for (int i = 0; i < 10; i++) {
LockSupport.unpark(A);
LockSupport.park();
System.out.print("C");
}
});
A.start();
B.start();
C.start();
}
}

15 请列举5个spring框架中的注解,并说明注解的用法以及使用场景

  • @component 标注一个POJO类
  • @Reposity 标注一个DAO类
  • @Service标注一个业务逻辑类
  • @Controller标注一个控制器类
  • @Autowired标注在字段或属性的setter 方法
  • @Before当前方面看成是前置通知
  • @After当前方面看成是始终通知
  • @Around当前方法看成环绕通知
  • @Aspect把当前类声明成切面类

16 给定一组自然数,数字的值有可能会大于2^64 ,要求计算出所有数字的和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import org.junit.Test;

import java.util.ArrayList;

public class Solution {

/**
* 计算一组数字的和
* @param numbers string字符串ArrayList 一组自然数
* @return string字符串
*/
public String sum (ArrayList<String> numbers) {
// write code here

String result="0";
for (String number : numbers) {

if(number==null||number.length()==0){
continue;
}
int resultLen = result.length();
int curNumLen = number.length();
int sum=0;
int remain;
StringBuilder stringBuilder = new StringBuilder();
while (resultLen>0||curNumLen>0){

int resultNum=0;
if(resultLen>0){
resultNum = result.charAt(--resultLen) - '0';
}

int curNum=0;
if(curNumLen>0){
curNum = number.charAt(--curNumLen) - '0';
}

sum=sum+resultNum+curNum;
remain=sum%10;
stringBuilder.append(remain);
sum/=10;
}

if(sum!=0){
stringBuilder.append(sum);
}
result=stringBuilder.reverse().toString();


}

return result;
}

@Test
public void test(){

String num1="123456";
String num2="123456789";
String num3="123456789123";
ArrayList<String> strings = new ArrayList<>();
strings.add(num1);
strings.add(num2);
strings.add(num3);
System.out.println(sum(strings));
}
}

17 给定一个int数字 要求计算出int数字对应的二进制中1的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.yuewen;

import java.util.Scanner;

public class Erjinzhi {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int ans = 0;
while (n > 0){
n = n & (n-1);
ans++;
}
System.out.println(ans);
}

}

18 根据产品策略某本书可以设置包月到期时间,需要计算指定时间到包月到期时间还有多少分钟,不足60S的不计入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static String dateSub(String a, String b) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.parse(a);
LocalDateTime t1 = LocalDateTime.parse(a, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
t2 = LocalDateTime.parse(b, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
long pass = t1.until(t2, ChronoUnit.MINUTES);
return String.valueOf(Math.abs(pass));
} catch (Exception e) {
System.out.println(String.format("格式转化错误:%s, 检查是否格式输入错误", e.getMessage()));
}
return "0";
}

public static void main(String[] args) {
System.out.println("请输入指定的两个日期【小者在前,大者在后,格式:yyyy-MM-dd hh:mm:ss】:");
Scanner sc = new Scanner(System.in);
String a = sc.nextLine(), b = sc.nextLine();
sc.close();
System.out.println(dateSub(a, b));
}

19 map是一种开发过程中经常使用的k-v数据结构,有个map保存了书名和书字数的关系,编写代码对map里面的书按照字数进行升序排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static Map<String, Integer> sortMap(Map<String, Integer> map) {
TreeMap<Integer, List<String>> treeMap = new TreeMap<>();
map.entrySet().forEach(entry -> {
List<String> indexList = treeMap.computeIfAbsent(entry.getValue(), k -> new ArrayList<>());
indexList.add(entry.getKey());
});
Map<String, Integer> result = new ListOrderedMap();
treeMap.entrySet().forEach(entry -> {
entry.getValue().forEach(key -> result.put(key, map.get(key)));
});
return result;
}

public static Map<String, Integer> sortMap2(Map<String, Integer> map) {
Map result = new ListOrderedMap();
map.entrySet().stream().
sorted(Map.Entry.comparingByValue()).
forEachOrdered(entry -> result.put(entry.getKey(), entry.getValue()));

return result;
}


20 起点APP上允许用户对作品进行评论,为了防止用户恶意评论,发表不当内容,需要对用户发布的内容进行过滤,请写程序过滤用户发布内容中带有的QQ号(6~10位数字组成) 允许对内容严格操作,如用户发表了 作者大大666666,为你点赞 ,经过过滤后也可以为作者大大****,为你点赞 ,将666666过滤掉了。

把6-10位的数字替换成””;

1
2
3
public static String filterQQ(String s) {
return s.replaceAll("\\d{6,10}","");
}

21 质数(又称素数),是指在大于1的自然数中,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为素数(质数)。请写个程序判断输入的数字是否是质数,如果是素数请输出:true,不是请输出false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package leecode;

public class IsPrimeDemo {
public static boolean isPrime(int n){
if (n < 1){
return false;
}
int i = 2;
int end = (int) Math.sqrt(n);
while (i <= end ){
if (n % i == 0){
return false;
}
++i;
}
return true;
}

public static void main(String[] args) {
int n = 7;
System.out.println(isPrime(n));
}
}

22 有 n 个台阶,你一次能走 1 个或者 2 个台阶,那么请问,走完这 n 个台阶共有几种方式?

经典爬楼梯问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.yuewen;

import java.util.Scanner;

public class ZouTaiJie {
public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] dp = new int[n];
int i = 2;
dp[0] = 1;
dp[1] = 2;
while (i < n)
{
dp[i] = dp[i-1] + dp[i-2];
i++;
}
System.out.println(dp[n-1]);
}

}

23 给定一个字符串,返回这个字符串中有多少个回文子串。两个相同的回文子串出现在不同的位置,认为是2个回文子串。a、aa、aaa、aba、aabaa、abcba均认为是回文子串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.*;


public class Solution {
/**
*
* @param str string字符串
* @return int整型
*/
public int palindromeCount (String str) {
int ans = 0;
for (int center = 0; center < str.length(); center++) {
ans += expand(str, center, center) + expand(str, center, center + 1);
}
return ans;
}

private int expand(String str, int left, int right) {
int ans = 0;
while (left >= 0 && right < str.length() && str.charAt(left) == str.charAt(right)) {
ans++;
left--;
right++;
}
return ans;
}
}

24 将一个给定的单链表反转,例:1-2-3-4-5,反转为5-4-3-2-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.*;

/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/

public class Solution {
/**
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode reverseList (ListNode head) {
ListNode pre = null, post = head, tmp;
while (post != null) {
tmp = post.next;//临时工具人结点
post.next = pre;//反转
pre = post;//pre进1
post = tmp;//post进1
}
return pre;
}
}

25 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

例:图中给定树 {3,5,1,6,2,0,8,#,#,7,4} 中,节点6、节点4的最近公共祖先为5。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.*;

/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/

public class Solution {
/**
*
* @param root TreeNode类
* @param p TreeNode类
* @param q TreeNode类
* @return TreeNode类
*/
public TreeNode nearestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root.val == p.val || root.val == q.val) {
return root;
}
TreeNode leftAns = nearestCommonAncestor(root.left, p, q),
rightAns = nearestCommonAncestor(root.right, p, q);
if (leftAns != null && rightAns != null) {
return root;
}
return leftAns != null ? leftAns : rightAns;
}
}

26 给定一个递增排序的数组,查找某个数字是否在数组中,如果在数组中,则返回该数字在数组中第一次出现的位置(从0开始);如果不在数组中,返回-1 。不需要考虑给定的数组不是递增的情况。务必使用二分查找的方式。

好像是错误的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int binarySearch(int* arr, int arrLen, int a) {
// write code here
int left = 0;
int right = arrLen-1;
int mid = 0;
while(left<=right)
{
mid = (left+right)/2;
if(arr[mid]==a)
{
if(arr[mid-1]!=a)
return mid;
else
return mid-1;
}
else if(arr[mid]>a)
right = mid - 1;
else
left = mid + 1;
}
return -1;
}

27 请编写程序实现矩阵的乘法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.util.*;
public class Main {
private static int[][] matrixMuilty(int[][] a, int[][] b) {
int m = a.length, p = b.length, n = b[0].length;
int[][] res = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < p; k++) {
res[i][j] += a[i][k] * b[k][j];
}
}
}
return res;
}

//输入三个整数,分别表示:第一个矩阵的 行 列【也就是第二个矩阵的行】 第二个矩阵的列
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String[] ss = s.split(",");
int m = Integer.parseInt(ss[0]), p = Integer.parseInt(ss[1]), n = Integer.parseInt(ss[2]);
if (m == 0 || p == 0 || n == 0) {
sc.close();
return;
}
int[][] a = new int[m][p];
int[][] b = new int[p][n];
for (int i = 0; i < m; i++) {
s = sc.nextLine();
ss = s.split(",");
for (int j = 0; j < p; j++) {
a[i][j] = Integer.parseInt(ss[j]);
}
}
for (int i = 0; i < p; i++) {
s = sc.nextLine();
ss = s.split(",");
for (int j = 0; j < n; j++) {
b[i][j] = Integer.parseInt(ss[j]);
}
}
sc.close();
int[][] res = matrixMuilty(a, b);
for (int i = 0; i < m; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < n; j++) {
sb.append(res[i][j]).append(",");
}
System.out.println(sb.substring(0, sb.length() - 1));
}
}
}

28 求出一个正整数转换成二进制后的数字“1”的个数。

例:数字23转为二进制为 10111,其中1的个数为4

1
2
3
4
5
public static int binaryTo(int num) { int sum = 0;  while (num > 0) {
sum += num % 2;
num = num / 2;
} return sum;
}

29 去除字符串中的重复字符,对于出现超过2次(包含2次)的字符,只保留第一个。

例:输入abcbdde,输出abcde。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.*;


public class Solution {
/**
*
* @param str string字符串
* @return string字符串
*/
public String removeDuplicatedChars (String str) {
// write code here
boolean[] isExistChar = new boolean[26];
boolean[] isExistNum = new boolean[10];
char[] chars = str.toCharArray();
StringBuilder sb = new StringBuilder();
for (char c : chars) {
//是字母
if(c >= 'a' && c <= 'z'){
if(!isExistChar[c - 'a']){
sb.append(c);
isExistChar[c - 'a'] = true;
}
}
//是数字
if(c >= '0' && c <= '9'){
if(!isExistNum[c - '0']){
sb.append(c);
isExistNum[c - '0'] = true;
}
}
}
return sb.toString();
}
}

30 给定一个整型数组,移除数组的某个元素使其剩下的元素乘积最大,如果数组出现相同的元素 ,请输出第一次出现的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String[] nums = scanner.next().split(",");
int n = nums.length;
// dp[i] -> 除 nums[i] 以外的其余所有元素的乘积
BigDecimal[] dp = new BigDecimal[n];
dp[0] = BigDecimal.valueOf(1);
for (int i = 1; i < n; i++) {
dp[i] = dp[i - 1].multiply(BigDecimal.valueOf(Integer.parseInt(nums[i - 1])));
}
BigDecimal temp = BigDecimal.valueOf(1);
for (int i = n - 1; i >= 0; i--) {
dp[i] = dp[i].multiply(temp);
temp = temp.multiply(BigDecimal.valueOf(Integer.parseInt(nums[i])));
}
BigDecimal max = dp[0];
int idx = 0;
for (int i = 1; i < n; i++) {
if (dp[i].compareTo(max) > 0) {
max = dp[i];
idx = i;
}
}
System.out.println(idx);
}
}

31 给定一个整型正方形矩阵 Matrix,请把该矩阵调整成顺时针旋转90度的样子。

1
2
3
4
5
6
7
8
9
10
11
12
public static int[][] rotationMatrix(int[][] matrix) {
if (matrix != null && matrix.length > 0 && matrix.length == matrix[0].length) {
int[][] result = new int[matrix.length][matrix.length];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
result[i][j] = matrix[matrix.length - j - 1][i];
}
}
return result;
}
return null;
}

32 在字符串中找到第一个不重复的字符。

例:对于字符串“hellohehe”,第一个不重复的字符是“o”。如果每个字符都有重复,则抛出运行时异常。

1
2
3
4
5
6
7
8
9
10
11
12
public char findFirstNonRepeatChar (String str) {
int[] map = new int[128];
for (char c: str.toCharArray()) {
map[c]++;
}
for (char c: str.toCharArray()) {
if (map[c] == 1) {
return c;
}
}
throw new RuntimeException("没有只有一个的字符");
}

33.假设有N个用户,其中有些人是朋友,有些则不是。A和B是朋友,B和C是朋友,这样ABC就是一个朋友圈,请计算给定的朋友关系的朋友圈数。

给定一个 N * N 的矩阵 M,表示用户之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个人互为朋友关系,否则为不知道。你必须输出所有用户中的已知的朋友圈总数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
String[][] relations = new String[n][n];
for (int i = 0; i < n; i++) {
relations[i] = scanner.next().split(",");
}
scanner.close();
System.out.println(friendCircle(relations));
}
private static int friendCircle(String[][] relations) {
int n = relations.length;
// graph[i] -> 用户 i 的所有朋友
Set[] graph = new HashSet[n];
for (int i = 0; i < n; i++) {
graph[i] = new HashSet();
for (int j = 0; j < n; j++) {
int relation = Integer.parseInt(relations[i][j]);
if (relation == 1) {
graph[i].add(j);
}
}
}
int ans = 0;
boolean[] visited = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
ans++;
dfs(graph, i, visited);
}
}
return ans;
}
private static void dfs(Set[] graph, int cur, boolean[] visited) {
visited[cur] = true;
for (int next : graph[cur]) {
if (!visited[next]) {
dfs(graph, next, visited);
}
}
}
}

34 假设有个文件,文件的每一行是书信息数据,分4个部分用逗号(,)进行分割,格式如下

id,category,words,updatetime

id 表示书id,long类型,id不重复;

category 表示书的分类,int类型,请注意全部数据的分类只有几个

words 表示书的字数,int类型

updatetime 表示书的更新时间 ,格式为2020-02-01 23:00:00

请编写程序对文件数据进行排序后输出id,排序优先级为: category>updatetime > words > id , 增序排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
Book[] books = new Book[n];
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < n; i++) {
String[] row = scanner.next().split(",");
try {
String updateTime = row[3] + " " + scanner.next();
Book book =
new Book(
Long.parseLong(row[0]),
Integer.parseInt(row[1]),
Integer.parseInt(row[2]),
dateFormat.parse(updateTime));
books[i] = book;
} catch (ParseException e) {
e.printStackTrace();
}
}
scanner.close();
// 升序排列。字段优先级:category > updateTime > words > id
Arrays.sort(books, (book1, book2) -> {
if (book1.getCategory() != book2.getCategory()) {
return book1.getCategory() - book2.getCategory();
}
long update1 = book1.getUpdateTime().getTime(), update2 = book2.getUpdateTime().getTime();
if (update1 != update2) {
return (int) (update1 - update2);
}
if (book1.getWords() != book2.getWords()) {
return book1.getWords() - book2.getWords();
}
return (int) (book1.getId() - book2.getId());
});
for (int i = 0; i < n; i++) {
System.out.println(books[i].getId());
}
}
}

class Book {
private final long id;
private final int category;
private final int words;
private final Date updateTime;

public Book(long id, int category, int words, Date updateTime) {
this.id = id;
this.category = category;
this.words = words;
this.updateTime = updateTime;
}

public long getId() {
return this.id;
}

public int getCategory() {
return this.category;
}

public int getWords() {
return this.words;
}

public Date getUpdateTime() {
return this.updateTime;
}
}

35请使用堆栈这一个数据结构实现简单FIFO(先入先出)队列,队列要实现两个方法: push、pop。

为自动测试方便,使用每行输入模拟操作:

1) push 1 表明向队列里面新增一个元素 1 , push 和元素之间用空格表示;

2) pop 表明输出当前队列里面的第一个元素,如果当前队列为空请输出null

请将每个输出以英文逗号拼接到一个字符串中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static class MyQueue {
int[] queue;
int lo, hi, size, capacity;

public MyQueue(int n) {
this.lo = this.hi = this.size = 0;
this.capacity = n;
this.queue = new int[n];
}

public MyQueue() {
this(10);
}

public void push(int val) {
if (hi == capacity) {
if (lo > 0) {
int idx = 0;
for (int i = lo; i < hi; i++) {
queue[idx++] = queue[i];
}
lo = 0;
hi = idx;
} else {
//扩容
int[] newQueue = new int[capacity * 2];
System.arraycopy(queue, 0, newQueue, 0, capacity);
this.queue = newQueue;
}
}
this.queue[hi++] = val;
}

public int pop() {
if (lo == hi) return -1;
else {
return this.queue[lo++];
}
}

}

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// int[] nums = Arrays.stream().mapToInt(Integer::parseInt).toArray();
String[] ss = scanner.nextLine().split(",");
YuewenJavaTest.MyQueue myQueue = new YuewenJavaTest.MyQueue();
for (String s: ss) {
if (s.startsWith("push")) {
myQueue.push(Integer.parseInt(s.split(" ")[1]));
} else {
System.out.println(myQueue.pop());
}
}

scanner.close();
}

36 在和外部公司联调HTTP接口时,对方要求调用的接口需要计算token,给到的规则如下:

1) 所有的参数值必须经过urlencode,编码为utf-8;

2) 对编码后数据按照key值进行字典升序排序;

3)将所有的参数按照排序的顺序拼接成字符串 ,格式如下: k1=v1&k2=v2&k3=v3;

\4) 将第三步的算出的值计算md5值,md5加密的值小写

请你编写一段方法计算token值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public String getToken(Map<String, String> params) {
List<String> ss = params.keySet().stream().sorted().map(
k -> {
String v = params.get(k);
String kv = null;
try {
kv = k + "=" + URLEncoder.encode(v, "utf-8");
} catch (Exception e) {

}
return kv;
}
).collect(Collectors.toList());
String token = null;
try {
token = new String(MessageDigest.getInstance("md5").digest(String.join("&", ss).getBytes()));
} catch (Exception e) {
}
return token;
}