柠檬水找零
链接: 860.柠檬水找零
思路
本题思路非常简单
主要是抓住问题的本质,没有抓住问题的本质,很容易被一些无关紧要的细节困住。
认真审题,情况也不多,把各种情况列一下呗。
代码
自己的代码,if结构太多,冗长,容易出错
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
int twenty = 0;
for (int i = 0;i < bills.length;i++){
if (bills[i] == 5)
five++;
else if (bills[i] == 10){
if (five > 0){
five--;
ten++;
}else{
return false;
}
}
else if (bills[i] == 20){
if (ten > 0 && five > 0){
ten--;
five--;
}else if(ten == 0 && five > 2){
five = five - 3;
}
else return false;
}
}
return true;
}
}
官方代码
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
five++;
} else if (bills[i] == 10) {
five--;
ten++;
} else if (bills[i] == 20) {
if (ten > 0) {
ten--;
five--;
} else {
five -= 3;
}
}
if (five < 0 || ten < 0) return false;
}
return true;
}
}
在前面的判断中,只考虑有的情况,没有的情况正常操作;最后再判断按正常操作下的ten和five是否为负数,有负数说明我们在不符合条件的情况下操作了,所以报错。
根据身高重建队列
链接: 406.根据身高重建队列
思路
本题的思路很简单,主要是二维数组看起来会比较复杂
但熟悉java语言的语法,本题会比较简单
思路就是按身高从大到小排列,身高相同的按k值,小的在前
然后再调整顺序
在java语言中,身高排序是用排序算法(自己会经常遗忘)
调整顺序用的是按k值插入
代码
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,(a,b) -> {
if (a[0] == b[0]) return a[1] - b[1];
return b[0] - a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for (int[] p : people){
que.add(p[1],p);
}
return que.toArray(new int[people.length][]);
}
}
每一步自己都不太熟悉,要记住本题。
PS:定义队列时ArrayList也可以
用最少数量的箭引爆气球
链接: 452. 用最少数量的箭引爆气球
思路
思路很简单,但自己容易想太多
先按左区间排序,排完序后只要前一元素的左区间小于后一元素的右区间,则不重合,计数。
若重合,则进行之后的比较,在比较之前,我们应决定重合后区间他们的最小右区间。
然后这个右区间是新的比较中的左区间
代码
class Solution {
public int findMinArrowShots(int[][] points) {
// Arrays.sort(points,(a,b) -> {
// return a[0] -b[0];
// });
// 报错
// 最后执行的输入:[[-2147483646,-2147483645],[2147483646,2147483647]]
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
int result = 1;
for (int i = 1;i < points.length;i++){
if (points[i - 1][1] < points[i][0]){
result++;
}else{
points[i][1] = Math.min(points[i - 1][1],points[i][1]);
}
}
return result;
}
}
注意看范围吧,如果数字的范围属于正负都有,且很大的情况,最好使用Integer
无重叠区间
链接: 435. 无重叠区间
思路
本题其实和452.用最少数量的箭引爆气球 (opens new window)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号([0,1]和[1,2]不是重叠区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。
代码
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
int result = 1;
for (int i = 1;i < intervals.length;i++){
if (intervals[i - 1][1] <= intervals[i][0]){
result++;
}else{
intervals[i][1] = Math.min(intervals[i - 1][1],intervals[i][1]);
}
}
return intervals.length - result;
}
}
对于二维数组而言,
intervals.length求的是行数
intervals[0].length求的是列数
划分字母区间
链接: 763.划分字母区间
思路
自己属于代码能力略差
有思路但不知道能不能用代码实现,所以不确定自己的思路是否可行,也可能因为不熟悉代码,所以不敢有思路;
涉及到相同不同字符串的,可以考虑一下使用哈希表
第一次遍历,记录一下字母的最远位置
设置left和right区间左边界和右边界
第二次遍历,利用哈希表来求右边界的最大值,当i遍历到右边界时,说明这个区间已经找到,将该区间的长度加入到result中,left = right + 1,接着遍历。
很顺畅的思路,我手写我心,因为自己代码能力较差,所以没有想到这种思路。
代码
class Solution {
public List<Integer> partitionLabels(String s) {
int[] hash = new int[27];
for (int i = 0;i < s.length();i++){
hash[s.charAt(i) - 'a'] = i;
}
// 注意审题
List<Integer> result = new ArrayList<>();
int left = 0;
int right = 0;
for (int i = 0;i < s.length();i++){
right = Math.max(right,hash[s.charAt(i) - 'a']);
if (i == right){
result.add(right - left + 1);
left = right + 1;
}
}
return result;
}
}
合并区间
链接: 56. 合并区间
思路
本题思路很简单,但自己思考不全面(在草稿纸上列一下诶,不要纯用脑子想)报错了很多次
主要有两个重要思想:合并,以及回溯
合并分三种情况,最普通的合并,以及两种包含(报错了至少三次)
intervals[i][0] = Math.min(intervals[i - 1][0],intervals[i][0]);
intervals[i][1] = Math.max(intervals[i - 1][1],intervals[i][1]);
不管是哪种情况,左边界取两个元素中最小的左边界,右边界取两个元素中最大的右边界
还有回溯:前一个元素进行完操作,加入结果中,但如果下一个元素需要与前一个元素进行合并操作再加入,这样结果就重复了,所以我们需要回溯,加入之后如果需要该元素进行处理,我们将该元素删除。
代码
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length == 1) return intervals;
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
// 首先要找到重叠的区间
ArrayList<int[]> result = new ArrayList<>();
int num = intervals.length;
result.add(intervals[0]);
for (int i = 1;i < intervals.length;i++){
// 重叠
if (intervals[i - 1][1] >= intervals[i][0]){
intervals[i][0] = Math.min(intervals[i - 1][0],intervals[i][0]);
intervals[i][1] = Math.max(intervals[i - 1][1],intervals[i][1]);
num--;
result.remove(result.size() - 1);
}
result.add(intervals[i]);
}
return result.toArray(new int[num][]);
}
}
单调递增的数字
链接: 738.单调递增的数字
思路
思路很简单,主要是自己思维不全面 (容易漏情况)加之不知道如何用代码去实现
强迫自己有条理地思考
针对不是递减的数字,从后往前遍历,如果不符合条件,前面的元素–,记录下当前元素的下标,继续遍历;然后在记录的下标开始往后的数都赋值为9
我自己能察觉到一点,不太自信。
代码
class Solution {
public int monotoneIncreasingDigits(int n) {
String str = String.valueOf(n);
char[] chars = str.toCharArray();
int flag = chars.length;
for (int i = chars.length - 1;i > 0;i--){
if (chars[i -1] > chars[i]){
chars[i - 1]--;
flag = i;
}
}
for (int i = flag;i < chars.length;i++){
chars[i] = '9';
}
return Integer.parseInt(String.valueOf(chars));
}
}
JAVA 中int类型转String类型的三种通常方法:
1、String.valueOf(int i)
2、Integer.toString(int i)
3、i + “”; //i 为 int类型,int+string型就是先将int型的i转为string然后跟上后面的空string。
三种方法效率排序为:
Integer.toString(int i) > String.valueOf(int i) > i+“”
最后的return 挺有意思的
监控二叉树
链接: 968.监控二叉树
思路
看起来很复杂,也是困难难度
本质还是二叉树的遍历
节点的三种状态:
0:无覆盖
1:有覆盖
2:有摄像头
终止条件:
空节点默认为有覆盖的状态,避免在叶子节点上放摄像头
单层逻辑:
如果左右节点都为1(有覆盖),则返回0
左右节点若有一个0(无覆盖),则返回2
左右节点若有一个2(有节点),则返回1
在主函数中如果返回的root节点为0,无覆盖的情况,我们仍要将result++
在if语句中,如果if语句外没有return返回值,则if语句需要将所有的情况都包含
代码
class Solution {
int result = 0;
int traversal(TreeNode root){
if (root == null) return 1;
int left = traversal(root.left);
int right = traversal(root.right);
if (left == 1 && right == 1){
return 0;
}else if (left == 0 || right == 0){
result++;
return 2;
}else{
return 1;
}
}
public int minCameraCover(TreeNode root) {
if (traversal(root) == 0)
result++;
return result;
}
}