饿了么2023年0817面试题
第一题
总体思路:先找出用了哪些字符,然后遍历字符串不同的就修改,重要的点是为了每个字符都被用到,需要++操作。
public class 小红的字符串构造 {
//小红拿到了一个字符串
//s,她准备构造一个和s长度相同的字符串
//t:满足以下条件:
//1.t的字符集和s的相同(去重后的,也就是说不考虑数量)
//2.t的每个位置的字符都和s不同。
//例如若 s="aabbc",那么t可以构造为"cbaca"。你能帮帮小红吗?
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
Set<Character> charSet = new HashSet<>();
for (char c : s.toCharArray()) {
charSet.add(c);
}
if (charSet.size() == 1) {
System.out.println(-1);
return;
}
// 将字符集合转换为字符数组,并排序
Character[] uniqueChars = charSet.toArray(new Character[0]);
Arrays.sort(uniqueChars);
// 构造字符串 t
StringBuilder t = new StringBuilder();
int idx = 0;
//为了出现所有字符,用一个就下一个
for (char c : s.toCharArray()) {
if (c == uniqueChars[idx]) {
t.append(uniqueChars[(idx + 1) % uniqueChars.length]);
} else {
t.append(uniqueChars[idx]);
}
idx = (idx + 1) % uniqueChars.length;
}
System.out.println(t.toString());
}
}
第二题
能想到用送出时间做基准进行判断,第二天怎么处理之类的
* 小红在饿了么上面点了一个外卖,饥肠辘辘的她等骑手等得望眼欲穿。
* 已知小红在时刻t1点了外卖,饿了么上面显示预计送达时间为 t2,实际送达时间为t3 。请你判断外卖是否超时?
*/
public class 小红等外卖 {
// 第一行输入一个正整数t,代表询问次数。
//接下来的3*t行,每 3 行代表一次询问:
//第一行为点外卖的时刻t_1,第二行为预计送达时间t_2,第三行为实际送达时间t_3。
//保证骑手送外卖的预期花费时间和实际花费时间均不超过 2 小时。
//难点在于找到基准点分类讨论,time1可以作为基准点,正数距离越小越近,负数绝对值越大越远,通过这个进行讨论
public static boolean isFoodDelayed(String t1, String t2, String t3) {
// 将时间字符串转换为分钟表示
int hour1 = Integer.parseInt(t1.substring(0, 2));
int min1 = Integer.parseInt(t1.substring(3));
int time1 = hour1 * 60 + min1;
int hour2 = Integer.parseInt(t2.substring(0, 2));
int min2 = Integer.parseInt(t2.substring(3));
int time2 = hour2 * 60 + min2;
int hour3 = Integer.parseInt(t3.substring(0, 2));
int min3 = Integer.parseInt(t3.substring(3));
int time3 = hour3 * 60 + min3;
if (time3-time1<0&&time2-time1<0){
return Math.abs(time3-time1) < Math.abs(time2-time1) ;
}else if (time3-time1<0&&time2-time1>0){
return true;
} else if (time3-time1>0&&time2-time1<0){
return false;
}else return time3>time2;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt(); // 询问次数
scanner.nextLine(); // 消耗换行符
for (int i = 0; i < t; i++) {
String t1 = scanner.nextLine(); // 点外卖的时间
String t2 = scanner.nextLine(); // 预计送达时间
String t3 = scanner.nextLine(); // 实际送达时间
boolean isDelayed = isFoodDelayed(t1, t2, t3);
if (isDelayed) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
}
第三题
主要就是图求距离。
* 小红在第三新北林市的学园城送外卖,学园城中有非常多的学校,学园城里有一个美食街。
* 小红每次会接一些同一个学校的订单,然后从美食街取餐出发,再骑车将外卖送到学校,最后回到美食街,
* 以此往复学园城有 n 个结点, m 条道路,美食街为1号结点,剩下的结点都是学校,保证学园城中所有结点连通。
* 给出小红每次要送外卖的学校,请计算小红最少需要骑行的距离。
*/
public class 小红送外卖 {
static int[] curSide ;
static int[] former;
static int[] to;
static int[] weight;
static int cnn;
//思路,迪克斯拉算法,求得从某个点到所有地方的距离之和
//可以用前向星+迪克斯拉算法
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 结点数
int m = scanner.nextInt(); // 边数
int q = scanner.nextInt(); // 送外卖次数
curSide = new int[n+1];
//无向图
former=new int[2*m+1];
to=new int[2*m+1];
weight=new int[2*m+1];
cnn=0;
for (int i = 0; i < m; i++) {
//建前向星图
int from = scanner.nextInt();
int arrive=scanner.nextInt();
int w=scanner.nextInt();
add(from,arrive,w);
add(arrive,from,w);
}
//此时迪克斯拉算法
long[] distance=new long[n+1];
Arrays.fill(distance,Integer.MAX_VALUE);
int[] visited=new int[n+1];
long sum=0;
PriorityQueue<int[]> heap = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1]-o2[1];
}
});
int cur=0;
heap.add(new int[]{1,0});
while (!heap.isEmpty()&&cur<n) {
int[] poll = heap.poll();
int pos = poll[0];
int dis = poll[1];
if (visited[pos] == 1) {
//看过就不管了
continue;
}
//没看过
//标记为看过,写距离
visited[pos] = 1;
distance[pos] = dis;
//将当前这个点的所有边放进堆里面去
int curS = curSide[pos];
while (curS != 0) {
//这条边去哪
int go = to[curS];
//这条边的权重
int w = weight[curS];
distance[go]=Math.min(distance[go],dis+w);
//处理当前链路上的所有边
heap.add(new int[]{go, dis + w});
//处理完后去下一条边
curS = former[curS];
}
cur++;
}
//此时distance里面应该是有数据了
for (int i = 0; i < q; i++) {
sum+=distance[scanner.nextInt()];
}
System.out.println(sum<<1);
}
private static void add(int from, int arrive, int w) {
cnn++;
//这条边的former指向上一条边
former[cnn]=curSide[from];
//这条边的to指向to
to[cnn]=arrive;
weight[cnn]=w;
curSide[from]=cnn;
}
顺便复习了图:
三种建图方式,第一是二维表,第二十临接表(list套list),第三种就是前向星,主要部分就是
static int[] curSide ;//代表当前点对应哪条边
static int[] former;//代表这条边指向的上一条边是哪里
static int[] to;//这条边的指向
static int[] weight;//这条边的权重
static int cnn;//到第几条边了
思路在于:curside[i]是指向的连接自己的某条边,而通过former[i]又能找到上一条,0这个位置不用,当找到0的时候说明这个点的所有边都找完了,找这些边的时候就可以穿插迪克斯拉和Prim算法进去了,迪克斯拉主要是能找到从某个点开头到所有点的距离集合,通过一个点找到这个点的所有边,全部放进小根堆里面去,pop出来一条就处理一条,处理到数目就完成了(还有一种是加强堆,带了个调整,写不动了)。prim是最小生成树,点解锁边,把解锁的边全部放进去小根堆里面,一个个弹出来,全部都处理完了就结束。k算法是全部边放进去小根堆,然后联合并查集,全部都找到之后也结束。