文章目录
🌕 前言:关于JAVA刷题
🤙关于JAVA的学习出了看视频以外,那就是刷题了,朋友们,你们有没有过这样的感觉,在网上看了视频过后感觉自己什么都听懂了,但就是写题和做项目时无从下手,或者就是因为某个细节一直错一直改,那背后的原因是什么呢?四个字——题刷少了,这里新一建议去
Leetcode
看看,那里的题库资源很丰富,并且在全球都有广泛涉猎。不仅如此,这里还有 课程 + 刷题 + 面经 + 求职 + 讨论区分享解题思路,用过的人都说好😜
👉除此之外,我的建议是初学者从简单题
开始练习,因为简单题是一切题的基础,一切的困难题都是从简单题衍生而来的,每天刷那么2~3题,后期再慢慢刷中等题,困难题,经过一段时间后会有很不错的提升
此外,在我们有一定的提升之后,我们便可以去刷剑指offer
了,在这里预祝各位大学生以及初学者都拿到自己满意的offer!💖
第一题:无法吃午餐的学生数量
👇👇👇
做题链接戳这里:1700.无法吃午餐的学生数量
🍂 题目描述
学校的自助午餐提供圆形和方形的三明治,分别用数字 0 和 1 表示。所有学生站在一个队列
里,每个学生要么喜欢圆形的要么喜欢方形的。餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 栈
里,
每一轮:
● 如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。
● 否则,这名学生会 放弃这个三明治 并回到队列的尾部。
● 这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。
给你两个整数数组 students
和 sandwiches
,其中 sandwiches[ i ] 是栈里面第 i 个三明治的类型(i = 0 是栈的顶部), students[ j ] 是初始队列里第 j 名学生对三明治的喜好(j = 0 是队列的最开始位置)。请你返回无法吃午餐的学生数量。
🍂示例
输入:students = [1,1,0,0], sandwiches = [0,1,0,1]
输出:0
解释 :
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,0,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,0,1,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [0,1,1],三明治栈为 sandwiches = [1,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,1,0]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1,0],三明治栈为 sandwiches = [0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1],三明治栈为 sandwiches = [1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [],三明治栈为 sandwiches = []。
所以所有学生都有三明治吃。
示例2
输入:students = [1,1,1,0,0,1], sandwiches = [1,0,0,0,1,1]
输出:3
🍂提示
● 1 <= students.length, sandwiches.length <= 100
● students.length == sandwiches.length
● sandwiches[i] 要么是 0 ,要么是 1 。
● students[i] 要么是 0 ,要么是 1
🍃题解
常规思路:
这题目好长啊我靠,没关系,不知大家上学时做数学题有没有这样一种感觉:越长的题越好做,越短的题越难😂,没关系,我们慢慢的读题就OK了,读懂题之后,我们知道学生用队列来存放,而三明治用栈来存放,最容易想到的是,遍历整个队列,由于跟栈长度相同,元素相同即匹配,我们出栈 + 出队,如果不匹配那么出队后重新入队,不过存在一个问题,它有一个条件是如果栈顶元素都不满足队列内所有学生需求示记录队列长度并退出,这时我们就需要一个count
来接收,每不匹配一个则count++
,若匹配count重新规0,如果count == queue.size()
我们直接break
class Solution{
public int countStudents(int[] students, int[] sandwiches) {
Queue<Integer> queue = new LinkedList<>();
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < students.length; i++) {
queue.offer(students[i]);
}
for (int i = sandwiches.length - 1; i >= 0; i--) {
stack.push(sandwiches[i]);
}
int count = 0;
while (queue.size() != 0){
if (queue.peek() == stack.peek()){
queue.poll();
stack.pop();
count = 0;
}else{
int num = queue.poll();
queue.offer(num);
count++;
}
if (count == queue.size()){
break;
}
}
return count;
}
}
优解:
上述方法虽然逻辑能通,但是效率太低了,还是不推荐,我们知道当队列元素跟栈顶元素不匹配时退出,意味着队列内所有元素都与其不匹配,既然题设说了这个栈跟队列只有元素0和1,就是看0跟1匹不匹配的问题,既然如此,那么我们不妨给个大小为2的数组。先将队列里边的0跟1全部放进去,然后在遍历整个放三明治的数组,根据三明治类型即0或者1,出现一个0或1,则0,1数组对应下标减1(相当于上述方法匹配成功),当任何一个减到0则退出循环,最后返回我们定义的0,1数组中0的个数加1的个数即可。
class Solution {
public int countStudents(int[] students, int[] sandwiches) {
int[] cnt = new int[2];
for(int student : students) cnt[student]++;
for(int sandwich : sandwiches){
if (cnt[sandwich] > 0){
cnt[sandwich]--;
}else{
break;
}
}
return cnt[0] + cnt[1];
}
}
第二题:最近的请求次数
👇👇👇
做题链接戳这里,933.最近的请求次数
🍂 题目描述
写一个 RecentCounter 类来计算特定时间范围内最近的请求。
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
保证 每次对 ping 的调用都使用比之前更大的 t 值。
🍂示例
输入:
[“RecentCounter”, “ping”, “ping”, “ping”, “ping”]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]
解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100); // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3
🍂提示
● 1 <= t <= 109
● 保证每次对 ping 调用所使用的 t 值都 严格递增
● 至多调用 ping 方法 104 次
🍃题解
队列
仔细读题后发现这道题挺简单的,因为它每次的t
值都是递增的,意味着我们只需返回开始符合条件的区间之后即可,我们用队列来实现,先将其入队,然后遍历队列,满足区间退出循环,不满足则出队,由于它只求满足条件的t
的个数,即队列最后的大小
class RecentCounter {
Queue<Integer> queue;
public RecentCounter(){
queue = new LinkedList<>();
}
public int ping(int t) {
queue.offer(t);
while(true){
int ret = queue.peek();
if (ret >= t - 3000 && ret <= t){
break;
}
queue.poll();
}
return queue.size();
}
}
数组优解
我们知道如果用队列,它的方法调用是需要时间的,包括出队,入队,以及计算size,我们用数组来模拟队列,即可完美解决这些问题,方法还是跟队列一样,只不过用数组的话得用双指针。
class RecentCounter {
int[] ret;
int left, right;
public RecentCounter() {
ret = new int[10001];
left = 0;
right = 0;
}
public int ping(int t) {
ret[right++] = t;
while (left < right && ret[left] < t - 3000){
left++;
}
return right - left;
}
}
第三题:字符串中的第一个唯一字符
👇👇👇
做题链接戳这里:387.字符串中的第一个唯一字符
🍂 题目描述
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
🍂示例
输入: s = “leetcode”
输出: 0
示例2
输入: s = “loveleetcode”
输出: 2
示例3
输入: s = “aabb”
输出: -1
🍂提示
● 1 <= s.length <= 105
● s 只包含小写字母
🍃题解
常规思路
重复数字,博主第一反应就是想到的HashMap
,因为它在重复数字这一方面的处理实在是太先进了。
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> map;
map = new HashMap<>(26);
char[] chars = s.toCharArray();
for (char ch : chars) {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
for (int i = 0; i < chars.length; i++) {
if (map.get(chars[i]) == 1) {
return i;
}
}
return -1;
}
}
数组模拟哈希
虽然说处理的还可以,但是效率却不尽人意,因为map
所属方法的调用耗费了时间,这道题其实还有一种思路,那就是用数组模拟hash
,这样哈希方法的调用也不需要耗费时间了,题中说:只存在小写字母,那么我们就可以直接用小写字母作为我们新数组的下标,遍历整个字符串,跟每个字符所对应的新数组下标,没遇到一次字符,对应数组值+1,最后只需要确定数组值为1时字符串下标即可,注意charAt()方法的调用需要时间,所以这里我们直接先将字符串转为数组统一操作,能节省不少时间
class Solution {
public int firstUniqChar(String s) {
int[] array = new int[26];
char[] ret = s.toCharArray();
for (int i = 0; i < ret.length; i++) {
array[ret[i] - 97]++;
}
for (int i = 0; i < ret.length; i++) {
if (array[ret[i] - 97] == 1){
return i;
}
}
return -1;
}
}