题目杂记(一)
记录一些暂时没找到出处的题目和解题思路
题目1
题目
给定一个数组arr长度为N,你可以把任意长度大于0且小于N的前缀作为左部分,剩下的 作为右部分。
但是每种划分下都有左部分的最大值和右部分的最大值,请返回最大的, 左部分最大值减去右部分最大值的绝对值。
思路
这道题不能被题目忽悠了,如果按照题目的意思去做,那么做出来的代码,应该是O(N^2)的。所以应该思考题目的真正含义。
无论以那个数作为分界,左边子数组的最大值,不可能小于l(l表示数组最左边的值);同理,右边子数组的最大值,不可能小于r(r表示数组最右边的值)。那么,遍历一遍数组,找到最大值之后,如果l>r,那么把最大值归在左边,返回max-r,就是最大的结果,如果l<r,同理。
遍历一遍数组,找到最大值max,然后返回max{max-l,max-r}。l是数组最左边的元素,r是数组最右边的元素。
代码
public static int maxABS3(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]);
}
题目2
题目
给定一个正整数M,请构造出一个长度为M的数组arr,要求对任意的i、j、k三个位置,如果i<j<k,都有arr[i] + arr[k] != 2*arr[j],返回构造出的arr
思路
这个题的答案肯定是不唯一的,只要能构造出符合条件的就可以。
假设数组中有三个元素a,b,c,满足条件,那么可以得出:
2a,2b,2c这三个数也肯定满足条件;同理2a-1,2b-1,2c-1这三个数也满足。也就是说,它们对应的奇数位置的数满足条件,偶数位置的数也满足条件。
举个例子:
数组[1,5,3]这三个数满足条件,那么这三个数对应的奇数位置的数,也满足条件,即[1,9,5],是满足条件的,1是第1个奇数,9是第5个奇数,5是第三个奇数。当然,2,10,6也是满足条件的。
并且,[1,9,5,2,10,6]这个数组也是满足条件的,因为。如果i,j,k都是从前三个或者后三个中选,那么肯定是满足的。如果i从前三个中选,k从后三个中选,那必然是一个奇数加一个偶数,和是奇数,无论j怎么选,2*arr[j]都不可能是奇数,所以数组整体是满足的。
因此,可以数组可以从小变长。开始时,数组是[1],变成[1,2]。然后扩城[1,3,2,4]。。。。也就是说,给定M,先求M/2的数组,给定M/2,先求M/4的数组。
复杂度公式位T(M) = T(M/2)+O(M),因为要把数组拼起来,所以要O(M)。根据Master公式,时间复杂度位O(M),或者说O(N)。
代码
// 生成长度为size的达标数组
// 达标:对于任意的 i<k<j,满足 [i] + [j] != [k] * 2
public static int[] makeNo(int size) {
if (size == 1) {
return new int[] { 1 };
}
// size
// 一半长达标来
// 7 : 4
// 8 : 4
// [4个奇数] [3个偶]
int halfSize = (size + 1) / 2;
int[] base = makeNo(halfSize);
// base -> 等长奇数达标来
// base -> 等长偶数达标来
int[] ans = new int[size];
int index = 0;
for(; index < halfSize;index++) {
ans[index] = base[index] * 2 + 1;
}
for(int i = 0 ;index < size;index++,i++) {
ans[index] = base[i] * 2;
}
return ans;
}
题目3
题目
有一些排成一行的正方形。每个正方形已经被染成红色或者绿色。现在可以选择任意一个正方形然后用这两种颜色的任意一种进行染色,这个正方形的颜色将 会被覆盖。目标是在完成染色之后,每个红色R都比每个绿色G距离最左侧近。 返回最少需要涂染几个正方形。
如样例所示: s = RGRGR 我们涂染之后变成RRRGG满足要求了,涂染的个数为2,没有比这个更好的涂染方案。
思路
这道题的意思是最终的结果,R都在左边,G都在右边。当然,也可以都涂成R或者G,但是要在满足条件下求最小的改动代价。如果用普通思路,那么O(N^2)是可以解决的。
进阶的话,可以用O(N)来解决。
需要使用额外的数组B[],B[i]表示第i个元素后面有几个R(包括自身)。对于s = RGRGR来说,B=[3,2,2,1,1]。
定义最小代价min_loss,先定义一个变量sum,表示目前的G的数量,遍历字符串,每次遍历字符i时,如果i==G,则sum++。min_loss = min(min_loss,sum+B[i+1]),也就是把从0到i位置都变成R,后面的都变成G,所需要的代码。每次都更新min_loss
遍历一遍,就可以得到最终的结果
代码
public static int minPaint(String s) {
if (s == null || s.length() < 2) {
return 0;
}
char[] chs = s.toCharArray();
int[] right = new int[chs.length];
right[chs.length - 1] = chs[chs.length - 1] == 'R' ? 1 : 0;
for (int i = chs.length - 2; i >= 0; i--) {
right[i] = right[i + 1] + (chs[i] == 'R' ? 1 : 0);
}
int res = right[0];
int left = 0;
for (int i = 0; i < chs.length - 1; i++) {
left += chs[i] == 'G' ? 1 : 0;
res = Math.min(res, left + right[i + 1]);
}
res = Math.min(res, left + (chs[chs.length - 1] == 'G' ? 1 : 0));
return res;
}
题目4
题目
括号有效配对是指:
1)任何一个左括号都能找到和其正确配对的右括号
2)任何一个右括号都能找到和其正确配对的左括号
有效的: (()) ()() (()()) 等
无效的: (() )( 等
问题一:怎么判断一个括号字符串有效?
问题二:如果一个括号字符串无效,返回至少填几个字符能让其整体有效
思路
问题1:
很简单的一道题,传统的方法是用栈,也可以不用栈,只使用一个计算变量count。每次遇到"(",count++;每次遇到")",count–;如果中途count==-1,则说明不匹配。如果最后count不等于0,也不匹配。否则,匹配。
问题2
定义一个sum,表示最少填几个。一个计算变量count。每次遇到"(",count++;每次遇到")",count–;如果中途count==-1,则sum++,把count置为0。如果最后count不等于0,则sum = sum +count,返回sum。
代码
问题1省略了
问题2
public static int needParentheses(String str) {
int t = 0;
int needSolveRight = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
t++;
} else { // 遇到的是')'
if (t == 0) {
needSolveRight++;
} else {
t--;
}
}
}
return t + needSolveRight;
}