关于JAVA的学习出了看视频以外,那就是刷题了,朋友们,你们有没有过这样的感觉,在网上看了视频过后感觉自己什么都听懂了,但就是写题和做项目时无从下手,或者就是因为某个细节一直错一直改,那背后的原因是什么呢?四个字——题刷少了,这里新一建议去
Leetcode
看看,那里的题库资源很丰富,并且在全球都有广泛涉猎。不仅如此,这里还有 课程 + 刷题 + 面经 + 求职 + 讨论区分享解题思路,用过的人都说好
除此之外,我的建议是初学者从简单题
开始练习,因为简单题是一切题的基础,一切的困难题都是从简单题衍生而来的,每天刷那么2~3题,后期再慢慢刷中等题,困难题,经过一段时间后会有很不错的提升
此外,在我们有一定的提升之后,我们便可以去刷剑指offer
了,在这里预祝各位大学生以及初学者都拿到自己满意的offer!
做题链接戳这里:20.有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
输入:s = “()”
输出:true
示例2
输入:s = “()[]{}”
输出:true
示例3
输入:s = “(]”
输出:false
● 1 <= s.length <= 104
● s 仅由括号 ‘()[]{}’ 组成
我们来看看,括号匹配问题是面试高频问题,我们不仅要考虑括号的数量
是否匹配还要考虑括号的类型
是否匹配,这就造成了一定的难度,既然要用到栈,那么怎么用即为关键了,将它全部压栈?然后再从后往前遍历?——错!如果是这种情况呢?({)},数量的确匹配了,但不对应也是不行,既然不能全部压栈,那么我们部分压栈呢?——遇到左括号压栈,右括号弹出判断是否匹配,为什么能那样呢,因为只要是匹配的那么每个左括号以及匹配的右括号之间的距离一定是偶数,于是我们便有了如下思路:
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') stack.push('(');
else if(c == '[') stack.push('[');
else if (c == '{') stack.push('{');
else if (!stack.empty()) {
char d = stack.peek();
if (c == ')' && d == '(' || c == ']' && d == '[' || c == '}' && d == '{'){
stack.pop();
}else{
break;
}
}else{
return false;
}
}
return stack.empty();
}
}
这样写虽然时间效率挺高,但是不是感觉代码挺冗杂的,有没有两全其美的办法呢?我们要不反其道而行之,遇到左括号我们依然压栈,但不是压它,而是压它与之对应的右括号,这样遇到右括号只需判断弹出与其是否相等即可
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c:s.toCharArray()
) {
if (c == '(') stack.push(')');
else if (c == '[') stack.push(']');
else if (c == '{') stack.push('}');
else if (stack.empty() || c != stack.pop()) return false;
}
return stack.empty();
}
}
做题链接戳这里:155.最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
示例2
输入:s = “()[]{}”
输出:true
示例3
输入:s = “(]”
输出:false
● -231 <= val <= 231 - 1
● pop、top 和 getMin 操作总是在 非空栈 上调用
● push, pop, top, and getMin最多被调用 3 * 104 次
这题的难点在哪?push?pop? 当然不是,这题难就难在getMin,常数时间内的getMin,这getMin都相当于O(1)时间复杂度的压栈,弹出了,我们就想想用一个栈来实现做得到吗,排好序再入栈?我们再看看题,如果可以这样,那么示例的top方法是不是应该返回-2,因为0是最大的,排好序后当然应该在栈底,故我们不能排序,我们不妨想想如果我们将最小元素每次都存在另一个栈里是不是就OK了,
我们定义两个栈,一个普通stack,一个最小栈getMin,入栈时先判断其是否为空,如果为空,先压一个进去,后续不为空压栈时即比较一下最小栈栈顶元素与压栈元素,如果压栈元素小,那么将其压入最小栈,否则只压普通栈,这样每次getMin只需要pop最小栈即可,需要注意的是这里一定要取等号,不然重复元素压栈时会处理错误。
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minstack;
public MinStack() {
stack = new Stack<>();
minstack = new Stack<>();
}
public void push(int val) {
stack.push((val));
if (!minstack.empty()){//判空
int top = minstack.peek();
if (top >= val){//取等
minstack.push(val);
}
}else{
minstack.push(val);
}
}
public void pop() {
int popVal = stack.pop();
if (!minstack.empty()){
if (popVal == minstack.peek()){
minstack.pop();//为了下一次获取最小值,相等时最小栈必须也弹出
}
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minstack.peek();
}
}
做题链接戳这里:496.下一个更大元素I
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例2
输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。
● 1 <= nums1.length <= nums2.length <= 1000
● 0 <= nums1[i], nums2[i] <= 104
● nums1和nums2中所有整数 互不相同
● nums1 中的所有整数同样出现在 nums2 中
进阶:你可以设计一个时间复杂度为 O(nums1.length + nums2.length) 的解决方案吗?
暴力解法
我们很容易想到的就是嵌套遍历两个数组,如果找到与第一个数组元素相等的将其压入栈,而后再往后遍历看是否有元素大于他,大于则退出循环,没有则返回-1;
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] ret = new int[nums1.length];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < nums1.length; i++) {
int j = 0;
for (j = 0; j < nums2.length; j++) {
if (nums1[i] == nums2[j]){
stack.push(nums1[i]);
}else if (!stack.empty() && nums2[j] > stack.peek()){
stack.pop();
ret[i] = nums2[j];
break;
}
}
if (j == nums2.length){
ret[i] = -1;
if (!stack.empty()){
stack.pop();
}
}
}
return ret;
}
}
没错,它来了,它来了,它带着HashMap走来了
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Stack<Integer> stack = new Stack<Integer>();
HashMap<Integer, Integer> hasMap = new HashMap<Integer, Integer>();
int[] result = new int[nums1.length];
for(int num : nums2) {
while(!stack.isEmpty() && stack.peek()<num){
hasMap.put(stack.pop(), num);
}
stack.push(num);
}
for(int i = 0; i < nums1.length; i++) result[i] = hasMap.getOrDefault(nums1[i], -1);
return result;
}
}