「这是我参与2022首次更文挑战的第31天,活动详情查看:2022首次更文挑战」。
LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode
851. 喧闹和富有
有一组 n 个人作为实验对象,从 0 到 n - 1 编号,其中每个人都有不同数目的钱,以及不同程度的安静值(quietness)。为了方便起见,我们将编号为 x 的人简称为 "person x "。
给你一个数组 richer ,其中 richer[i] = [ai, bi] 表示 person ai 比 person bi 更有钱。另给你一个整数数组 quiet ,其中 quiet[i] 是 person i 的安静值。richer 中所给出的数据 逻辑自洽(也就是说,在 person x 比 person y 更有钱的同时,不会出现 person y 比 person x 更有钱的情况 )。
现在,返回一个整数数组 answer 作为答案,其中 answer[x] = y 的前提是,在所有拥有的钱肯定不少于 person x 的人中,person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。
示例 1:
输入: richer = [[1,0],[2,1],[3,1],[3,7],[4,3],[5,3],[6,3]], quiet = [3,2,5,4,6,1,7,0]
输出: [5,5,2,5,4,5,6,7]
解释:
answer[0] = 5,
person 5 比 person 3 有更多的钱,person 3 比 person 1 有更多的钱,person 1 比 person 0 有更多的钱。
唯一较为安静(有较低的安静值 quiet[x])的人是 person 7,
但是目前还不清楚他是否比 person 0 更有钱。
answer[7] = 7,
在所有拥有的钱肯定不少于 person 7 的人中(这可能包括 person 3,4,5,6 以及 7),
最安静(有较低安静值 quiet[x])的人是 person 7。
其他的答案也可以用类似的推理来解释。
示例 2:
输入: richer = [], quiet = [0]
输出: [0]
提示:
n == quiet.length1 <= n <= 5000 <= quiet[i] < nquiet的所有值 互不相同0 <= richer.length <= n * (n - 1) / 20 <= ai, bi < nai != biricher中的所有数对 互不相同- 对 ****
richer的观察在逻辑上是一致的
PS:
现在所有的rich关系找到
a 比 b 有钱,就把a放到b的list里面
然后递归循环所有人的有钱列表,找到安静值最低的那个
class Solution {
public int[] loudAndRich(int[][] richer, int[] quiet) {
int n = quiet.length;
List<Integer>[] g = new List[n];
for (int i = 0; i < n; ++i) {
g[i] = new ArrayList<Integer>();
}
for (int[] r : richer) {
g[r[1]].add(r[0]);
}
int[] ans = new int[n];
Arrays.fill(ans, -1);
for (int i = 0; i < n; ++i) {
dfs(i, quiet, g, ans);
}
return ans;
}
public void dfs(int x, int[] quiet, List<Integer>[] g, int[] ans) {
if (ans[x] != -1) {
return;
}
ans[x] = x;
for (int y : g[x]) {
dfs(y, quiet, g, ans);
if (quiet[ans[y]] < quiet[ans[x]]) {
ans[x] = ans[y];
}
}
}
}
852. 山脉数组的峰顶索引
符合下列属性的数组 arr 称为 山脉数组 :
-
arr.length >= 3 -
存在
i(0 < i < arr.length - 1)使得:arr[0] < arr[1] < ... arr[i-1] < arr[i]arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1] 的下标 i 。
示例 1:
输入: arr = [0,1,0]
输出: 1
示例 2:
输入: arr = [0,2,1,0]
输出: 1
示例 3:
输入: arr = [0,10,5,2]
输出: 1
示例 4:
输入: arr = [3,4,5,1]
输出: 2
示例 5:
输入: arr = [24,69,100,99,79,78,67,36,26,19]
输出: 2
提示:
3 <= arr.length <= 1040 <= arr[i] <= 106- 题目数据保证
arr是一个山脉数组
进阶: 很容易想到时间复杂度 O(n) 的解决方案,你可以设计一个 O(log(n)) 的解决方案吗?
PS:
山脉的特点就是,两边都比当前点的值小
我们之间遍历一个方向,如果找到第一个当前值比右面大的,证明当前点就是峰顶
第一个是关键,降序的每一个比右面都大,我们找第一个就是峰顶
前面的都是升序的
如果是logn的方法,大概是二分的方法
因为这里是一个升序数组一个降序数组,找的时候根据当前是升序或者降序数组就可以得到峰顶
class Solution {
public int peakIndexInMountainArray(int[] arr) {
int n = arr.length;
int ans = -1;
for (int i = 1; i < n - 1; ++i) {
if (arr[i] > arr[i + 1]) {
ans = i;
break;
}
}
return ans;
}
}
853. 车队
在一条单行道上,有 n 辆车开往同一目的地。目的地是几英里以外的 target 。
给定两个整数数组 position 和 speed ,长度都是 n ,其中 position[i] 是第 i 辆车的位置, speed[i] 是第 i 辆车的速度(单位是英里/小时)。
一辆车永远不会超过前面的另一辆车,但它可以追上去,并与前车 以相同的速度 紧接着行驶。此时,我们会忽略这两辆车之间的距离,也就是说,它们被假定处于相同的位置。
车队 **是一些由行驶在相同位置、具有相同速度的车组成的非空集合。注意,一辆车也可以是一个车队。
即便一辆车在目的地才赶上了一个车队,它们仍然会被视作是同一个车队。
返回到达目的地的 车队数量 。
示例 1:
输入: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]
输出: 3
解释:
从 10 和 8 开始的车会组成一个车队,它们在 12 处相遇。
从 0 处开始的车无法追上其它车,所以它自己就是一个车队。
从 5 和 3 开始的车会组成一个车队,它们在 6 处相遇。
请注意,在到达目的地之前没有其它车会遇到这些车队,所以答案是 3。
示例 2:
输入: target = 10, position = [3], speed = [3]
输出: 1
解释: 只有一辆车,因此只有一个车队。
示例 3:
输入: target = 100, position = [0,2,4], speed = [4,2,1]
输出: 1
解释:
以0(速度4)和2(速度2)出发的车辆组成车队,在4点相遇。舰队以2的速度前进。
然后,车队(速度2)和以4(速度1)出发的汽车组成一个车队,在6点相遇。舰队以1的速度前进,直到到达目标。
提示:
n == position.length == speed.length1 <= n <= 1050 < target <= 1060 <= position[i] < targetposition中每个值都 不同0 < speed[i] <= 106
PS:
自定义car类,保存出发点和这个车将要运行的时候
我们判断两个车能不能成为车队,直接判断他们的剩余时间就可以
我们按照汽车的初始位置进行排序
然后我们倒叙循环,就是按照距离目标位置由近到远
如果距离目标近的车需要的时间大于他的下一位的时间,则这会组成一个车队
class Solution {
public int carFleet(int target, int[] position, int[] speed) {
int N = position.length;
Car[] cars = new Car[N];
for (int i = 0; i < N; ++i)
cars[i] = new Car(position[i], (double) (target - position[i]) / speed[i]);
Arrays.sort(cars, (a, b) -> Integer.compare(a.position, b.position));
int ans = 0, t = N;
while (--t > 0) {
if (cars[t].time < cars[t-1].time) ans++; //if cars[t] arrives sooner, it can't be caught
else cars[t-1] = cars[t]; //else, cars[t-1] arrives at same time as cars[t]
}
return ans + (t == 0 ? 1 : 0); //lone car is fleet (if it exists)
}
}
class Car {
int position;
double time;
Car(int p, double t) {
position = p;
time = t;
}
}
855. 考场就座
难度中等109收藏分享切换为英文接收动态反馈
在考场里,一排有 N 个座位,分别编号为 0, 1, 2, ..., N-1 。
当学生进入考场后,他必须坐在能够使他与离他最近的人之间的距离达到最大化的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外,如果考场里没有人,那么学生就坐在 0 号座位上。)
返回 ExamRoom(int N) 类,它有两个公开的函数:其中,函数 ExamRoom.seat() 会返回一个 int (整型数据),代表学生坐的位置;函数 ExamRoom.leave(int p) 代表坐在座位 p 上的学生现在离开了考场。每次调用 ExamRoom.leave(p) 时都保证有学生坐在座位 p 上。
示例:
输入: ["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
输出: [null,0,9,4,2,null,5]
解释:
ExamRoom(10) -> null
seat() -> 0,没有人在考场里,那么学生坐在 0 号座位上。
seat() -> 9,学生最后坐在 9 号座位上。
seat() -> 4,学生最后坐在 4 号座位上。
seat() -> 2,学生最后坐在 2 号座位上。
leave(4) -> null
seat() -> 5,学生最后坐在 5 号座位上。
提示:
1 <= N <= 10^9- 在所有的测试样例中
ExamRoom.seat()和ExamRoom.leave()最多被调用10^4次。 - 保证在调用
ExamRoom.leave(p)时有学生正坐在座位p上。
class ExamRoom {
int N;
TreeSet<Integer> students;
public ExamRoom(int N) {
this.N = N;
students = new TreeSet();
}
public int seat() {
//确定下一个的位置
int student = 0;
if (students.size() > 0) {
//dist:到最近学生的距离
//
//考虑左面的学生
int dist = students.first();
Integer prev = null;
for (Integer s: students) {
if (prev != null) {
//每对相邻的学生在位置 prev,s
//d:离学生最近的距离
int d = (s - prev) / 2;
if (d > dist) {
dist = d;
student = prev + d;
}
}
prev = s;
}
// 考虑右面
if (N - 1 - students.last() > dist)
student = N - 1;
}
//把学生添加到treeset里面
students.add(student);
return student;
}
public void leave(int p) {
students.remove(p);
}
}
856. 括号的分数
给定一个平衡括号字符串 S,按下述规则计算该字符串的分数:
()得 1 分。AB得A + B分,其中 A 和 B 是平衡括号字符串。(A)得2 * A分,其中 A 是平衡括号字符串。
示例 1:
输入: "()"
输出: 1
示例 2:
输入: "(())"
输出: 2
示例 3:
输入: "()()"
输出: 2
示例 4:
输入: "(()(()))"
输出: 6
提示:
S是平衡括号字符串,且只含有(和)。2 <= S.length <= 50
PS:
分割每段字符串,左括号为1,其他的为-1
如果bal为0,则括号平衡了,我们可以进行下一段了
class Solution {
public int scoreOfParentheses(String S) {
return F(S, 0, S.length());
}
public int F(String S, int i, int j) {
int ans = 0, bal = 0;
for (int k = i; k < j; ++k) {
bal += S.charAt(k) == '(' ? 1 : -1;
if (bal == 0) {
if (k - i == 1) ans++;
else ans += 2 * F(S, i+1, k);
i = k+1;
}
}
return ans;
}
}
858. 镜面反射
有一个特殊的正方形房间,每面墙上都有一面镜子。除西南角以外,每个角落都放有一个接受器,编号为 0, 1,以及 2。
正方形房间的墙壁长度为 p,一束激光从西南角射出,首先会与东墙相遇,入射点到接收器 0 的距离为 q 。
返回光线最先遇到的接收器的编号(保证光线最终会遇到一个接收器)。
示例:
输入: p = 2, q = 1
输出: 2
解释: 这条光线在第一次被反射回左边的墙时就遇到了接收器 2 。
提示:
1 <= p <= 10000 <= q <= p
class Solution {
double EPS = 1e-6;
public int mirrorReflection(int p, int q) {
double x = 0, y = 0;
double rx = p, ry = q;
// 循环没有到达的
while (!( close(x, p) && (close(y, 0) || close(y, p))
|| close(x, 0) && close (y, p) )) {
// 暴力循环,一直到只要存在一个到了
double t = 1e9;
if ((-x / rx) > EPS) t = Math.min(t, -x / rx);
if ((-y / ry) > EPS) t = Math.min(t, -y / ry);
if (((p-x) / rx) > EPS) t = Math.min(t, (p-x) / rx);
if (((p-y) / ry) > EPS) t = Math.min(t, (p-y) / ry);
x += rx * t;
y += ry * t;
if (close(x, p) || close(x, 0)) rx *= -1;
if (close(y, p) || close(y, 0)) ry *= -1;
}
if (close(x, p) && close(y, p)) return 1;
return close(x, p) ? 0 : 2;
}
public boolean close(double x, double y) {
return Math.abs(x - y) < EPS;
}
}
859. 亲密字符串
给你两个字符串 s 和 goal ,只要我们可以通过交换 s 中的两个字母得到与 goal 相等的结果,就返回 true ;否则返回 false 。
交换字母的定义是:取两个下标 i 和 j (下标从 0 开始)且满足 i != j ,接着交换 s[i] 和 s[j] 处的字符。
- 例如,在
"abcd"中交换下标0和下标2的元素可以生成"cbad"。
示例 1:
输入: s = "ab", goal = "ba"
输出: true
解释: 你可以交换 s[0] = 'a' 和 s[1] = 'b' 生成 "ba",此时 s 和 goal 相等。
示例 2:
输入: s = "ab", goal = "ab"
输出: false
解释: 你只能交换 s[0] = 'a' 和 s[1] = 'b' 生成 "ba",此时 s 和 goal 不相等。
示例 3:
输入: s = "aa", goal = "aa"
输出: true
解释: 你可以交换 s[0] = 'a' 和 s[1] = 'a' 生成 "aa",此时 s 和 goal 相等。
示例 4:
输入: s = "aaaaaaabc", goal = "aaaaaaacb"
输出: true
提示:
1 <= s.length, goal.length <= 2 * 104s和goal由小写英文字母组成
PS:
如果字符串一开始就相等的话,只要有一个字符的数量大于1,
我们交换相同字符的位置,
剩下的我们看不同的位置是不是大于2了,如果不同的位置大于2,即使交换也不能凑成目标字符串
class Solution {
public boolean buddyStrings(String s, String goal) {
if (s.length() != goal.length()) {
return false;
}
if (s.equals(goal)) {
int[] count = new int[26];
for (int i = 0; i < s.length(); i++) {
count[s.charAt(i) - 'a']++;
if (count[s.charAt(i) - 'a'] > 1) {
return true;
}
}
return false;
} else {
int first = -1, second = -1;
for (int i = 0; i < goal.length(); i++) {
if (s.charAt(i) != goal.charAt(i)) {
if (first == -1)
first = i;
else if (second == -1)
second = i;
else
return false;
}
}
return (second != -1 && s.charAt(first) == goal.charAt(second) &&
s.charAt(second) == goal.charAt(first));
}
}
}
860. 柠檬水找零
难度简单285收藏分享切换为英文接收动态反馈
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
示例 1:
输入: bills = [5,5,5,10,20]
输出: true
解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。
示例 2:
输入: bills = [5,5,10,10,20]
输出: false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。
示例 3:
输入: bills = [5,5,10]
输出: true
示例 4:
输入: bills = [10,10]
输出: false
提示:
1 <= bills.length <= 105bills[i]不是5就是10或是20
PS:
直接暴力模拟
如果是收的 5 直接收下,不需要找钱
如果收的 10 ,找一个 5,然后5的数量-1 10的数量+1
如果是二十的,找钱的时候考虑5和10一样一张
或者三张五元,
如果不符合,直接返回false
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0, ten = 0;
for (int bill : bills) {
if (bill == 5) {
five++;
} else if (bill == 10) {
if (five == 0) {
return false;
}
five--;
ten++;
} else {
if (five > 0 && ten > 0) {
five--;
ten--;
} else if (five >= 3) {
five -= 3;
} else {
return false;
}
}
}
return true;
}
}