系统设计题
主要是考察数据结构的设计,多条件排序算法
练习题目:
1772
379
1369
1817
635
1500
355
1845
贪心算法
455.饼干分发
贪心算法基本入门题目,了解局部最优情况下,累计后达成全局最优
题目描述:需求数组g和饼干数组s,求解最大的满足数量(最多的s[i] >= g[j])
解体思路:排序需求数组和饼干数组,按需进行匹配,实现每次的最优解
public int findContentChildren(int[] g, int[] s) {
// 两个数组先进行排序
Arrays.sort(g);
Arrays.sort(s);
int out = 0;
int j = 0;
for (int i = 0; i < s.length && j < g.length; i++) {
// 每次满足 s[i] >= g[j] 时,结果集+1,需求和饼干都往后+1
// 不满足时,饼干+1,需求不变
if (s[i] >= g[j]) {
out++;
j++;
}
}
return out;
}
买卖股票的最佳时机 II
贪心算法中需要理解的题目,初步看很难一下子想通,但是有了方法和套路之后,往贪心算法考,想每次的最优,最大的利益可以转化成每次的利益相加,(最后就转化为了,只要明天价格比今天高,就进行买卖)
这种题目比较容易一下子看蒙,所以要多练习,还有就是要思路清晰
public int maxProfit(int[] prices) {
int result = 0;
for (int i = 0; i < prices.length - 1; i++) {
if (prices[i] < prices[i + 1]) {
result+= prices[i+1] - prices[i];
}
}
return result;
}
135.分发糖果
贪心算法中有难度的题目,之前考试中也遇到过,基本都是不会做,第一次做也是看着答案做,边界控制异常复杂,要十分小心,不然修修补补会浪费很多时间
类似题目有套路,利用条件要求,形成逻辑,但还是要一定的思考和经验,熟能生巧,边界的处理,只能多加练习
核心:两次变量,形成两次规则下的条件,基本生产的数据,形成最终答案
public int candy(int[] ratings) {
int len = ratings.length;
int[] arr = new int[len];
// 按照左侧规则,最左先设置为1,左 < 右,即 右侧+1
for (int i = 0; i < len; i++) {
if (i > 0 && ratings[i - 1] < ratings[i]) {
arr[i] = arr[i - 1] + 1;
} else {
arr[i] = 1;
}
}
int tmp = 0;
int result = 0;
// 右规则,最右侧先为1, 右 < 左, 左侧+1
for (int i = len - 1; i >= 0; i--) {
if (i < len - 1 && ratings[i] > ratings[i + 1]) {
tmp++;
} else {
tmp = 1;
}
// 同时满足 左右规则时,数值的的保留进行累加
result += Math.max(tmp, arr[i]);
}
return result;
}
55. 跳跃游戏
之前写过的题目,再次打开,居然一下子没有想出来,看来之前写的才发现,思路和写法真的s是秒
核心点:不用拘泥于每次究竟跳跃多少步,而是看覆盖范围
两年前的写法:很巧妙,不知道是不是当时自己想出来的
public boolean canJump(int[] nums) {
int n=1;
for(int i=nums.length-2; i>=0;i--){
if(nums[i]>=n){
n=1;
}else {
n++;
}
}
if(n>1){
return false;
}else {
return true;
}
}
参考答案,第二次解题,过程不是很流畅
public boolean canJump(int[] nums) {
int rang = 0;
for (int i = 0; i < nums.length; i++) {
// 坐标如果直接大于范围rang,结果肯定就失败了
if (i <= rang) {
rang = Math.max(i + nums[i], rang);
if (rang >= nums.length-1) {
return true;
}
} else {
return false;
}
}
return false;
}
134. 加油站
这道题目比较常规,常规暴力也可以解答,算是简单题目,巧妙就是要寻找出好方法
常规暴力法:
public int canCompleteCircuit(int[] gas, int[] cost) {
// 贪心暴力遍历
int len = gas.length;
if (len == 1 && gas[0] == cost[0]) {
return 0;
}
for (int i = 0; i < len; i++) {
if (gas[i] <= cost[i]) {
continue;
}
int res = 0;
int index = 0;
while (res >= 0 && index < len) {
int tmp = (index + i) % len;
res += gas[tmp] - cost[tmp];
index++;
}
if (index == len && res >= 0) {
return i;
}
}
return -1;
}
巧妙解法,将问题转化才是核心亮点
public int canCompleteCircuit(int[] gas, int[] cost) {
int res = 0;
int mincos = Integer.MAX_VALUE;
int index = 0;
for (int i = 0; i < gas.length; i++) {
res += gas[i] - cost[i];
// 计算出整个过程中油耗最高点,把该点作为最后一个点
if (res < mincos) {
mincos = res;
index = i;
}
}
if (res >= 0) {
// 返回油耗最高点的下个点作为开始点
return (index + 1) % gas.length;
} else {
return -1;
}
}
406. 根据身高重建队列
多个条件或者纬度时,一定要先确定好一个纬度,再确定另一个纬度
属于长题目,较复杂,需要理解力,细致,思路清晰,尤其是排序好之后的插入算法,没有想到的化,就十分困难,要梳理关系
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (t1, t2) -> {
if (t1[0] == t2[0]) {
return t1[1] - t2[1];
} else {
return t2[0] -t1[0];
}
});
List<int[]> list = new ArrayList<>();
for (int[] i : people) {
list.add(i[1], i);
}
int i = 0;
for (int[] j : list) {
people[i] = j;
i++;
}
return people;
}