腾讯笔试是五道题目,两个小时。
3道ACM模式,两道核心代码模式。
第1题
题目描述
有n个长度相等由数字组成的字符串,从上往下读,并排序,输出新的数字字符串,去掉前导0。
示例1
输入
3
0123
1234
2345
输出
12 123 234 345
思路分析
简单的字符串拆分拼接,以及list排序。
参考代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String[] str = new String[n];
for (int i = 0; i < n; i++) {
str[i] = in.nextLine();
}
int m = str[0].length();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < m; i++) {
String num = "";
for (int j = 0; j < n; j++) {
num += str[j].charAt(i);
}
list.add(Integer.parseInt(num));
}
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
System.out.println(list.get(i));
} else {
System.out.print(list.get(i) + " ");
}
}
}
}
第2题
题目描述
一个长度为n的数组a,数组下标从1~n。每一次将a中所有下标为非质数的元素进行删除,即ai且i不为质数。在删完之后,将数组a重新按顺序拼接起来,不断循环,直到数组大小为1,计算数组最后剩下的元素的值。
注:1不是质数
(本题是核心模式)
示例1
输入
[1,2,3,4]
输出
3
思路分析
本题是核心代码模式,这里自己写了输入进行测试。首先判断索引是不是质数,是质数的对应的索引-1加入list。然后循环计算。
参考代码
import java.util.ArrayList;
import java.util.List;
public class Main2 {
public static void main(String[] args) {
int a[] = new int[]{1,2,3,4};
System.out.println(getNumber(a));
}
public static int getNumber(int[] a) {
int n = a.length;
List<Integer> list = new ArrayList<>();
for (int i = 2; i < n; i++) {
if (isPrime(i)) { // 将质数索引加入list
list.add(i - 1);
}
}
while (n != 1) {
int k = 0;
for (int i = 0; i < list.size() && list.get(i) < n; i++) {
a[k++] = a[list.get(i)];
}
n = k;
}
return a[0];
}
public static boolean isPrime(int x){ // 判断是不是质数
int m = (int)(Math.sqrt(x) + 1);
for (int i = 2; i < m; i++) {
if (x % i == 0) {
return false;
}
}
return true;
}
}
第3题
题目描述
有n个战士站成1排,编号为1,2……n,战士的战斗力等于编号,一些战士只会进攻,一些战士只会防守,将他们从某个点分成两个阵营,假设这个点为pos,则0~pos的是进攻组,只算攻击力,pos+1~n的是防御组,只算防御力。pos可以取0~n。假设第一个阵营中进攻的战力总和为w,第二个阵营防守战力总和为v,求|w-v|最小值。
输入描述
第一行输入一个整数n,表示战士数量
第二行给一个字符串s,仅由0和1组成,0代表进攻,1代表防守。
输出描述
输出|w-v|最小值
示例1
输入
4
0011
输出
1
思路分析
前缀和,计算进攻方的战力总和,防守方的战力总和,再逐个求差值绝对值最小值。注意全进攻和全防守的情况。
参考代码
import java.util.Scanner;
public class Main3 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String str = in.nextLine();
long[] a = new long[n + 1];
long[] d = new long[n + 1];
for (int i = 1; i <= n; i++) {
a[i] = a[i - 1];
d[i] = d[i - 1];
if (str.charAt(i - 1) == '0') {
a[i] += i;
} else {
d[i] += i;
}
}
long res = Math.min(a[n], d[n]); // 全进攻全防守
for (int i = 1; i < n; i++) {
res = Math.min(res, Math.abs(a[i] - (d[n] - d[i])));
}
System.out.println(res);
}
}
第4题
题目描述
给出一个链表数组,该链表数组均是某一个环状链表的一部分,请将这些链表组合并成环状链表,然后需要找到一个位置,使得这个位置将环切开后,按照顺序或逆序遍历这个环,形成的链字典顺序尽量小,并返回这条链。
- 链表字典序的定义:对于两个链表a,b,从头节点到尾节点遍历,找到第一个不相同的节点值,如果存在i使得a[i].val < b[i].val,那么就让我a的字典序比较小。比如链表{1,2,3}<链表{1,2,4},链表{3,4,5}<链表{6,7}。
- 环状链表不存在相同的节点值
- 该题环状链表节点个数最小为2
- 每个链表都是环状链表上的顺时针的一部分
- 给定的链表数组一定能组成一个环状链表
(本题是核心模式)
示例1
输入
[{1,2,3},{2,3,4},{4,1}]
输出
{1,2,3,4}
示例2
输入
[{3,7,4},{7,4,5,1,10,3}]
输出
{1,5,4,7,3,10}
思路分析
链表中结点的值唯一,使用哈希表记录结点的前驱和后继,并记录最小值,然后从最小值开始遍历,并判断最小值的前驱和后继哪个更小,从更小的开始顺序遍历。
参考代码
import java.util.*;
class ListNode {
int val;
ListNode next = null;
public ListNode(int val) {
this.val = val;
}
}
public class Main4 {
public ListNode solve (ListNode[] a) {
// write code here
Map<ListNode, ListNode> map = new HashMap<>();
Map<Integer, ListNode> map2 = new HashMap<>();
for (ListNode node : a) {
ListNode pre = map2.computeIfAbsent(node.val, v -> new ListNode(v));
node = node.next;
while (node != null) {
ListNode mNode = map2.computeIfAbsent(node.val, v -> new ListNode(v));
map.put(pre, mNode);
pre = mNode;
node = node.next;
}
}
for (Map.Entry<ListNode, ListNode> entry : map.entrySet()) { // 对哈希表进行排序
entry.getKey().next = entry.getValue();
}
ListNode head = map2.get(a[0].val);
ListNode minNode = head;
ListNode p = head.next;
while (p != head) {
if (p.val < minNode.val) {
minNode = p;
}
p = p.next;
}
// 找到minNode最后的节点
ListNode node = minNode;
while (node.next != minNode) {
node = node.next;
}
int preVal = node.val;
ListNode next = minNode.next;
if (next.val < preVal) {
node.next = null;
return minNode;
}else {
// 反转链表
minNode.next = null;
reverse(next);
return minNode;
}
}
private void reverse(ListNode head) {
ListNode pre = null;
while (head != null) {
ListNode next = head.next;
head.next = pre;
pre = head;
head = next;
}
}
}
参考:腾讯4.24笔试
第5题
题目描述
现有一长度为n的数组a,表示每支股票的价格,每天最多买入或卖出该支股票的1手股票,买入或卖出没有手续费,且卖出股票前必须手里已经有股票才能卖出,但是持有的股票数目不受限制,并且初始资金为m元,在任意时刻不能进行透支,所以资金必须始终大于等于0,请问在n天结束后,拥有最大总资产是多少?(最大总资产为持有现金+持有的股票价格)
输入描述
第一行两个整数,n,m
第二行n个整数{ai},其中ai表示股票在第i天的售价
输出描述
输出n天结束后,拥有最大总资产
示例1
输入
6 2
2 3 1 1 1 2
输出
6
说明
第1天买入1手,第2天卖出1手,第3,4,5天都买入1手,最后总持有3手股票,股票价格为2,总资产为6
示例2
输入
3 2
1 1 4
输出
8
说明
第一天买入,第二天买入,第三天继续持有,总共持有两手股票,股票价格为4,总资产为8
示例3
输入
3 100
10 9 8
输出
100
说明
股票一直在下跌,都不买入,现金为100,股票为0,总资产为0
思路分析
看到股票问题,首先想到leetcode上的股票买卖问题,122. 买卖股票的最佳时机 II,但不同的是,买卖股票的最佳时机 II中在任何时候 最多 只能持有 一股 股票。而本题中同一时刻可以持有多手股票。
但思路相同,动态规划求解。
设dp[i][j]为第i天持有j个股票能拥有的最大现金。
每天的操作有3种,无操作,买入1手,卖出1手。买入的时候有限制条件,不能透支。则dp[i][j] = Math.min(dp[i-1][j] , dp[i][j+1] +prices[i] , dp[i][j-1]-prices[i])
需要注意的是,股票的价格可能很大,所以定义dp数组为long。
参考代码
import java.util.*;
public class Main5 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int price = in.nextInt();
int[] prices = new int[N];
for (int i = 0; i < N; i++) {
prices[i] = in.nextInt();
}
long[][] dp = new long[N][N]; // 注意这里定义为long
// dp[i][j]为第i天持有j个股票能拥有的最大现金额度,n天最多n-1支股票
Arrays.fill(dp[0], -1);
dp[0][0] = price; // 第0天,0注股票price元
if (price >= prices[0]) {
dp[0][1] = price - prices[0];
}
for (int i = 1; i < N; i++) {
for (int j = 0; j < N; j++) {
dp[i][j] = dp[i - 1][j]; // 无操作,不买入也不卖出
if (j > 0) { // 买入1注,且金额足够
if (dp[i - 1][j - 1] != -1 && dp[i - 1][j - 1] >= prices[i]) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] - prices[i]);
}
}
if (j < N - 1) { // 卖出1注
if (dp[i - 1][j + 1] != -1) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j + 1] + prices[i]);
}
}
}
}
long res = 0;
for (int i = 0; i < N; i++) {
if (dp[N - 1][i] == -1) continue;
res = Math.max(res, dp[N - 1][i] + i * prices[N - 1]);
}
System.out.println(res);
}
}