最小编辑代价
动态规划,但是这个递推公式说实话我老是想不通…
import java.util.*;
public class Solution {
/**
* min edit cost
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @param ic int整型 insert cost
* @param dc int整型 delete cost
* @param rc int整型 replace cost
* @return int整型
*/
public int minEditCost (String str1, String str2, int ic, int dc, int rc) {
int m=str1.length();
int n=str2.length();
int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++){
dp[i][0]=i*dc;
}
for(int j=1;j<=n;j++){
dp[0][j]=j*ic;
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}
else{
int temp1=dp[i-1][j-1]+rc;
int temp2=dp[i-1][j]+dc;
int temp3=dp[i][j-1]+ic;
dp[i][j]=Math.min(Math.min(temp1,temp2),temp3);
}
}
}
return dp[m][n];
}
}
树的直径
框架就是一个经典的矩阵dfs,一般这种传的参就是一个矩阵map,一个visited列表或矩阵表示访问过的点,一个我们需要更新得到的结果result,这个result虽然是作为参数传进去,但是是最终我们需要的结果,只是拿来更新的,而dfs这个函数return的结果通常不是最后需要的结果,而是一些中间变量,比如左子树和右子树里面哪个大啊之类的
但是算路径和那一块儿没有懂得很透彻,日后来看
import java.util.*;
/*
* public class Interval {
* int start;
* int end;
* }
*/
public class Solution {
/**
* 树的直径
* @param n int整型 树的节点个数
* @param Tree_edge Interval类一维数组 树的边
* @param Edge_value int整型一维数组 边的权值
* @return int整型
*/
//最大距离为什么是直径
int max=Integer.MIN_VALUE;
public int solve (int n, Interval[] Tree_edge, int[] Edge_value) {
// write code here
List<int[]>[] list=new ArrayList[n];
for(int i=0;i<n;i++){
list[i]=new ArrayList<>();
}
for(int i=0;i<n-1;i++){
list[Tree_edge[i].start].add(new int[]{Tree_edge[i].end,Edge_value[i]});
list[Tree_edge[i].end].add(new int[]{Tree_edge[i].start,Edge_value[i]});
}
dfs(list,0,new boolean[n]);
return max;
}
public int dfs(List<int[]>[] list,int index,boolean[] visited){
int left = 0;
int right = 0;
visited[index]=true;
for(int[] item:list[index]){
int kid=item[0];
int weight=item[1];
if(visited[kid]==true) continue;
weight+=dfs(list,kid,visited);
if(weight>left){
right=left;
left=weight;//左边的路径大了的话就更新一下左边的路径,但是为什么要right=left?
}else if(weight>right){
right=weight;//就是右边的路径大了的话就更新一下右边的路径
}
//然后每次遍历的时候需要更新max的结果
max=Math.max(right+left,max);
}
//返回的是左右子树中大的那一条路
return Math.max(left,right);
}
}
设计模式之适配器模式
结构型模式是个大类,它的作用是从程序的结构上实现松耦合,从而扩大整体的类结构,用来解决更大的问题
分类:
适配器模式/代理/桥接/装饰/组合/外观
适配器可以理解为插头的转换器,里面网线是一个类,USB是一个类,中间的适配器是一个类
角色分析:
1.目标接口,比如一个电脑的usb接口
//客户端类:想上网,但插不上网线
public class Computer {
//电脑需要链接转接器才能上网
public void net(NetToUsb adapter){
//上网的具体实现,找一个转接头
adapter.handleRequest();
}
public static void main(String[] args) {
/*
[继承的写法]
//需要一个电脑/适配器.网线
Computer computer=new Computer();
Adapted adapted=new Adapted();
Adapter adapter=new Adapter();
//如果不写一个adpter适配器类的话,电脑和网线之间不会有任何的关系
//但是通过传入一个适配器,就实现了电脑的上网
computer.net(adapter);
*/
/*
[组合的写法,对象适配器]
这个方法更加高效
*/
Computer computer=new Computer();
Adapted adapted=new Adapted();
Adapter2 adapter2=new Adapter2(adapted);
computer.net(adapter2);
}
}
2.需要适配的接口,比如网线
//要被适配的类:网线
public class Adapted {
public void request(){
System.out.println("请求");
}
}
3.适配器,用于连接这两个类,连接的时候适配器有两种写法,一种是继承的写法,继承网线这个类,一种更好的写法是使用组合,也就是向适配器中传入一个网线的对象,再调用网线里面的请求方法
它的抽象接口长这个样子,里面有一个抽象函数就是解决连接的问题
//接口转换器的抽象实现
public interface NetToUsb {
//作用:处理请求:网线->usb
public void handleRequest();
}
实现一个具体的从网线连接到usb的适配器
这是第一种继承的实现方法
//要被适配的类:网线
//真正的适配器,需要链接usb
public class Adapter extends Adapted implements NetToUsb {
@Override
public void handleRequest() {
super.request();//继承了网线,调用它的请求方法,这样就可以实现上网了
}
}
这是第二种组合的写法
//真正的适配器,需要链接usb
//另一种非继承的组合写法
public class Adapter2 extends Adapted implements NetToUsb {
private Adapted adapted;
public Adapter2(Adapted adapted) {
this.adapted = adapted;
}
@Override
public void handleRequest() {
adapted.request();//new了一个适配器来实现上网,这个和继承后使用super是不同的
}
}
Java中常见的适配器:
InputStreamReader(InputStream)这个实现的就是一个从字符流到字节流的转接器
SpringMVC中的处理器,映射器
GUI和SpringBoot编程也用到了大量的适配器
异常
异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象,java处理异常的方式是jvm进行中断处理
- Throwable
异常的根类是throwable,有两个子类,一个是error一个是exception
exception:编译期异常,进行编译(写代码)java程序出现的问题
Runtimeexception:运行期异常,java程序运行过程中出现的问题
异常就相当于程序得了一个小感冒,把异常处理掉,程序可以继续执行
而error相当于得了绝症,必须修改源代码,程序才能继续执行
异常处理的两种方法,一种是抛出去,一种是trycatch继续执行
try里面放的是可能会出现异常的代码,catch里面放的是异常的处理逻辑
public static void main(String[] args) /*throws ParseException8*/ {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
try {
Date date=sdf.parse("1999-0909");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("后续代码");
}
-
OOM问题
outofmemoryerror,JVM内存溢出错误,这个问题必须修改代码 -
异常的产生过程解析及其处理解析
我们先来看一个例子
public static int getelement(int[]arr,int index){
int ele=arr[index];
return ele;
}
public static void main(String[] args) /*throws ParseException8*/ {
int[] arr={1,2,3};
int ele=getelement(arr,3);
System.out.println(ele);
}
访问了数组中的3索引,但数组是没有3索引的,JVM就会检测出程序出现异常,这个时候jvm会做两件事情
1.JVM会根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的(内容,原因,位置)
2.在getelement方法中没有异常处理的逻辑(trycatch),那么JVM就会把异常对象抛出给方法的调用者main方法来处理这个异常
↓
main方法接收到了这个异常对象,main方法也没有异常的处理逻辑,继续吧对象抛出给mian方法的调用者JVM处理
↓
JVM接收到了异常对象,做了两件事情
1.把异常对象内容原因位置,以红色的字体打印在控制台
2.JVM会终止当前正在执行的java程序–>中断处理
- 处理的过程
使用throw关键字在指定的方法中抛出指定的异常:throw new xxxException(异常产生的原因)
1.throw关键字必须写在方法的内部
2.throw后面new的对象必须是exception本身或其子类
3.throw抛出的异常对象,必须要处理,RuntimeException是个例外,可以不处理,让JVM来处理(也就是中断)
4.如果throw创建的是编译异常就必须处理,要么throws要么trycatch
处理的代码如下:
public static int getelement (int[]arr,int index){
/*
对传递过来的参数数组进行合法性校验
如果arr是空的,
*/
//nullpointer是一个运行期异常,我们默认交给jvm处理
if(arr==null){
throw new NullPointerException("传递的数组是null");
}
/*
我们还可以对传递过来的index参数做一个合法性校验
如果index不在索引范围内,我们就抛出数组索引越界异常,告知方法的调用者
*/
if(index>arr.length-1 || index<0){
throw new ArrayIndexOutOfBoundsException("数组的索引越界了");
}
int ele=arr[index];
return ele;
}
public static void main (String[] args) /*throws ParseException8*/ {
int[] arr=new int[]{1,2,3};
int ele=getelement(arr,3);
System.out.println(ele);
}
- objects非空判断
Objects.requireNonNull(obj)可以进行一个合法性的判断
int[] b=null;
Objects.requireNonNull(b);
比如这样,仍然可以抛出一个空指针异常,就不用自己throw一个nopointer异常了
-
throws关键字:声明异常
当方法内部排除异常对象的时候,那么我们就必须处理这个异常对象,可以使throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,甩锅给别人处理),最终交给JVM中断处理,要么就是trycatch自己处理了
这个关键字必须在方法声明的时候用
注意因为throws最后是把异常交给JVM处理的,所以异常了就中断了,异常后面的代码执行不了,为了解决这个问题我们用到了trycatch -
try…catch捕获异常
1.try里面可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常
2.如果try里面产生了异常,那么就会执行catch里面的处理逻辑,执行完catch里面的,继续执行后面的代码,没产生异常的话catch里面就不执行了 -
Throwable类定义的三个异常处理的方法
1.getmessage简短描述
2.tostring字符串
3.printstacktrace这个打印的异常信息是最全面的 -
finally代码块
不论是否出现异常,finally里面的代码都会执行,finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源回收(IO)
一个try多个catch注意事项:catch里面定义的异常变量,如果有子类父类关系,那么子类的异常必须写在[上面],否则就会报错
比如产生了一个new ArrayIndex…异常和一个new IndexOut…异常
try中如果出现了异常对象,会把异常对象抛出给catch处理,抛出的异常对象,会从上到下一次赋值给catch
因为父类IndexOut…写在上面的时候存在多态,IndexOut…也可以捕获ArrayIndex…也可以,这样就会报错了 -
父类异常什么样,子类异常就什么样
子类重写父类方法时,可以抛出和父类相同的异常/或者抛出父类的异常类的子类
另外父类的某方法没有抛出异常,那么子类重写这个方法的时候就一定不能抛出异常 -
finally中有return时会发生什么?
如果finally里面有return语句,因为它一定会执行,那么永远返回的都是finally里面的结果,这种情况应该尽量避免 -
怎样自定义异常类?
注意里面要写俩构造方法
/*
自定义异常类
//可以继承自exception或者runtimeexception
继承自exception的话就是一个编译期间异常,如果方法抛出了编译期异常,就必须处理这个异常
继承自runtimeexception的话就是一个运行期异常,我们就无需处理了,让JVM中断就好了
*/
public class RegisterException extends Exception{
//格式:
//添加一个空参构造方法
//添加一个带异常信息的构造方法
public RegisterException() {
}
public RegisterException(String message) {
super(message);
}
}
= =
哎看到一群本科生嫌弃开奖开得低,甚至说出了**k以下都是侮辱这种词,心态裂开了…撑住啊不要崩啊我这个菜鸡T_T
感觉现在这个刷题量不是很够,很多题没想法,睡前再看一会leetcode教程视频当做催眠 吧