先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
#因为字符只包含括号,所以只有偶数时才有可能匹配上
if len(s) % 2 == 1:
return False
dict = {
“)”: “(”,
“]”: “[”,
“}”: “{”,
}
stack = list()
for ch in s:
#代表遍历到右括号
if ch in dict:
#看栈顶元素是否能匹配上,如果没有匹配上,返回false
if not stack or stack[-1] != dict[ch]:
return False
#如果匹配上,弹出栈顶元素
stack.pop()
else:
#匹配到左括号,入栈
stack.append(ch)
#栈为空,代表s是有效串,否则是无效串
return not stack
4包含min函数的栈
==========
剑指 Offer 30. 包含min函数的栈
问题描述
====
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数,并且调用 min函数、push函数 及 pop函数 的时间复杂度都是 O(1)。
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素
示例:
minStack.push(-2); -->将-2入栈
minStack.push(0); -->将0入栈
minStack.push(-3); -->将-3入栈
minStack.min(); -->返回栈中最小元素-3
minStack.pop(); -->弹出栈顶元素-3
minStack.top(); -->返回栈顶元素0
minStack.min(); -->返回栈中最小元素-2
分析问题
====
对于普通的栈来说,执行push和pop的时间复杂度是O(1),而执行min函数的时间复杂度是O(N),因为要想找到最小值,就需要遍历整个栈,为了降低min函数的时间复杂度,我们引入了一个辅助栈。
-
数据栈A:栈A用来存储所有的元素,保证入栈push、出栈pop、获取栈顶元素top的操作。
-
辅助栈B:栈B用来存储栈A中所有非严格递减的元素,即栈A中的最小值始终在栈B的栈顶,这样可以保证以O(1)的时间复杂度来返回栈中的最小值。
下面我们来看一下代码实现。
class Solution:
def init(self):
#数据栈
self.A = []
#辅助栈
self.B = []
#push操作
def push(self, x):
self.A.append(x)
#如果辅助栈B为空,或者栈顶元素大于x,则入栈
if not self.B or self.B[-1] >= x:
self.B.append(x)
def pop(self):
#弹出数据栈A中的元素
s = self.A.pop()
#如果弹出的元素和栈B的栈顶元素相同,则为了保持一致性
#将栈B的栈顶元素弹出
if s == self.B[-1]:
self.B.pop()
def top(self):
#返回数据栈A中的栈顶元素
return self.A[-1]
def min(self):
#返回辅助栈B中的栈顶元素
return self.B[-1]
5表达式求值
======
问题描述
====
请写一个整数计算器,支持加减乘三种运算和括号。
示例:
输入:“(2 * (3 - 4)))* 5”
返回值:-10
分析问题
====
因为只支持加、减、乘、括号,所以我们根据优先级可以分为3类,即括号>乘>加、减,假设先把括号去掉,那么就剩下乘和加减运算,根据运算规则,我们需要先计算乘、再计算加、减,因此我们可以这么来考虑,我们先进行乘法运算,并将这些乘法运算后的整数值返回原表达式的相应位置,则随后整个表达式的值,就等于一系列整数加减后的值。而对于被括号分割的表达式,我们可以递归的去求解,具体算法如下。
遍历字符串s,并用变量preSign记录每个数字之前的运算符,初始化为加号。
-
遇到空格时跳过。
-
遇到数字时,继续遍历求出这个完整的数字的值,保存到num中。
-
遇到左括号时,需要递归的求出这个括号内的表达式的值。
-
遇到运算符或者表达式的末尾时,就根据上一个运算符的类型来决定计算方式。
-
如果是加号,不需要进行计算,直接push到栈里
-
如果是减号,就去当前数的相反数,push到栈里
-
如果是乘号,就需要从栈内pop出一个数和当前数求乘法,再把计算结果push到栈中
-
最后把栈中的结果求和即可。
下面我们来看一下代码实现。
class Solution:
def calculate(self, s):
n = len(s)
#存取部分数据和
stack = []
preSign = ‘+’
num = 0
i=0
while i<n:
c=s[i]
if c==’ ':
i=i+1
continue
if c.isdigit():
num = num * 10 + ord© - ord(‘0’)
#如果遇到左括号,递归求出括号内表达式的值
if c==‘(’:
j=i+1
counts=1
#截取出括号表达式的值
while counts>0:
if s[j]==“(”:
counts=counts+1
if s[j]==“)”:
counts=counts-1
j=j+1
#剥去一层括号,求括号内表达式的值
num=self.calculate(s[i+1:j-1])
i=j-1
if not c.isdigit() or i==n-1:
if preSign==“+”:
stack.append(num)
elif preSign==“-”:
stack.append(-1*num)
elif preSign==“*”:
tmp=stack.pop()
stack.append(tmp*num)
num=0
preSign=c
i=i+1
return sum(stack)
s=Solution()
print(s.calculate(“(3+4)*(5+(2-3))”))
6滑动窗口的最大值
=========
LeetCode 239. 滑动窗口最大值
问题描述
====
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
示例:
输入:[2,3,4,2,6,2,5,1],3
输出:[4,4,6,6,6,5]
分析问题
====
这道题的关键点在于求滑动窗口中的最大值。大小为k的滑动窗口,我们可以通过遍历的方式来求出其中的最大值,需要O(k)的时间复杂度。对于大小为n的数组nums,一共有n-k+1个窗口,因此该算法的时间复杂度是O(nk)。
通过观察,我们可以知道,对于两个相邻的滑动窗口,有k-1个元素是共用的,只有一个元素是变化的,因此我们可以利用此性质来优化我们的算法。
对于求最大值问题,我们可以使用优先级队列(大顶推)来求解。首先,我们将数组的前k个元素放入优先级队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入队列中,此时堆顶元素就是堆中所有元素的最大值,然而这个最大值有可能不属于当前的滑动窗口中,我们需要将该元素进行移除处理(如果最大值不在当前滑动窗口中,它只能在滑动窗口的左边界的左侧,所以滑动窗口向右移动的过程中,该元素再也不会出现在滑动窗口中了,所以我们可以对其进行移除处理)。我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。
为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素num在数组中的下标为index。
小trick:因为python中只提供了小顶堆,所以我们需要对元素进行取反处理,例如对于列表[1, -3],我们对元素进行取反,然后插入小顶堆中,此时堆中是这样的[-1,3],我们取出堆顶元素-1,然后取反为1,正好可以得到列表中的最大值1。
我们nums=[2,3,4,2,6,2,5,1],k=3为例,来看一下具体的过程。
-
首先,我们将nums的前3个元素放入优先级队列中,队首元素下标值index=2>0,在窗口中,所以加入结果中,此时res=[4]。
-
下一个元素2入队,此时队首元素下标index=2>1,在窗口中,所以加入结果中,此时res=[4,4]。
-
下一个元素6入队,此时队首元素下标index=4>2,在窗口中,所以加入结果中,此时res=[4,4,6]。
-
下一个元素2入队,此时队首元素下标index=4>3,在窗口中,所以加入结果中,此时res=[4,4,6,6]。
-
下一个元素5入队,此时队首元素下标index=4=4,在窗口中,所以加入结果中,此时res=[4,4,6,6,6]。
-
下一个元素1队列,此时队首元素下标index=4<5,不在窗口中,所以我们将其弹出,此时队首元素的下标变为6,在窗口中,所以加入结果中,此时res=[4,4,6,6,6,5]。
进阶
==
这道题我们也可以使用双端队列来求解。我们在遍历数组的过程中,不断的对元素对应的下标进行出队入队操作,在出入队的过程中,我们需要保证队列中存储的下标对应的元素是从大到小排序的。具体来说,当有一个新的元素对应的下标需要入队时,如果该元素比队尾对应的元素的值大,我们需要弹出队尾,然后循环往复,直到队列为空或者新的元素小于队尾对应的元素。
由于队列中下标对应的元素是严格单调递减的,因此队首下标对应的元素就是滑动窗口中的最大值。但是此时的最大值可能在滑动窗口左边界的左侧,并且随着窗口向右移动,它永远不可能出现在滑动窗口中了。因此我们还需要不断从队首弹出元素,直到队首元素在窗口中为止。
我们还是以nums=[2,3,4,2,6,2,5,1],k=3为例,来看一下具体的过程。我们首先初始化一个空队列que。
-
此时队列为que空,元素2对应的下标0入队。并且此时未形成窗口,不取值。
-
此时队列que=[0],队尾元素为0,它对应数组中的元素是nums[0] < nums[1]的,所以我们把队尾0弹出,此时队列为空,我们将1入队。并且此时未形成窗口,不取值。
-
此时队列que=[1],队尾元素为1,它对应的数组中的元素是nums[1] < nums[2]的,所以我们把队尾1弹出,此时队列为空,我们将2入队。并且此时队首元素2在窗口[0,2]中,所以取出队首元素。
-
此时队列que=[2],队尾元素为2,它对应的数组中的元素是nums[2] > nums[3]的,所以我们将3入队。并且此时队首元素2在窗口[1,3]中,所以取出队首元素。
-
此时队列que=[2,3],队尾元素为3,它对应的数组中的元素是nums[3] < nums[4]的,所以我们把队尾3弹出,并且此时队尾元素对应的数组中的元素是nums[2] < nums[4],所以我们把队尾2弹出,此时队列为空,我们将4入队。并且此时队首元素4在窗口[2,4]中,所以取出队首元素。
-
此时队列que=[4],队尾元素为4,它对应的数组中的元素是nums[4] > nums[5]的,所以我们将5入队。并且此时队首元素4在窗口[3,5]中,所以我们取出队首元素。
-
此时队列que=[4,5],队尾元素为5,它对应的数组中的元素是nums[5] < nums[6]的,所以我们把队尾5弹出,此时队尾元素对应的数组中的元素时nums[4] > nums[6] ,所以我们将6入队。并且此时队首元素4在窗口[4,6]中,所以我们取出队首元素。
-
此时队列que=[4,6],队尾元素为6,它对应的数组中的元素是nums[6] > nums[7]的,所以我们将7入队。而此时队首元素4不在窗口[5,7]中,所以我们将其移除队列,此时队首元素6在窗口[5,7]中,所以我们将其取出。
下面我们来看一下代码实现。
import collections
class Solution:
def maxSlidingWindow(self, nums, k):
n = len(nums)
#申请一个双端队列
q = collections.deque()
#初始化第一个窗口
for i in range(k):
#如果队列不为空且比队尾元素大,将队尾出队
while q and nums[i] >= nums[q[-1]]:
q.pop()
#直到队列为空,或者比队尾元素小,入队
q.append(i)
#将队首元素加入结果中
ans = [nums[q[0]]]
#窗口逐步向右移动
for i in range(k, n):
#如果队列不为空且比队尾元素大,将队尾出队
while q and nums[i] >= nums[q[-1]]:
q.pop()
#直到队列为空,或者比队尾元素小,入队
q.append(i)
#如果队首元素不在该窗口内,出队操作
while q[0] <= i - k:
q.popleft()
#将队首元素加入结果中
ans.append(nums[q[0]])
return ans
s=Solution()
print(s.maxSlidingWindow([2,3,4,2,6,2,5,1],3))
7栈和排序
=====
问题描述
====
给你一个由1~n,n个数字组成的一个排列和一个栈,要求按照排列的顺序入栈。如何在不打乱入栈顺序的情况下,仅利用入栈和出栈两种操作,输出字典序最大的出栈序列。
排列:指 1 到 n 每个数字出现且仅出现一次。
示例:
输入:[2,4,5,3,1]
输出:[5,4,3,2,1]
分析问题
====
由于我们只能使用出栈和入栈两种操作,要想使得出栈序列字典序最大,首先想到的就是令高位尽可能地大,我们出栈的时机就是:当前入栈元素若是大于之后将要入栈的元素,那么就将其出栈。当元素出栈后,还需要判断栈顶元素与之后将要入栈元素之间的大小关系,如果此时栈顶元素大于之后将要入栈的元素,那么就将其出栈,不断判断直到栈为空或条件不满足。
为了快速判断“当前入栈元素是否大于之后将要入栈的元素”,我们需要创建一个辅助数组temp,其中temp[i]表示i之后的最大元素。借助辅助数组,我们可以以O(1)的时间复杂度去判断当前入栈元素是否大于之后将要入栈的元素。
下面我们来看一下代码的实现。
import sys
class Solution:
def solve(self , a):
n=len(a)
res=[]
if n==0:
return res
stack=[]
temp=[0]*n
temp[n-1]=-sys.maxsize-1
#从右往左遍历数组a,然后取填充temp
#使得temp[i]表示i之后的最大元素
for i in range(n-2,-1,-1):
temp[i]=max(a[i+1],temp[i+1])
#遍历数组a
for i in range(0,n):
if a[i] > temp[i]: #若当前元素大于之后将要入栈的元素,将其加入结果中
res.append(a[i])
若栈不为空,且栈顶元素大于temp[i],
栈顶出栈,加入结果中
while stack and stack[-1] > temp[i]:
res.append(stack[-1])
stack.pop()
else:
stack.append(a[i])
while stack:
res.append(stack[-1])
stack.pop()
return res
该算法的时间复杂度是O(n),空间复杂度也是O(n)。
8单调栈
====
问题描述
====
给定一个长度为 n 的可能含有重复值的数组 arr ,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。请设计算法,返回一个二维数组,表示所有位置相应的信息。位置信息包括:两个数字 l 和 r,如果不存在,则值为 -1,下标从 0 开始。
示例:
输入:[3,4,1,5,6,2,7]
输出:[[-1,2],[0,2],[-1,-1],[2,5],[3,5],[2,-1],[5,-1]]
分析问题
====
这道题最简单的解法就是暴力求解,即通过两层for循环来求解。如下所示:
class Solution:
def foundMonotoneStack(self , nums):
n=len(nums)
res=[]
#遍历一遍数组
for i in range(0,n):
l=-1
r=-1
#从左往右寻找l,寻找比nums[i]小的最近的nums[l]
for j in range(0,i):
if nums[j] < nums[i]:
l=j
#从右往左寻找l,寻找比nums[i]小的最近的nums[r]
for j in range(n-1,i,-1):
if nums[j] < nums[i]:
r=j
res.append([l,r])
return res
该算法的时间复杂度是O(n^2),空间复杂度是O(1)。
显然暴力求解的时间复杂度太高,那我们该如何优化呢?其实这道题我们可以使用单调栈来求解,首先我们来看一下单调栈的定义。
单调栈是指栈内元素是具有有单调性的栈,和普通栈相比,单调栈在入栈的时候,需要将待入栈的元素和栈顶元素进行对比,看待加入栈的元素入栈后是否会破坏栈的单调性,如果不会,直接入栈,否则一直弹出到满足条件为止。
本题中,我们维护一个存储数组下标的单调栈。然后遍历数组,执行如下操作。
我们以求每一个i左边离i最近且小于arr[i]的位置为例,来看一下算法的执行流程。首先我们从左往右遍历数组。
-
假设遍历到的元素是 arr[i],栈顶元素 top 对应的数组中的元素是 arr[top],然后我们拿 arr[i] 和 arr[top] 进行对比。
-
如果 arr[top] > arr[i],就说明 top 不是第 i 个元素的解,也不会是 i 以后任何元素的解(因为 i 比 top 距离后面的数更近,同时arr[i] < arr[top]),所以我们就把top弹出,直到栈为空或者栈顶元素(数组的下标)对应数组中的元素小于 arr[i]。
-
如果arr[top] < arr[i],就说明第 i 个数的解就是 top,因为栈内的元素都是单调递增的,所以 top 是离 i 最近的数,即 top 就是所求值。然后因为 i 可能是 i 右侧的候选解,所以把 i 加入栈中。
同理,我们从右往左遍历数组,就可以得到每一个i右边离i最近且小于arr[i]的位置。
下面我们来看一下代码的实现。
class Solution:
def foundMonotoneStack(self , nums):
n=len(nums)
res=[]
l=[0]*n
r=[0]*n
#单调栈
stack=[]
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
最常见Java面试题解析(2021最新版)
2021企业Java面试题精选
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
mg-blog.csdnimg.cn/img_convert/f4bf211df9bf50247054e8a7bdf3d809.png)
同理,我们从右往左遍历数组,就可以得到每一个i右边离i最近且小于arr[i]的位置。
下面我们来看一下代码的实现。
class Solution:
def foundMonotoneStack(self , nums):
n=len(nums)
res=[]
l=[0]*n
r=[0]*n
#单调栈
stack=[]
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
[外链图片转存中…(img-6lsSw5Q8-1713368621109)]
[外链图片转存中…(img-LbuO2Pzl-1713368621109)]
最常见Java面试题解析(2021最新版)
[外链图片转存中…(img-A8nQHaZG-1713368621110)]
[外链图片转存中…(img-omeI2qPK-1713368621110)]
2021企业Java面试题精选
[外链图片转存中…(img-mgSsjch3-1713368621110)]
[外链图片转存中…(img-OgI3M3ga-1713368621111)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-vXtfIy02-1713368621111)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!