由于笔试时需要退出其他软件录制屏幕开启摄像头,因此原题只根据回忆写出
第一题 M个数求最大的N个数和最小的N个数的和
题目
在提供的M个数里找最大的N个和最小的N个的和,需要自己去重,最大数集合和最小数集合有重合的则返回-1
输入 第一行是数组元素个数M,第二行是数组里的数字,每个数字用空格隔开,第三个数字是N
输入
6
2 3 1 5 12 7
2
输出
22
最大的2个数是 12 7,最小的两个数是1 2,则输出 12+7+1+2=22
输入
4
1 7 1 2 2
2
输出
-1
最大的2个数是7 2,最小的2个数是1 2,有重合的输出-1
思路
读取数据后,第一步去重,第二步根据去重后留下的数个数可以算出是否会返回-1,如果不返回-1则继续计算,将数组排序,取前后N个求和返回。也可以不排序,定义两个堆,一个最大堆,一个最小堆,让最大堆存最大的N个数,最小堆存最小的N个数。
代码
第一次快速写出的解答,时空复杂度上比后续用大小堆的更高
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String m = scanner.nextLine();
String arrayStr = scanner.nextLine();
String n = scanner.nextLine();
// int M = Integer.parseInt(m);
int N = Integer.parseInt(n);
String[] arrays = arrayStr.split(" ");
HashSet<Integer> numsSet = new HashSet<>();
for (String s : arrays){
if (!s.equals("")){
numsSet.add(Integer.parseInt(s));
}
}
//不合法的情况,有重叠
if (N * 2 > numsSet.size()){
System.out.println(-1);
return;
}
//没有数字那就直接不算了,和必定是0
if (N == 0 || numsSet.size() == 0){
System.out.println(0);
return;
}
Iterator<Integer> iterator = numsSet.iterator();
int[] nums = new int[numsSet.size()];
int i = 0;
while (iterator.hasNext()){
nums[i++] = iterator.next();
}
//排序
Arrays.sort(nums);
//求和
int sum = 0;
for (int j = 0,len = nums.length; j < N;j++){
sum += nums[j];
sum += nums[len - j -1];
}
System.out.println(sum);
}
}
大小堆处理思路
import java.util.*;
public class Main2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String m = scanner.nextLine();
String arrayStr = scanner.nextLine();
String n = scanner.nextLine();
// int M = Integer.parseInt(m);
int N = Integer.parseInt(n);
String[] arrays = arrayStr.split(" ");
HashSet<Integer> numsSet = new HashSet<>();
for (String s : arrays){
if (!s.equals("")){
numsSet.add(Integer.parseInt(s));
}
}
//不合法的情况,有重叠
if (N * 2 > numsSet.size()){
System.out.println(-1);
return;
}
//没有数字那就直接不算了,和必定是0
if (N == 0 || numsSet.size() == 0){
System.out.println(0);
return;
}
Iterator<Integer> iterator = numsSet.iterator();
//大小堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<>();
PriorityQueue<Integer> minHeap = new PriorityQueue<>(Comparator.reverseOrder());
while (iterator.hasNext()){
int i = iterator.next();
maxHeap.add(i);
if (maxHeap.size() > N){
//默认是个最小堆,但是我每次弹出掉最小的,这样最小堆成了最大堆了
maxHeap.poll();
}
minHeap.add(i);
if (minHeap.size() > N){
//原本是个最大堆,但是我每次弹出了最大的,反而这个堆成了个最小堆了
minHeap.poll();
}
}
//求和
int sum = 0;
while (!maxHeap.isEmpty()){
sum += maxHeap.poll();
}
while (!minHeap.isEmpty()){
sum += minHeap.poll();
}
System.out.println(sum);
}
}
第二题 分班问题
题目
假设现在有两个班级的学生排队的时候顺序打乱了, 但是每个同学知道自己前面的同学是不是自己班上的,需要你写个程序来把两个班的同学分出来
学生序号范围(0,999]
如果输入不合法则打印ERROR
打印时两组学生第一个学生编号小的放第一行,如果有个班是空的则放第二行打印一个空行。
输入
1/N 2/Y 3/N 4/Y 5/Y
输出
1 2
3 4 5
思路
这个既然他是两个班级,我就搞两个集合,定义为1班、2班,然后从第一个孩子开始,第一个孩子直接往1班送,然后后面那个孩子如果是同班就是在1班,否则就是2班,后面的学生就会像诺米勒骨牌效应一样,一个个都能算出来了,算完之后对1、2班集合里的学号排序,然后如果第二个集合的第一个学生学号比第一个集合的小就换个顺序,如果第一个空的第二个有,那也要换顺序,最后数据到位了,打印即可。
代码
public class Main {
public static final String ERROR = "ERROR";
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String data = scanner.nextLine();
if (!data.equals("")){
String[] datas = data.split(" ");
if (datas.length > 0){
List<Integer> cls1 = new ArrayList<>();
List<Integer> cls2 = new ArrayList<>();
Student[] students = new Student[datas.length];
for (int i = 0;i < datas.length ;i++){
try {
if (!datas[i].equals("")){
students[i] = new Student(datas[i]);
if (students[i].num <= 0 || students[i].num >999){
throw new RuntimeException("num error");
}
}
}catch (RuntimeException e){
System.out.println(ERROR);
return;
}
}
//先假设第一个同学是第一个班的(最后再根据最小学号来定一二班)
cls1.add(students[0].num);
int lastCls = 1;
if (students.length > 1){
for (int i = 1, len = students.length; i < len;i++){
Student student = students[i];
if (student.same){
//和上个人是同一个班
if (lastCls == 1){
cls1.add(student.num);
}else {
//不是同一个班的
cls2.add(student.num);
lastCls = 2;
}
}else {
if (lastCls == 1){
cls2.add(student.num);
lastCls = 2;
}else {
cls1.add(student.num);
lastCls = 1;
}
}
}
}
//全部分班完成之后,排序
if (cls1.size() > 0){
cls1.sort((Comparator.comparingInt(o -> o)));
}
if (cls2.size() > 0){
cls2.sort((Comparator.comparingInt(o -> o)));
}
//转换班级
if (cls1.size() > 0 && cls2.size() > 0){
//两个班都有学生
if (cls1.get(0) > cls2.get(0)){
//替换顺序(第一个编号小的放前面)
List<Integer> tmp = cls1;
cls1 = cls2;
cls2 = tmp;
}
}else {
//只有一个班有学生
if (cls2.size() > 0){
//替换顺序
List<Integer> tmp = cls1;
cls1 = cls2;
cls2 = tmp;
}
}
//打印输出
printList(cls1);
printList(cls2);
}else {
System.out.println(ERROR);
}
}else {
System.out.println(ERROR);
}
}
public static void printList(List<Integer> list){
if (list.size() > 0){
for (int i = 0,len = list.size();i < len;i++){
if (i != 0){
System.out.print(" ");
}
System.out.print(list.get(i));
}
System.out.print("\n");
}else {
System.out.println();
}
}
}
class Student {
int num;
boolean same;
public Student(String s){
//转化
String[] datas = s.split("/");
if (datas.length == 2){
this.num = Integer.parseInt(datas[0]);
this.same = datas[1].equals("Y");
}else {
throw new RuntimeException("format illegal");
}
}
@Override
public String toString() {
return "Student{" +
"num=" + num +
", same=" + same +
'}';
}
}
第三题 考古问题
题目
考古问题,假设以前的石碑被打碎成了很多块,每块上面都有一个或若干个字符,请你写个程序来把之前石碑上文字可能的组合全部写出来,排序按大小升序
输入
3
a b c
输出
abc
acb
bac
bca
cab
cba
输入
3
a b a
输出
aab
aba
baa
思路
一开始想先生成下标序列再转换为字符串,后面觉得吧,还不如我直接动态递归生成这些,我用StringBuilder即可,反正字符串必定是要生成的。
我把那些碎片当做一个字符串集合,每次取一块(排列组合),然后递归继续取,最后取完了就把最后的字符串丢到Set集合中去重。最后排个序输出。其中一定要注意List、StringBuilder这种对象进行递归调用传递参数的时候传递的是引用类型,所以每次操作完下去递归的时候都是需要复制一份的,不要直接传递会改的同一份的。
代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine());
String str = scanner.nextLine();
String[] strings = str.split(" ");
ArrayList<String> items = new ArrayList<>();
for (String s : strings){
items.add(s);
}
HashSet<String> hashSet = new HashSet<>();
dynamicCombine(items,new StringBuilder(),hashSet);
//最后排序
List<String> list = new ArrayList(strings.length);
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
list.add(iterator.next());
}
list.sort(((o1, o2) -> {
return ((String)o1).compareTo((String)o2);
}));
for (String s : list){
System.out.println(s);
}
}
/**
* 组合
* @param items
* @param builder
* @param ret
*/
public static void dynamicCombine(ArrayList<String> items,StringBuilder builder,HashSet<String> ret){
if (items.size() != 0){
for (int i = 0,len = items.size(); i < len ;i++){
StringBuilder newBuilder = new StringBuilder(builder);
newBuilder.append(items.get(i));
ArrayList<String> newItems = new ArrayList<>();
for (int j = 0; j < len;j++){
newItems.add(j,items.get(j));
}
newItems.remove(i);
dynamicCombine(newItems,newBuilder,ret);
}
}else {
if (builder.length() > 0){
ret.add(builder.toString());
}
return;
}
}
}