刷题汇总
目录:
1、容器储水问题
2、给定一个数组arr,返回所有子数组的累加和中,最大的累加和
3、生成窗口最大值数组
4、给定两个字符串str1和str2,判断str2是不是由str1循环右移得到的
5、给定一个字符串str,和一个整数k,返回str向右循环右移k位后的结果
6、给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。
7、会议室宣讲问题
8、最大的leftMax与rightMax之差的绝对值
【题目】
代表0位置是一个宽度为1,高度为3的直方图。
代表1位置是一个宽度为1,高度为1的直方图。
代表2位置是一个宽度为1,高度为2的直方图。
代表3位置是一个宽度为1,高度为4的直方图。
所有直方图的底部都在一条水平线上,且紧靠着。
把这个图想象成一个容器,这个容器可以装3格的水。
给定一个没有负数的数组arr,返回能装几格水?
【思路】
不能把求解方向放在计算形成多少个波峰与波谷之间,如果当两边界都很高时该方案会失效。
方向就放在单看每个位置上面能够留下多少水上。对一个位置,看其左右两边所有位置的最大值,得到max左和max右,利用较小的max减去当前位置的高度,差值就是能够留下的水量,差值为负数则不能够留下水。
时间复杂度O(N),空间复杂度O(N)的方法:
查询数据比较麻烦的时候可以往预处理数组上引。当有一个暴力解的时候,想想暴力点的目的是什么,是否我们能够有一些方法可以提前生成暴力点所需要的数据,以便不需要暴力求解。比方说上题,需要知道当前i位置的max左和max右,就需要遍历0~i-1位置和i+1~N-1位置,找出两个max值,每个位置都需要如此,则代价是N²。我们提前遍历数据,生成0-~i-1位置的max左值,存进辅助数组1,生成i~N-1的max右值,存进辅助数据2,等下需要暴力求max的时候,直接根据当前i位置,在辅助数组中进行max的取值即可避免暴力,以一定的空间复杂度换取时间复杂度的降低。
最优解:
时间复杂度O(N),空间复杂度O(1)的方法:
准备左指针和右指针,左指针指向1位置,右指针指向N-2位置,max左记录左指针之前的最大值,max右记录右指针之后的最大值。初始max左是0位置,初始max右是N-1位置。对比max左右,对较小一方,使用其max减去指针指向数值,当为负数时证明当前数值比max大,则更新max,否则累加装水量。完毕之后重复对比max,继续选择较小的一边执行上述操作。
【Code】
public static int getWater(int[] arr)
{
if(arr==null || arr.length<3)
return -1;
int value = 0;
int leftMax = arr[0];
int rightMax = arr[arr.length-1];
int l = 1;
int r = arr[arr.length-2];
while(l<=r)
{
if(leftMax<=rightMax)
{
value+=Math.max(0, leftMax-arr[l]);
leftMax = Math.max(leftMax, arr[l++]);
}
else {
value+=Math.max(0, rightMax-arr[r]);
rightMax = Math.max(rightMax, arr[r--]);
}
}
return value;
}
【题目】
【最优解】时间复杂度o(n),空间复杂度o(1)
设置一个当前指针cur,变量max记录最大累加和。cur从0位置出发,累加上i位置的数,如果比max大,则更新max,如果累加后cur变成负数,则将cur重置为0,继续对下个位置进行累加,但不重置max
【证明】
对一个数组,找到其最大最长子数组,记为arr,arr的前缀部分累加和一定为负数,因为如果arr的某段前缀如果为0或者正数,这一段一定会被加进arr中,但目前事实是arr并不包含该段前缀,则证明该部分的累加和一定为负数。且要求最大的累加和的话,要从0开始对arr的第一个位置进行累加。这就是为什么最优解当cur加完某个位置变成负数时要将cur重置为0的原因。
同理反推,在arr数组当中的某一段前缀累加和必定为正数,如果不为正数,则应该抛弃这段前缀才对,但事实是这一段前缀被包含进了最大最长子数组arr中,所以必定为正数。最优解的这个流程不会错过最大的累加和的这个答案。
【Code】
public static int maxValue(int[] arr)
{
if(arr==null||arr.length==0)
return 0;
int max = Integer.MIN_VALUE;
int cur = 0;
for(int i = 0;i<arr.length-1;i++)
{
cur+=arr[i];
max = Math.max(cur, max);
cur = cur<0?0:cur;
}
return max;
}
生成窗口最大值数组
【题目】
【思路】
1、明确窗口内最大/小值的更新结构
2、使用双端队列(可以从头进头出,尾进尾出):队列里从头到尾数据是严格从大到小的。窗口内的最大值就是头部元素。
1)加数规则:
默认先从尾进,之后对每个元素,对比队列内的数据,如果尾有元素比即将入队的元素小,则将其出队,直到队尾的元素比即将入队的元素大或者出空为止(相等也出队),然后再将新元素入队(即保证队列内从头到尾都是从大到小的顺序)
2)减数规则:
对左边界L,往右移动,对比移动后的新位置下标与队列中队头元素的下标,观察是否过期,如过期则出队头元素,如无则不动。
时间复杂度O(N),空间复杂度O(1):
具体解法:对这个窗口,右边加一个,左边减一个,然后使用一个数组记录当前队头元素,当右边走到超过数组末尾时返回。
注意,不保证每个更新的时候都是O(1),存在某个时刻单次可能是O(N),但平均下来是O(1)。
因为每个元素只可能入队和出队一次。
【Code】
public static int[] getMaxWindownums(int[] arr,int w)
{
if(arr==null||w<1||arr.length<w)
return null;
//w为窗口大小
LinkedList<Integer> winMax = new LinkedList<>();
int[] res = new int[arr.length-w+1];//窗口滑动到末会产生的最大值数量
int index = 0;
for(int i = 0;i<arr.length;i++)
{
//对比当前arr[i]与双向链表队尾元素,如大则持续出队列
while(!winMax.isEmpty() && arr[winMax.peekLast()] <= arr[i] )
winMax.pollLast();
winMax.add(i);//进队
//如果队头位置过期,则将其出队
if(winMax.peekFirst()==i-w)
winMax.pollFirst();
if(i>=w-1)
res[index++] = arr[winMax.peekFirst()];
}
return res;
}
【题目】
比如”abc”,
向右循环右移一位,得到”cab”,
向右循环右移两位,得到“bca”,
向右循环右移三位,得到“abc”,
给定两个字符串str1和str2,判断str2是不是由str1循环右移得到的
【思路】
首先判断str1和str2长度是否相等,如不等则一定不是,如是则判断str2是否在str1+str1中,是则是。
如何判断str2是否在str1+str1中?使用KMP算法。
【Code】
public static boolean isright(String str1,String str2)
{
if(str1.length()!=str2.length()||str1==null||str2==null)
return false;
String str = str1+str1;
return str.contains(str2);//或手动实现kmp
}
【题目】
给定一个字符串str,和一个整数k,返回str向右循环右移k位后的结果
【思路】
将字符串在k位置分割成左右两半,先将左右分别逆序,再整体逆序即得到。
底层问题:如何将一个字符数组逆序(不用额外空间):对两端位置进行互换,然后往中间方向移动
【Code】
public static void reverse(char[] arr,int star,int end)
{
char temp = 0;
while(star<end)
{
temp = arr[star];
arr[star] = arr[end];
arr[end] = temp;
star++;end--;
}
}
public static String moveK(String str,int k)
{
//先按k划分str。进行左右分别逆序,再整体进行逆序
char[] arr = str.toCharArray();
reverse(arr, 0, k-1);
reverse(arr, k, arr.length-1);
//整体逆序
reverse(arr, 0, arr.length-1);
return arr.toString();
}
贪心策略:
【题目】
给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。
【思路】
1、字典序:
1)当长度相等时,从左第一个元素开始往右对比字符的阿斯克码大小。
2)当长度不等时,将长度较小的串末尾补0至长度相等再进行比较。
2、贪心策略:
(str1,str2)两个字符串,如果字典序str1+str2<=str2+str1,则返回str1,排序位置将str1放在str2前面
3、不具备传递性的排序策略一定是错的。
4、首先需要证明提出的排序策略是有传递性的。
【Code】
public static String lowestString(String[] strs)
{
if(strs==null || strs.length == 0)
return "";
Arrays.sort(strs,new strComparator());
String reS = "";
for(int i = 0;i<strs.length-1;i++)
{
reS+=strs[i];
}
return reS;
}
//比较规则
public static class strComparator implements Comparator<String>
{
public int compare(String a,String b)
{
return (a+b).compareTo(b+a);
}
}
【题目】
一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次。
public static class Program { public int start; public int end; public Program(int start, int end) { this.start = start; this.end = end; } } public static class ProgramComparator implements Comparator<Program> { @Override public int compare(Program o1, Program o2) { return o1.end - o2.end; } } public static int bestArrange(Program[] programs, int start) { Arrays.sort(programs, new ProgramComparator()); int result = 0; for (int i = 0; i < programs.length; i++) { if (start <= programs[i].start) { result++; start = programs[i].end; } } return result; }
【题目】
给定一个长度为N(N>1)的整型数组arr,可以划分成左右两个部分,左部分为arr[0..K],右部分为arr[K+1..N-1],K可以取值的范围是[0,N-2]。求这么多划分方案中,左部分中的最大值减去右部分最大值的绝对值中,最大是多少?例如:[2,7,3,1,1],当左部分为[2,7],右部分为[3,1,1]时,左部分中的最大值减去右部分最大值的绝对值为4。当左部分为[2,7,3],右部分为[1,1]时,左部分中的最大值减去右部分最大值的绝对值为6。还有很多划分方案,但最终返回6。
public static int maxABS(int[] arr) { int max = Integer.MIN_VALUE; for (int i = 0; i < arr.length; i++) { max = Math.max(arr[i], max); } return max - Math.min(arr[0], arr[arr.length - 1]); }