【案例1】
【题目描述 难度非常高】
【思路解析】
因为要求额外空间复杂度为O(1),所以我们只能使用有限几个变量,来得到整个数组所在的城市距离首都的距离。因为数组paths[i]表示,i城市指向paths[i]城市,我们可以利用这个指向关系(next为下次要去的城市,last为当前城市。并且在往前遍历时,要记录回去的信息,即paths[next的值]=last的值,这样我们就可以再通过last和next往回走),一直往前遍历,直到next==paths[next],或者paths[next] < 0,我们便开始往回走,并且回走时,如果城市i距离首都为x,则给paths[i]赋值为-x。
然后得到了这个距离数组后,再通过判断距离数组的正负来统计距离(通过指向关系来进行循环计数)的个数。
【代码实现】
import java.util.Arrays;
/**
* @ProjectName: study3
* @FileName: Ex1
* @author:HWJ
* @Data: 2023/9/14 22:30
*/
public class Ex1 {
public static void main(String[] args) {
int[] paths = {9, 1, 4, 9, 0, 4, 8, 9, 0, 1};
pathToDistance(paths);
distanceToCount(paths);
System.out.println(Arrays.toString(paths));
}
public static void pathToDistance(int[] paths) {
int n = paths.length;
int capital = -1;
for (int start = 0; start < n; start++) {
if (paths[start] == start) {
capital = start;
} else if (paths[start] > -1) {
int last = start;
int next = paths[last];
while (paths[next] != next && paths[next] > -1) {
int num = paths[next];
paths[next] = last;
last = next;
next = num;
}
int i = paths[next] == next ? 0 : paths[next];
while (last != start) {
int num = paths[last];
paths[last] = --i;
last = num;
}
paths[last] = --i;
}
}
paths[capital] = 0;
}
public static void distanceToCount(int[] paths){
for (int i = 0; i < paths.length; i++) {
int cur = paths[i];
paths[i] = Math.max(paths[i], 0);
while (cur <= 0){
int next = paths[-cur];
if (next < 0){
paths[-cur] = 1;
cur = next;
}else {
paths[-cur]++;
break;
}
}
}
}
}
【案例2】
【题目描述 分糖果问题】
一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:
-
每个孩子不管得分多少,起码分到一个糖果。
-
任意两个相邻的孩子之间,得分较多的孩子必须拿多一些糖果。
-
【进价】要求相邻的两个孩子如果得分相同,得到的糖果应该相同。
-
要求时间复杂度为O(N)
【思路解析】
我们可以通过得分数组得到一个得分的折线图(即升降情况),然后通过从左向右遍历这个升降情况得到每个人应该分到的糖果数。然后通过从右向左遍历这个升降情况得到每个人应该分到的糖果数。然后对应求最大即为每个孩子的糖果数。遍历时要求升序糖果数++,如果降序或者保持不变,使糖果数变回1.
【进价思路分析】
我们可以通过得分数组得到一个得分的折线图(即升降情况),然后通过从左向右遍历这个升降情况得到每个人应该分到的糖果数。然后通过从右向左遍历这个升降情况得到每个人应该分到的糖果数。然后对应求最大即为每个孩子的糖果数。遍历时要求升序糖果数++,如果降序使糖果数变回1,如果保持不变,则使糖果数保持不变。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex2
* @author:HWJ
* @Data: 2023/9/15 0:07
*/
public class Ex2 {
public static void main(String[] args) {
int[] scores = {1, 2, 2};
System.out.println(candy1(scores));
System.out.println(candy2(scores));
}
public static int candy1(int[] scores){
int[] left = new int[scores.length];
int[] right = new int[scores.length];
left[0] = 1;
for (int i = 1; i < scores.length; i++) {
if(scores[i] > scores[i - 1]){
left[i] = left[i - 1] + 1;
}else {
left[i] = 1;
}
}
right[scores.length - 1] = 1;
for (int i = scores.length - 2; i >= 1; i--) {
if(scores[i] > scores[i + 1]){
right[i] = right[i + 1] + 1;
}else {
right[i] = 1;
}
}
int res = 0;
for (int i = 0; i < scores.length; i++) {
res += Math.max(left[i], right[i]);
}
return res;
}
public static int candy2(int[] scores){
int[] left = new int[scores.length];
int[] right = new int[scores.length];
left[0] = 1;
for (int i = 1; i < scores.length; i++) {
if(scores[i] > scores[i - 1]){
left[i] = left[i - 1] + 1;
}else if (scores[i] < scores[i - 1]){
left[i] = 1;
}else {
left[i] = left[i - 1];
}
}
right[scores.length - 1] = 1;
for (int i = scores.length - 2; i >= 1; i--) {
if(scores[i] > scores[i + 1]){
right[i] = right[i + 1] + 1;
}else if (scores[i] < scores[i + 1]){
right[i] = 1;
}else {
right[i] = right[i + 1];
}
}
int res = 0;
for (int i = 0; i < scores.length; i++) {
res += Math.max(left[i], right[i]);
}
return res;
}
}
【超级进阶 非常难】
要求时间复杂度为O(N), 额外空间复杂度为O(1)。 实现特别困难。
【案例3】
【题目描述】
【思路解析】二叉树递归套路类题目
第一种思路,列出所有可能性,然后在递归时得到所有可能性,然后不断汇总求最优解。
能获得最优解的可能性如下:
(1)x结点以下都被照亮(包含x结点),且x上不放灯。
(2)x结点以下都被照亮(包含x结点),且x上放灯。
(3)x结点以下都被照亮(不包含x结点),但x上放灯。(因为这种情况可能引出下一个最优解,在x的父上放灯,影响的结点更多)。
第二种思路,我们在传递的时候已知最优解需要的是什么信息,利用贪心加速常数时间。
比如当左孩子和右孩子,未被覆盖时,x上必须放灯,如果左孩子和右孩子,被覆盖且放灯,则x上一定不放灯。如果左孩子和右子,被覆盖但没放灯,则x未被覆盖。
【代码实现】
/**
* @ProjectName: study3
* @FileName: Ex3
* @author:HWJ
* @Data: 2023/9/15 0:16
*/
public class Ex3 {
public static void main(String[] args) {
}
public static class Node{
Node left;
Node right;
}
public static class ReturnData{
public int unCovered;
public int coveredNoCamera;
public int coveredHasCamera;
public ReturnData(int unCovered, int coveredNoCamera, int coveredHasCamera) {
this.unCovered = unCovered;
this.coveredNoCamera = coveredNoCamera;
this.coveredHasCamera = coveredHasCamera;
}
}
public static int getMin(Node head){
ReturnData data = process(head);
return Math.min(data.unCovered, Math.min(data.coveredHasCamera, data.coveredNoCamera));
}
public static ReturnData process(Node node){
if (node == null){
return new ReturnData(Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
}
ReturnData left = process(node.left);
ReturnData right = process(node.right);
int unCovered = left.coveredNoCamera + right.coveredNoCamera;
int coveredNoCamera = Math.min(left.coveredHasCamera + right.coveredHasCamera,
Math.min(left.coveredHasCamera + right.coveredNoCamera,
left.coveredNoCamera + right.coveredHasCamera));
int coveredHasCamera = Math.min(left.unCovered,
Math.min(left.coveredHasCamera, left.coveredNoCamera)) +
Math.min(right.unCovered,
Math.min(right.coveredHasCamera, right.coveredNoCamera)) + 1;
return new ReturnData(unCovered, coveredNoCamera, coveredHasCamera);
}
public enum Status{
UNCOVERED, COVERED_NO_CAMERA, COVERED_HAS_CAMERA;
}
public static class ReturnData2{
public Status status;
public int cameras;
public ReturnData2(Status status, int cameras) {
this.status = status;
this.cameras = cameras;
}
}
public static int getMin2(Node head){
ReturnData2 data = process2(head);
return data.cameras + (data.status == Status.UNCOVERED ? 1 : 0);
}
public static ReturnData2 process2(Node node){
if (node == null){
return new ReturnData2(Status.COVERED_NO_CAMERA, 0);
}
ReturnData2 left = process2(node.left);
ReturnData2 right = process2(node.right);
int cameras = left.cameras + right.cameras;
if (left.status == Status.UNCOVERED || right.status == Status.UNCOVERED){
return new ReturnData2(Status.COVERED_HAS_CAMERA, cameras + 1);
}
if (left.status == Status.COVERED_HAS_CAMERA && right.status == Status.COVERED_HAS_CAMERA){
return new ReturnData2(Status.COVERED_NO_CAMERA, cameras);
}
return new ReturnData2(Status.UNCOVERED, cameras);
}
}