1. 关于贪心
还是感觉没有定论,甚至不觉得自己贪心
2. 例题
lc406 根据身高重建队列
前置知识点
- 链表
利用add(index, element) 方法,允许在链表中的指定位置插入元素 - sort排序
- 利用sort(数据源,排序方法即lambda (参数1,参数2)->{表达式})
- 关于升降序:a-b升序,b-a降序
- 二维数组
- 遍历二维数组中的每一个数组for(p : people)。第一行取值是p[0],第二列取值是p[1]
- 将链表转为数组queue.toArray(new int[people.length][ ])
- 因为链表中的每个元素都对应一个一维数组,而链表的大小就是元素的个数,所以第二维的长度会与链表的大小一致,无需额外指定
思路
我的思路:排序之后不断互换
Step 1: 对people进行排序,按身高降序,如果身高相同,按列的排序位置升序排序
Step 2: 从矮到高遍历,进行互换
people.sort(key=lambda x: (-x[0], x[1]))
for i in range(len(people)):
j = i
while j > 0 and people[j][1] < j:
//交换当前人和前面的人
people[j], people[j - 1] = people[j - 1], people[j]
j -= 1
return people
精简思路:插入新的队列,而不是在原队列上互换
Step 1: 排序,从高到矮,顺序则从前到后
Step 2: 将排序后的数组从第一个点cur开始插入queue,插入给定的位置cur[1]即可
易错点
到处都是
代码实现
class Solution {
public int[][] reconstructQueue(int[][] people) {
//先将peope排序
Arrays.sort(people, (a,b)->{ //a,b这里是待比较的两个一维数组
if(a[0]==b[0])
return a[1]-b[1]; //step2,若身高相同,则按顺序[1]升序排序
return b[0]-a[0]; //step1,按身高[0]降序排序
});
//将people中的值从前往后遍历,不断插入queue中
LinkedList<int[]> queue = new LinkedList<>(); //创建链表,每个节点都是一维数组
for(int[] p:people){ //遍历people中的每个一维数组
queue.add(p[1],p); //利用了链表的功能.add(indexLoc, val),在指定位置插入元素,而不用费劲去找到底位置在哪里才是正确的了
}
return queue.toArray(new int[people.length][]); //不要忘记返回值改成数组
}
}
lc435 无重叠区间
思路
- 区间去重问题
- 所有需要排序的问题,仔细思考要对谁进行排序
- ∵ 删除最小=区间间挨得距离最近=每一个区间越小越好–>终止值越小越好
- ∴ 重点(接下来是否删除的重点也在终止值)。即让终止值小的区间先排在前面
易错点
- 我的问题:排序时先考虑起始值再考虑终止值
代码实现
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
//优先按照终止值来升序排序。这里没有设置起始值:因为如果终止值相同,默认起始值也是升序排序
Arrays.sort(intervals, (a,b)->{
return a[1]-b[1];
});
int remove =0;
int prev_start = intervals[0][0];
int prev_end = intervals[0][1];
for(int i=1;i<intervals.length;i++){
int[] cur = intervals[i];
int cur_start = cur[0];
int cur_end = cur[1];
//如果当前起始值在上一个值的区间里,则舍弃它(因为它的终止值比上一个更大,所以删掉不可惜),接下来继续找和上一个小区间(小终止值)挨得最近的那个区间
if(cur_start<prev_end){
remove++;//相当于不把当前数组加入数组里,所以不更新prev_start和prev_end
}else{
prev_start=cur_start;
prev_end=cur_end;
}
}
return remove;
}
}
lc763 划分字母区间
思路
- 一共26个字母,给每个字母计数 每次都遍历一个字母,找到它的最后一位——这个思路是对的
- 在找到每个字母的最远区间后,怎么利用最远的那个字母来切割?——更新游标boundary,如果下一个字母的boundary比现在大就更新,直到i=boundary为止,进行切割,然后重置boundary
代码实现
class Solution {
public List<Integer> partitionLabels(String s) {
LinkedList<Integer> cut = new LinkedList<>();
int[] alphabet = new int[26];
//这里要把s转为字符数组,方便引用检索
char[] str = s.toCharArray();
//记录每个字母的最后一位位置
for(int i=0;i<str.length;i++){
alphabet[str[i]-'a'] = i;
}
//遍历str,不断更新boundary直到真的可以切割,把切割记录进cut
int boundary=0;
int prevCutloc=-1;
for(int i=0;i<str.length;i++){
if(alphabet[str[i]-'a']>boundary){
boundary = alphabet[str[i]-'a'];
}
if(boundary==i){
cut.add(i-prevCutloc);
prevCutloc=i; //更新上一刀的位置
}
}
return cut;
}
}
lc56 合并区间
思路
去重题,优先考虑排序的顺序问题,从前往后还是从后往前?
与435互为镜像
检测重叠:如果curStart<preEnd则确定重叠
易错点
- 根据[0]还是[1]排序?做这种去重题,首先要重新排序让数组规整,但是如果目的是覆盖区间,则一定要从起始值最小开始排。和435删除重叠区间相反,那个要确保终止值最小,所以要以终止值为基准排
- (在检测重合后)更新cur的值时:起始值确定(已经按照从大到小排序了,只需要取前一个值确定更小),那终止值取prev还是cur?需要比大小
代码实现
class Solution {
public int[][] merge(int[][] intervals) {
//易错点1:如果目的是覆盖区间,则一定要从起始值最小开始排。和435删除重叠区间相反,那个要确保终止值最小,所以要以终止值为基准排
Arrays.sort(intervals, (a,b) ->{
return a[0]-b[0];
});
int[] prev = intervals[0];
LinkedList<int[]> result = new LinkedList<>();
result.add(prev);
for(int i=1;i<intervals.length;i++){
int[] cur=intervals[i];
if(cur[0]>prev[1]){//判定不重叠
result.add(cur);
}else{
cur[0]=prev[0];
cur[1]=Math.max(cur[1],prev[1]); //易错点2:注意这个不能省略,因为可能出现第二个虽然起始值大,但是整个数组包含于第一个数组区间的情况
result.removeLast(); //删除上一个重叠的prev
result.add(cur);
}
prev=cur; //prev前进到当前位置
}
return result.toArray(new int[result.size()][]);
}
}
lc738 单调递增数字
前置知识
string,int, char[]之间的转换
- 数字变为字符数组char[]:Integer.toString(n).toCharArray()
- 数字变为数字数组int[]:
- 先变为字符数组:char[] charArray = Integer.toString(n).toCharArray()
- 然后把字符变为数字:for (int i=0; i<=charArray.length; i++) {int[] intArray[i] =Character.getNumericValue(charArray[i]);}
- 整数数组int[]变成数字:for(int number : numbers {result=result*10+number;}
- 字符数组char[]变成字符串变成数字:str= new String(char) 再 integer.parseInt(str) 或者 Integer.parseInt(String.valueOf(digits))
思路
我的思路
1.先把数字转为数组:char[] digits = Integer.toString(n).toCharArray();
2.从前向后遍历若cur<next,则cur-1,next之后都是9,每次类推 -->这样有个问题,碰到332,就需要退两格修改digits[i-2]和digits[i-1],这样会很麻烦
优化:从后向前遍历:标记不符合条件的情况cur<next,每次标记开始change的位置next,并且把当前值cur–
如:标记最后一位,倒数第二位都不符合要求,从倒数第二位开始改9. 332–32[2]–2[2][2]–>299
易错点
在修改digit[i]赋值为9时,注意应赋值字符‘9’而不是数字
代码实现
class Solution {
public int monotoneIncreasingDigits(int n) {
//精简1:转为字符串后直接转为char[]
char[] digits = Integer.toString(n).toCharArray();
int changeIndex =digits.length; //预设不需要修改9
//从后向前遍历
for(int i=digits.length-2;i>=0;i--){ //从倒数第二位开始,同时排除了如果i-2<0的情况
if(digits[i]>digits[i+1]){
digits[i]--;
changeIndex=i+1; //*重点:每次标记开始change的位置next,并且把当前值cur--
}
}
//从changeIndex开始全部改成9
for(int j=changeIndex;j<digits.length;j++){
digits[j]='9'; //易错点:这里是字符而不是数字!这是一个char。如果转为int[]就不用管
}
//最后变成数字
int result = Integer.parseInt(new String(digits));
return result;
}
}