This commit is contained in:
markilue 2022-10-09 13:37:34 +08:00
commit f55bd38ecd
127 changed files with 12319 additions and 241 deletions

View File

@ -125,6 +125,21 @@
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.bahir</groupId>-->
<!-- <artifactId>flink-connector-redis_2.11</artifactId>-->
<!-- <version>1.0</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.apache.bahir/flink-connector-redis -->
<dependency>
<groupId>org.apache.bahir</groupId>
<artifactId>flink-connector-redis_2.12</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies> </dependencies>

View File

@ -1,6 +1,9 @@
package day01.scala package day01.scala
//import org.apache.flink.api.common.functions.FlatMapFunction
//import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment
//import org.apache.flink.util.Collector
object Example_scala1 { object Example_scala1 {
@ -11,27 +14,48 @@ object Example_scala1 {
env.setParallelism(1) env.setParallelism(1)
//TODO 读取数据源 //TODO 读取数据源
val stream = env.socketTextStream("localhost", 9999) // val stream = env.socketTextStream("localhost", 9999)
val stream = env.fromElements("hello world", "hello world")
//
// val value: SingleOutputStreamOperator[WordWithCount] = stream.flatMap[WordWithCount] {
//
// case (inputData:String, out:Collector[WordWithCount]) => {
// val strings = inputData.split(" ")
// strings.foreach {
// word => {
// out.collect(new WordWithCount(word, 1L))
// }
// }
// }
// }
// stream.flatMap( // val value: SingleOutputStreamOperator[WordWithCount] = stream.flatMap(new FlatMapFunction[String, WordWithCount] {
// override def flatMap(t: String, collector: Collector[WordWithCount]): Unit = {
// val strings = t.split(" ")
// strings.foreach {
// word => {
// collector.collect(new WordWithCount(word, 1L))
// }
// }
// }
// })
// value.print()
// env.execute()
// stream.flatMap {
// words => { // words => {
// val word = words.split(" ") // val word = words.split(" ")
//
// word.map( // word.map(
// word => { // word => {
// WordWithCount(word, 1L) // WordWithCount(word, 1L)
// } // }
// ) // )
//
// } // }
// ) // }
} }
case class WordWithCount(var word: String, var count: Long) case class WordWithCount(var word: String, var count: Long)

View File

@ -48,7 +48,7 @@ public class Example5 {
//声明一个状态变量作为累加器 //声明一个状态变量作为累加器
//状态变量的可见范围(作用域)的当前key //状态变量的可见范围(作用域)的当前key
//状态变量是单例只能被实例化一次 //状态变量是单例只能被实例化一次
private ValueState<Tuple2<Integer,Integer>> valueState; private ValueState<Tuple2<Integer,Integer>> valueState;
//保存定时器的时间戳 //保存定时器的时间戳
private ValueState<Long> timerTs; private ValueState<Long> timerTs;

View File

@ -0,0 +1,125 @@
package day03.selftry;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import org.apache.flink.util.Collector;
import java.util.Random;
/**
* @BelongsProject: Flink
* @BelongsPackage: day03.selftry
* @Author: dingjiawen
* @CreateTime: 2022-09-05 16:06
* @Description: TODO example5_retry,尝试计算十秒内的平均值
* @Version: 1.0
*/
public class Example5_retry {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
env
.addSource(
new SourceFunction<Integer>() {
private Random random = new Random();
private boolean running = true;
@Override
public void run(SourceContext<Integer> sourceContext) throws Exception {
while (running) {
int i = random.nextInt(10);
sourceContext.collect(i);
Thread.sleep(1000);
}
}
@Override
public void cancel() {
running = false;
}
}
)
.keyBy(r -> true)
.process(new KeyedProcessFunction<Boolean, Integer, Double>() {
//设置两个状态后端
//用于存储<值之和数量>
private ValueState<Tuple2<Integer, Integer>> valueState;
//存储一个定时器,多久做一次定时任务
private ValueState<Long> timeTs;
//在任务开始时注册并实例化两个状态后端
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
valueState = getRuntimeContext().getState(
//第一个值是名字第二个值是类型
new ValueStateDescriptor<Tuple2<Integer, Integer>>("sum-count", Types.TUPLE(Types.INT, Types.INT))
);
timeTs = getRuntimeContext().getState(
new ValueStateDescriptor<Long>("timer", Types.LONG)
);
}
//对每一个值进行处理,并且设置定时器
@Override
public void processElement(Integer integer, Context context, Collector<Double> collector) throws Exception {
//如果该状态后端的值是null就证明是第一次调用
if (valueState.value() == null) {
valueState.update(Tuple2.of(integer, 1));
} else {
//之后的调用了
Tuple2<Integer, Integer> temp = valueState.value();
valueState.update(Tuple2.of(temp.f0 + integer, temp.f1 + 1));
}
//第一次进来时设置定时器
if (timeTs.value() == null) {
//先根据context获取到当前的处理时间
long currentTime = context.timerService().currentProcessingTime();
//在上下文中注册对应的定时器
context.timerService().registerProcessingTimeTimer(currentTime + 10 * 1000L);
//更新定时器,避免下次再进入这个if
timeTs.update(1111L);
}
}
//定时器函数当定时器触发时会进行调用
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Double> out) throws Exception {
// super.onTimer(timestamp, ctx, out);
if (valueState.value() != null) {
//定时器触发开始计算平均值,并将其放入收集器collect中
out.collect((double) valueState.value().f0 / valueState.value().f1);
//将定时器清空
timeTs.clear();
}
}
})
.print();
env.execute();
}
}

View File

@ -48,7 +48,7 @@ public class Example2 {
//迭代器参数中只包含了一个元素就是增量聚合函数发送过来的聚合结果 //迭代器参数中只包含了一个元素就是增量聚合函数发送过来的聚合结果
long windowStart =context.window().getStart(); long windowStart =context.window().getStart();
long windowEnd = context.window().getEnd(); long windowEnd = context.window().getEnd();
long count = iterable.iterator().next(); //取出增量聚合函数的一个元素 long count = iterable.iterator().next(); //取出增量聚合函数的一个元素
collector.collect("用户:"+key+"在窗口" collector.collect("用户:"+key+"在窗口"
+""+new Timestamp(windowStart)+"~"+new Timestamp(windowEnd) +""+new Timestamp(windowStart)+"~"+new Timestamp(windowEnd)
+""+"中的pv次数是"+count); +""+"中的pv次数是"+count);

View File

@ -33,7 +33,7 @@ public class Example7 {
env.setParallelism(1); env.setParallelism(1);
env env
.readTextFile("E:\\Big_data_example\\Flink\\src\\main\\resources\\UserBehavior.csv") .readTextFile("D:\\example\\self_example\\Big_data_example\\Flink\\src\\main\\resources\\UserBehavior.csv")
.map(new MapFunction<String, UserBehavior>() { .map(new MapFunction<String, UserBehavior>() {
@Override @Override
public UserBehavior map(String value) throws Exception { public UserBehavior map(String value) throws Exception {

View File

@ -54,7 +54,7 @@ public class Example4 {
orderStream.keyBy(r ->r.userId) orderStream.keyBy(r ->r.userId)
.intervalJoin(pvStream.keyBy(r -> r.userId)) .intervalJoin(pvStream.keyBy(r -> r.userId))
//第一条流和第二条流的哪一段join //第一条流和第二条流的哪一段join
//最近10min和未来10min以内的 //最近10min和未来5min以内的
.between(Time.minutes(-10),Time.minutes(5)) .between(Time.minutes(-10),Time.minutes(5))
.process(new ProcessJoinFunction<Event, Event, String>() { .process(new ProcessJoinFunction<Event, Event, String>() {
@Override @Override
@ -67,7 +67,7 @@ public class Example4 {
pvStream.keyBy(r ->r.userId) pvStream.keyBy(r ->r.userId)
.intervalJoin(orderStream.keyBy(r -> r.userId)) .intervalJoin(orderStream.keyBy(r -> r.userId))
//第一条流和第二条流的哪一段join //第一条流和第二条流的哪一段join
//最近10min和未来10min以内的 //最近5min和未来10min以内的
.between(Time.minutes(-5),Time.minutes(10)) .between(Time.minutes(-5),Time.minutes(10))
.process(new ProcessJoinFunction<Event, Event, String>() { .process(new ProcessJoinFunction<Event, Event, String>() {
@Override @Override

View File

@ -0,0 +1,64 @@
package day07;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.redis.RedisSink;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommand;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommandDescription;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisMapper;
/**
* @BelongsProject: Flink
* @BelongsPackage: day07
* @Author: dingjiawen
* @CreateTime: 2022-09-06 18:42
* @Description: TODO flink连接redis写入redis
* @Version: 1.0
*/
public class Example3 {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
DataStreamSource<Tuple2<String, Integer>> stream = env.fromElements(
Tuple2.of("key", 1),
Tuple2.of("key", 2)
);
FlinkJedisPoolConfig conf = new FlinkJedisPoolConfig.Builder().setHost("localhost").build();
stream.addSink(new RedisSink<Tuple2<String,Integer>>(conf,new MyRedisMapper()));
env.execute();
}
public static class MyRedisMapper implements RedisMapper<Tuple2<String,Integer>> {
@Override
public RedisCommandDescription getCommandDescription() {
//一个参数是操作符第二个值是表名
return new RedisCommandDescription(RedisCommand.HSET,"tuple");
}
@Override
public String getKeyFromData(Tuple2<String, Integer> in) {
return in.f0;
}
@Override
public String getValueFromData(Tuple2<String, Integer> in) {
return in.f1.toString();
}
}
}

View File

@ -0,0 +1,93 @@
package day07;
import org.apache.flink.api.common.functions.RuntimeContext;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* @BelongsProject: Flink
* @BelongsPackage: day07
* @Author: dingjiawen
* @CreateTime: 2022-09-06 19:04
* @Description: TODO 实现一个mysql的幂等写入
* @Version: 1.0
*/
//create table kv(k varchar(10),v int)
public class Example4 {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
DataStreamSource<Tuple2<String, Integer>> stream = env.fromElements(
Tuple2.of("key", 1),
Tuple2.of("key", 2)
);
stream.addSink(new RichSinkFunction<Tuple2<String, Integer>>() {
private Connection conn;
private PreparedStatement insertStmt;
private PreparedStatement updateStmt;
//生命周期开始创建mysql的连接
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/temp",
"root2",
"root2"
);
insertStmt = conn.prepareStatement("INSERT INTO kv (k, v) value(?,?)" );
updateStmt = conn.prepareStatement("UPDATE kv SET v = ? WHERE k=?");
}
//每来一次数据就会执行一次
@Override
public void invoke(Tuple2<String, Integer> value, Context context) throws Exception {
super.invoke(value, context);
//保证幂等性的操作:存在key就修改,不存在才插入
updateStmt.setInt(1, value.f1);
updateStmt.setString(2, value.f0);
updateStmt.execute();
if (updateStmt.getUpdateCount() == 0) {
insertStmt.setString(1, value.f0);
insertStmt.setInt(2, value.f1);
insertStmt.execute();
}
}
//生命周期结束,关闭mysql的连接
@Override
public void close() throws Exception {
super.close();
insertStmt.close();
updateStmt.close();
conn.close();
}
});
env.execute();
}
}

View File

@ -28,7 +28,7 @@ public class Example10 {
env.setParallelism(1); env.setParallelism(1);
SingleOutputStreamOperator<UserBehavior> stream = env SingleOutputStreamOperator<UserBehavior> stream = env
.readTextFile("E:\\Big_data_example\\Flink\\src\\main\\resources\\UserBehavior.csv") .readTextFile("D:\\example\\self_example\\Big_data_example\\Flink\\src\\main\\resources\\UserBehavior.csv")
.map( .map(
new MapFunction<String, UserBehavior>() { new MapFunction<String, UserBehavior>() {
@Override @Override

View File

@ -0,0 +1,119 @@
package day08.selftry;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import java.sql.Timestamp;
import static org.apache.flink.table.api.Expressions.$;
/**
* @BelongsProject: Flink
* @BelongsPackage: day08.selftry
* @Author: dingjiawen
* @CreateTime: 2022-09-07 16:46
* @Description: TODO example10的自己尝试 pv的Top10
* @Version: 1.0
*/
public class Example10 {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
SingleOutputStreamOperator<UserBehavior> stream = env.readTextFile("D:\\example\\self_example\\Big_data_example\\Flink\\src\\main\\resources\\UserBehavior.csv")
.map(new MapFunction<String, UserBehavior>() {
@Override
public UserBehavior map(String s) throws Exception {
String[] array = s.split(",");
return new UserBehavior(array[0], array[1], array[2], array[3], Long.parseLong(array[4]));
}
})
.filter(userBehavior -> userBehavior.behavior.equals("pv"))
.assignTimestampsAndWatermarks(
WatermarkStrategy.<UserBehavior>forMonotonousTimestamps()
.withTimestampAssigner(new SerializableTimestampAssigner<UserBehavior>() {
@Override
public long extractTimestamp(UserBehavior userBehavior, long l) {
return userBehavior.timeStamp;
}
})
);
//注册表的环境
EnvironmentSettings settings = EnvironmentSettings.newInstance().inStreamingMode().build();
StreamTableEnvironment tableEnvironment = StreamTableEnvironment.create(env, settings);
Table table = tableEnvironment.fromDataStream(
stream,
$("userId"),
$("itemId"),
$("categoryId"),
$("behavior"),
$("timeStamp").rowtime().as("ts")
);
tableEnvironment.createTemporaryView("userBehavior",table);
String countsql="select itemId,COUNT(itemId) as cnt,HOP_END(ts , INTERVAL '5' MINUTE ,INTERVAL '1' HOUR) as end_time " +
"from userBehavior group by itemId,HOP(ts,INTERVAL '5' MINUTE,INTERVAL '1' HOUR)";
//开窗求rank
String ranksql="select *,ROW_NUMBER() OVER(PARTITION BY end_time ORDER BY cnt DESC) as rk " +
"FROM ("+countsql+")";
//取前三名
String resultSql = "select * from ("+ranksql+") WHERE rk<=3";
Table result = tableEnvironment.sqlQuery(resultSql);
tableEnvironment.toChangelogStream(result).print();
env.execute();
}
/**
* 用户行为POJO类
*/
public static class UserBehavior {
public String userId;
public String itemId;
public String categoryId;
public String behavior;
public Long timeStamp;
public UserBehavior() {
}
public UserBehavior(String userId, String itemId, String categoryId, String behavior, Long timeStamp) {
this.userId = userId;
this.itemId = itemId;
this.categoryId = categoryId;
this.behavior = behavior;
this.timeStamp = timeStamp;
}
@Override
public String toString() {
return "UserBehavior{" +
"userId='" + userId + '\'' +
", itemId='" + itemId + '\'' +
", categoryId='" + categoryId + '\'' +
", behavior='" + behavior + '\'' +
", timeStamp=" + new Timestamp(timeStamp) +
'}';
}
}
}

View File

@ -13,8 +13,8 @@ import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectIn
* 1.实现接口或者继承类 * 1.实现接口或者继承类
* 2.重写相应的方法 * 2.重写相应的方法
* 3.打包 * 3.打包
* * <p>
* * <p>
* 自定义UDF函数类 * 自定义UDF函数类
* 继承Hive提供的GenericUDF类 * 继承Hive提供的GenericUDF类
*/ */
@ -22,6 +22,7 @@ public class CalStringLengthUDF extends GenericUDF {
/** /**
* 初始化方法 * 初始化方法
*
* @param objectInspectors 传入到函数中的参数对应的类型的鉴别器对象 * @param objectInspectors 传入到函数中的参数对应的类型的鉴别器对象
* @return 指定函数的返回值类型对象的鉴别器对象 * @return 指定函数的返回值类型对象的鉴别器对象
* @throws UDFArgumentException * @throws UDFArgumentException
@ -34,7 +35,7 @@ public class CalStringLengthUDF extends GenericUDF {
} }
//2.校验函数的参数类型,getCategory()返回的是传入的类型,PRIMITIVE表示基本类型 //2.校验函数的参数类型,getCategory()返回的是传入的类型,PRIMITIVE表示基本类型
if (!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) { if (!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {
throw new UDFArgumentTypeException(0,"磺酸钠会参数类型不正确"); throw new UDFArgumentTypeException(0, "函数的参数类型不正确");
} }
//3.返回函数的返回值类型对应的鉴别器类型 //3.返回函数的返回值类型对应的鉴别器类型
return PrimitiveObjectInspectorFactory.javaIntObjectInspector; return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
@ -42,6 +43,7 @@ public class CalStringLengthUDF extends GenericUDF {
/** /**
* 函数核心处理方法 * 函数核心处理方法
*
* @param deferredObjects 传入到函数的参数 * @param deferredObjects 传入到函数的参数
* @return 函数的返回值 * @return 函数的返回值
* @throws HiveException * @throws HiveException
@ -58,6 +60,7 @@ public class CalStringLengthUDF extends GenericUDF {
/** /**
* 用于以后sql函数需要显示哪些内容 * 用于以后sql函数需要显示哪些内容
*
* @param strings * @param strings
* @return * @return
*/ */

View File

@ -25,7 +25,10 @@ object Spark010_RDD_Operator_Transform {
//(null.null) =>null null //(null.null) =>null null
//(1,null) =>1 //(1,null) =>1
val rdd1: RDD[Int] = rdd.distinct() val rdd1: RDD[Int] = rdd.distinct()
val rdd2=rdd.map(x=>(x,null)).reduceByKey((x, _) => x, 7)
rdd1.collect().foreach(println(_)) rdd1.collect().foreach(println(_))
println("==========================")
rdd2.collect().foreach(println(_))
sc.stop() sc.stop()
} }

View File

@ -39,6 +39,27 @@ object Spark01_RDD_Operator_Transform_Par {
} }
) )
mapRDD1.collect() mapRDD1.collect()
/*
结果:
>>>>>>1
>>>>>>3
######1
######3
>>>>>>2
######2
>>>>>>4
######4
第二次:
>>>>>>3
>>>>>>1
######1
######3
>>>>>>2
>>>>>>4
######4
######2
TODO 即分区内的1和2一定是先1后2分区内的3和4一定是先3后4但是1和3谁先不一定
*/
sc.stop() sc.stop()
} }

View File

@ -113,7 +113,7 @@ object SparkStreaming11_Req1_blackList1 {
println(s"${day} ${user} ${ad} ${sum}") println(s"${day} ${user} ${ad} ${sum}")
if(sum >= 30){ if(sum >= 30){
//拉入黑名单 //拉入黑名单
val conn = JDBCUtil.getConnection // val conn = JDBCUtil.getConnection
val sql = val sql =
""" """
|insert into black_list (userid) values (?) |insert into black_list (userid) values (?)
@ -126,7 +126,7 @@ object SparkStreaming11_Req1_blackList1 {
//将当天的广告点击数量进行更新 //将当天的广告点击数量进行更新
//查询统计表数据 //查询统计表数据
val conn = JDBCUtil.getConnection // val conn = JDBCUtil.getConnection
val sql = val sql =
""" """
|select * |select *

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.markilue.java_learning</groupId>
<artifactId>java_learning</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.projectlombok</groupId>-->
<!-- <artifactId>lombok</artifactId>-->
<!-- <version>RELEASE</version>-->
<!--&lt;!&ndash; <scope>compile</scope>&ndash;&gt;-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,96 @@
package com.markilue.java_learning.annotationTest;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.annotationTest
* @Author: dingjiawen
* @CreateTime: 2022-09-22 18:58
* @Description:
* TODO 测试注解:
* 1)一个完整的注解有三个部分
* 1.注解的声明就如同类方法变量等一样需要先声明后使用
* 2.注解的使用用于注解在包方法属性构造局部变量等上面的10个位置中一个或多个位置
* 3.注解的读取有一段专门用来读取这些使用的注解然后根据注解信息作出相应的处理这段程序称为注解处理流程这也是注解区别与普通注释最大的不同
* 2)系统预定义的三个最基本的注解:
* 1@Override:
* 用于检测被修饰的方法为有效的重写方法如果不是则报编译错误!
* 只能标记在方法上
* 它会被编译器程序读取
* 2.@Deprecated:
* 用于表示被标记的数据已经过时不建议使用
* 可以用于修饰 属性方法构造局部变量参数
* 它会被编译器程序读取
* 3.@SuppressWarnings:
* 抑制编译警告
* 可以用于修饰类属性方法构造局部变量参数
* 它会被编译器程序读取
* @Version: 1.0
*/
public class AnnotationTest {
@SuppressWarnings({"unused","rawtypes", "unchecked"})
@Test
public void test(){
int i;
//可以发现:这里没有声明List<T>类型,仍然没有报错
List list = new ArrayList();
list.add("");
list.add(123);
list.add("");
Father f = new Son();
f.show();
f.methodOl();
}
}
class Father{
@Deprecated
public void show() {
}
public void methodOl() {
System.out.println("Father Method");
}
public void print1n(){
System.out.println("Father Method");
}
public int sum(int... nums){
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
}
class Son extends Father{
/* @Override
public void method01() {
System.out.println("Son Method");
}
@Override
public void println(){
System.out.println("Father Method");
}
@Override
public long sum(int[] nums){
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}*/
}

View File

@ -0,0 +1,92 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-18 14:34
* @Description:
* TODO collection的addAll,add,retainAll方法
* 1addAll(另一个集合)方法会将单个元素都加进去
* 2add(另一个集合)方法会将其作为一个整体加入
* 3retainAll(另一个集合)方法会将两个集合的交集保留下来,其他的丢弃 =>注意不是返回一个新集合,而是改变原来的集合
* @Version: 1.0
*/
public class CollectionCommonFunction {
//addAll
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(1);
coll.add(2);
System.out.println("coll集合元素的个数" + coll.size()); //2
Collection other = new ArrayList();
other.add(1);
other.add(2);
other.add(3);
coll.addAll(other);
// coll.add(other);
System.out.println("coll集合元素的个数" + coll.size()); //5
for (Object o : coll) {
System.out.println(o);// 1 2 1 2 3
}
}
//add
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(1);
coll.add(2);
System.out.println("coll集合元素的个数" + coll.size()); //2
Collection other = new ArrayList();
other.add(1);
other.add(2);
other.add(3);
coll.add(other);
// coll.add(other);
System.out.println("coll集合元素的个数" + coll.size()); //3
for (Object o : coll) {
System.out.println(o);// 1 2 [1 2 3]
}
}
//retainAll
@Test
public void test5(){
Collection coll = new ArrayList();
coll.add(1);
coll.add(2);
coll.add(3);
coll.add(4);
coll.add(5);
System.out.println("coll集合元素的个数" + coll.size());//5
Collection other = new ArrayList();
other.add(1);
other.add(2);
other.add(8);
coll.retainAll(other);//保留交集
System.out.println("coll集合元素的个数" + coll.size());//2
for (Object o : coll) {
System.out.println(o); //1 2
}
}
}

View File

@ -0,0 +1,99 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.HashMap;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-19 17:03
* @Description: TODO 测试HashMap的树化和反树化
* @Version: 1.0
*/
public class HashMapTest {
@Test
public void test1(){
//这里为了演示的效果我们造一个特殊的类这个类的hashCode方法返回固定值1
//因为这样就可以造成冲突问题使得它们都存到table[1]
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);//树化演示
}
}
@Test
public void test2(){
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);
}
for (int i = 1; i <=11; i++) {
map.remove(new MyKey(i));//反树化演示
}
}
@Test
public void test3(){
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);
}
for (int i = 1; i <=5; i++) {
map.remove(new MyKey(i));
}//table[1]下剩余6个结点
for (int i = 21; i <= 100; i++) {
map.put(new MyKey(i), "value"+i);//添加到扩容时反树化
}
}
}
class MyKey{
int num;
public MyKey(int num) {
super();
this.num = num;
}
@Override
public int hashCode() {
//容量小于20全放一个里面
if(num<=20){
return 1;
}else{
//大于30之后尽可能分开
final int prime = 31;
int result = 1;
result = prime * result + num;
return result;
}
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyKey other = (MyKey) obj;
if (num != other.num)
return false;
return true;
}
}

View File

@ -0,0 +1,110 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-18 14:50
* @Description:
* TODO iterator是一个可迭代接口,collection对象都实现了这个接口(java.util.Iterator)
* 1)其使用方法是:首先通过调用集合的iterator()方法获得迭代器对象然后使用hashNext()方法判断集合中是否存在下一个元素如果存在则调用next()方法将元素取出否则说明已到达了集合末尾停止遍历元素
* 内部是通过迭代器获取索引实现的:在调用Iterator的next方法之前迭代器的索引位于第一个元素之前不指向任何元素当第一次调用迭代器的next方法后迭代器的索引会向后移动一位指向第一个元素并将该元素返回当再次调用next方法时迭代器的索引会指向第二个元素并将该元素返回依此类推直到hasNext方法返回false表示到达了集合的末尾终止对元素的遍历
* 2)可以通过iterable对collection里的元素进行删除,相比于.remove()方法的好处是通过iterable的方式可以加上条件判断 =>使用方式iterator.remove
* 3)未实现java.util.Iterator的类如果想实现foreach等增强for循环的方式可以去实现java.lang.Iterable接口,具体方式见MyArrayList类
* 4)快速失败fail-fast机制: 在调用iterator方法获取迭代器操作时调用迭代器自身以外的方法对集合中的结构进行改变,如调用list.add()或remove()方法否则会报错ConcurrentModificationException
* 注意迭代器的快速失败行为不能得到保证一般来说存在不同步的并发修改时不可能作出任何坚决的保证快速失败迭代器尽最大努力抛出 `ConcurrentModificationException`
* 因此编写依赖于此异常的程序的方式是错误的正确做法是迭代器的快速失败行为应该仅用于检测 bug
* 实现方式:1)在ArrayList等集合类中都有一个modCount变量它用来记录集合的结构被修改的次数
* 2)当我们给集合添加和删除操作时会导致modCount++
* 3)然后当我们用Iterator迭代器遍历集合时创建集合迭代器的对象时用一个变量记录当前集合的modCount
* 例如`int expectedModCount = modCount;`并且在迭代器每次next()迭代元素时都要检查 `expectedModCount != modCount`
* 如果不相等了那么说明你调用了Iterator迭代器以外的Collection的add,remove等方法修改了集合的结构使得modCount++值变了就会抛出ConcurrentModificationException
* @Version: 1.0
*/
public class Iterable<E> {
//测试iterator.remove方法
@Test
public void test02(){
Collection<String> coll = new ArrayList<>();
coll.add("陈琦");
coll.add("李晨");
coll.add("邓超");
coll.add("黄晓明");
//删除名字有三个字的
// coll.remove(o)//无法编写
Iterator<String> iterator = coll.iterator();
while(iterator.hasNext()){
String element = iterator.next();
if(element.length()==3){
// coll.remove(element);//错误的
iterator.remove();
}
}
System.out.println(coll);
}
//测试继承了java.lang.Iterator的MyArrayList类
@Test
public void test(){
MyArrayList<String> my = new MyArrayList<String>();
my.add("hello");
my.add("java");
my.add("world");
my.add("atguigu");
my.add("list");
my.add("data");
for (String string : my) {
System.out.println(string);
}
}
@Test
public void test01() {
Collection<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("atguigu");
list.add("world");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
list.remove(iterator.next());
}
}
@Test
public void test03() {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("atguigu");
list.add("world");
//以下代码没有发生ConcurrentModificationException异常
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if("atguigu".endsWith(str)){
list.remove(str);
}
}
}
}

View File

@ -0,0 +1,88 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-18 15:57
* @Description:
* TODO List的实现类:ArrayList,Vector,LinkedList
* 1)ArrayList:
* 1.数据存储的结构是数组结构元素增删慢查找快;
* 2.发生扩容时ArrayList扩容为原来的1.5倍;
* 3.初始化为长度为0的空数组之后在添加第一个元素时再创建长度为10的数组
* 2)Vector:
* 1.底层也用的是数组结构,线程安全效率低
* 2.发生扩容时Vector扩容为原来的2倍;
* 3.初始容量默认为10
* 4.支持Enumeration 迭代器但是该迭代器不支持快速失败面对并发的修改迭代器很快就完全失败
* 3)LinkedList:
* 1.数据存储的结构是链表结构(双向链表)方便元素添加删除的集合
* 2.LinkedList还实现了Deque接口为在列表的开头及结尾 getremove insert 元素提供了统一的命名方法这些操作允许将链接列表用作堆栈队列或双端队列
* 3.Stack与LinkedList都能作为栈结构对外表现的功能效果是一样但是它们的物理结构不同Stack的物理结构是顺序结构的数组而LinkedList的物理结构是链式结构的双向链表我们推荐使用LinkedList
* 4.每种方法都存在两种形式一种形式在操作失败时抛出异常(addremove,get)另一种形式返回一个特殊值null false具体取决于操作(offer,poll,peek)
*
* 4)ListTest 集合额外提供了一个 listIterator() 方法该方法返回一个 ListIterator 对象 ListIterator 接口继承了 Iterator 接口提供了专门操作 ListTest 的方法
* @Version: 1.0
*/
public class ListTest {
//LinkedList作为栈;使用addFirst和removeFirst方法
@Test
public void test(){
LinkedList<Integer> list = new LinkedList<>();
//入栈
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
//出栈 LIFO后进先出
System.out.println(list.removeFirst());//3
System.out.println(list.removeFirst());//2
System.out.println(list.removeFirst());//1
//栈空了会报异常java.util.NoSuchElementException
System.out.println(list.removeFirst());
}
//LinkedList作为队列;使用addLast和pollFirst方法
@Test
public void test1(){
LinkedList<Integer> list = new LinkedList<>();
//入队
list.addLast(1); //或者使用offerLast
list.addLast(2);
list.addLast(3);
//出队 FIFO先进先出
System.out.println(list.pollFirst());//1 //或者使用removeFirst
System.out.println(list.pollFirst());//2
System.out.println(list.pollFirst());//3
//队空了返回null
System.out.println(list.pollFirst());//null
}
//测试List中的listIterator迭代器方法
@Test
public void test2(){
List<String> c = new ArrayList<String>();
c.add("张三");
c.add("李四");
c.add("王五");
//从指定位置往前遍历
ListIterator<String> listIterator = c.listIterator(c.size());
while(listIterator.hasPrevious()){
String previous = listIterator.previous();
System.out.println(previous);
}
}
}

View File

@ -0,0 +1,166 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-19 15:35
* @Description:
* TODO Map及其实现类:HashMapHashTableLinkedHashMapTreeMapProperties
* 1)Map:
* 1.这些实现类均继承自java.util.Map<key,value>接口
* 2.使用put方法时若指定的键(key)在集合中没有则没有这个键对应的值返回null并把指定的键值添加到集合中
* 若指定的键(key)在集合中存在则返回值为集合中键对应的值该值为替换前的值并把指定键所对应的值替换成指定的新值
* 3.Map的遍历不能支持foreach因为Map接口没有继承java.lang.Iterable<T>接口也没有实现Iterator iterator()方法
* 但是可以分别遍历其key(keySet()方法)和value(values方法),也可以成对遍历<key,value>(entrySet()方法)
* 4.Map.Entry是Map接口的内部接口每一种Map内部有自己的Map.Entry的实现类
* 在Map中存储数据实际上是将Key---->value的数据存储在Map.Entry接口的实例中再在Map集合中插入Map.Entry的实例化对象
* 2)HashMap:
* 1.底层是哈希表 数组加链表 JDK1.7散列表table[index]也称为bucket桶,其中table是一个HashMap.Entry<Key,value>是一个链表,HashMap.Entry中还存放着next指针存放相同hashcode值的元素
* JDK1.8封装为HashMap.Node类型或HashMap.TreeNode类型它俩都直接或间接的实现了Map.Entry接口即table[index]下的映射关系可能串起来一个链表或一棵红黑树自平衡的二叉树
* 链表长度大于8(且总容量大于等于64,不大的话会优先扩容而不是树化)转为红黑树一种解释是节点规律服从泊松分布经过计算得出这个值会比较好,树节点数小于6退化为链表
* 2.评价两个key是否相等的指标是其hashcode相等,且equal方法返回true,所以存放的对象必须实现hashcode和equal方法
* 3.线程不安全的并允许使用 null 值和 null
* 4.默认初始化长度1<<4 即16(一定是2的倍数).每次扩容变为原来的两倍未避免分布不均匀增加了扰动计算
* 5.哈希算法:使用位运算代替取模运算(前提:容量length一定是2^n) 源码hashcode&(length-1) X %2^n=X &(2^n-1)
* 3)HashTable:
* 1.底层实现是哈希表
* 2.评价两个key是否相等的指标是其hashcode相等,且equal方法返回true,所以存放的对象必须实现hashcode和equal方法
* 3.线程安全的任何非 null 对象都可以用作键或值
* 4.默认初始化长度11(素数)内次扩容变为原来的2n+1倍,扩容结果也为奇数素数使得节点取模更加均匀分散效果越好
* 5.哈希算法:在保证是正数的情况下直接取模 源码:(hashcode & 0x7FFFFFFF)%table.length
* 4)LinkedHashMap:
* 1.LinkedHashMap是HashMap的子类
* 2.与HashMap的不同在于:前者维护着一个运行于所有条目的双重链接列表此链接列表定义了迭代顺序该迭代顺序通常就是将键插入到映射中的顺序插入顺序
* 5)TreeMap:
* 1.基于红黑树Red-Black tree NavigableMap 实现
* 2.该映射根据其键的自然顺序进行排序或者根据创建映射时提供的 Comparator 进行排序具体取决于使用的构造方法
* 6)Properties:
* 1.Properties 类是 Hashtable 的子类Properties 可保存在流中或从流中加载属性列表中每个键及其对应值都是一个字符串
* 2.存取数据时建议使用setProperty(String key,String value)方法和getProperty(String key)方法
* 7)HashSet与Map的关系
* 1.Set的内部实现其实是一个Map即HashSet的内部实现是一个HashMapTreeSet的内部实现是一个TreeMapLinkedHashSet的内部实现是一个LinkedHashMap
* 2.Set中只有一个元素又是怎么变成(key,value)的呢
* 源码实现private static final Object PRESENT = new Object();
* public boolean add(E e) {
* return map.put(e, PRESENT)==null;
* }
* public Iterator<E> iterator() {
* return map.keySet().iterator();
* }
* @Version: 1.0
*/
public class MapTest {
//HashMap的put覆盖测试和可以存放null值测试
@Test
public void test(){
HashMap<String,Double> map = new HashMap<>();
map.put("张三", 10000.0);
//key相同新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set<Map.Entry<String, Double>> entrySet = map.entrySet();
for (Map.Entry<String, Double> entry : entrySet) {
System.out.println(entry);//null=null;李四=14000.0;张三=12000.0
}
}
//LinkedHashMap的插入顺序测试
@Test
public void test1(){
LinkedHashMap<String,Double> map = new LinkedHashMap<>();
map.put("张三", 10000.0);
//key相同新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set<Map.Entry<String, Double>> entrySet = map.entrySet();
for (Map.Entry<String, Double> entry : entrySet) {
System.out.println(entry); //张三=12000.0;李四=14000.0;null=null
}
}
//TreeMap自然顺序比较
@Test
public void test2() {
TreeMap<String,Integer> map = new TreeMap<>();
map.put("Jack", 11000);
map.put("Alice", 12000);
map.put("zhangsan", 13000);
map.put("baitao", 14000);
map.put("Lucy", 15000);
//String实现了Comparable接口默认按照Unicode编码值排序
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println(entry);
/*
Alice=12000
Jack=11000
Lucy=15000
baitao=14000
zhangsan=13000
*/
}
}
//TreeMap实现comparator比较器顺序
@Test
public void test3() {
//指定定制比较器Comparator按照Unicode编码值排序但是忽略大小写
TreeMap<String,Integer> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
map.put("Jack", 11000);
map.put("Alice", 12000);
map.put("zhangsan", 13000);
map.put("baitao", 14000);
map.put("Lucy", 15000);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println(entry);
/*
Alice=12000
baitao=14000
Jack=11000
Lucy=15000
zhangsan=13000
*/
}
}
//properties方法测试
@Test
public void test4(){
// Properties properties1 = new Properties();
// properties1.setProperty()
Properties properties = System.getProperties();
String p2 = properties.getProperty("file.encoding");//当前源文件字符编码
System.out.println(p2);
}
}

View File

@ -0,0 +1,57 @@
package com.markilue.java_learning.collection;
import java.util.Arrays;
import java.util.Iterator;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-18 15:13
* @Description: TODO 测试实现java.lang.Iterator类实现增强for
* @Version: 1.0
*/
public class MyArrayList<E> implements java.lang.Iterable<E> {
private Object[] array;
private int size;
public MyArrayList() {
array = new Object[5];
}
public void add(E e) {
ensureCapacityEnough();
array[size++] = e;
}
public void ensureCapacityEnough() {
if (size >= array.length) {
array = Arrays.copyOf(array, array.length * 2);
}
}
@Override
public Iterator<E> iterator() {
return new Iter();
}
private class Iter implements Iterator<E> {
int cursor;
@Override
public boolean hasNext() {
return cursor <= size;
}
@Override
public E next() {
return (E) array[cursor++];
}
}
}

View File

@ -0,0 +1,35 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-23 16:38
* @Description:
* TODO 面试题中的一些集合类题
* @Version: 1.0
*/
public class Problem {
@Test
public void test(){
//注意这里可以不指定泛型不报错但是不能加上<>不然会报错
List list1=new ArrayList();
list1.add(0);
List list2=list1;
System.out.println(list1.get(0) instanceof Integer); //true
System.out.println(list2.get(0) instanceof Integer); //true
System.out.println(list1.get(0).getClass()); //class java.lang.Integer
System.out.println(list1.get(0).getClass().getName()); //java.lang.Integer
Class<?> aClass = list1.get(0).getClass();
String name = list1.get(0).getClass().getName();
}
}

View File

@ -0,0 +1,268 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-18 16:46
* @Description:
* TODO Set的三个实现类 HashSetLinkedHashSetTreeSet:
* Set接口是Collection的子接口set接口没有提供额外的方法但是比`Collection`接口更加严格了Set 集合不允许包含相同的元素如果试把两个相同的元素加入同一个 Set 集合中则添加操作失败
* 1)HashSet:
* 1.底层的实现其实是一个java.util.HashMap支持.底层物理实现是一个Hash表
* 2.HashSet 集合判断两个元素相等的标准两个对象通过 hashCode() 方法比较相等并且两个对象的 equals() 方法返回值也相等因此存储到HashSet的元素要重写hashCode和equals方法具体写法可以参照Employee类
* 2)LinkedHashSet
* 1.LinkedHashSet是HashSet的子类它在HashSet的基础上在结点中增加两个属性before和after维护了结点的前后添加顺序
* 2.`java.util.LinkedHashSet`它是链表和哈希表组合的一个数据存储结构LinkedHashSet插入性能略低于 HashSet但在迭代访问 Set 里的全部元素时有很好的性能
* 3)TreeSet:
* 1.底层结构里面维护了一个TreeMap都是基于红黑树实现的
* 2.特点不允许重复实现排序(自然排序或定制排序)
* 如何实现去重:如果使用的是自然排序则通过调用实现的compareTo方法;如果使用的是定制排序则通过调用比较器的compare方法
* 如何实现排序:
* 方式一自然排序:让待添加的元素类型实现Comparable接口并重写compareTo方法
* 方式二定制排序:创建Set对象时指定Comparator比较器接口并实现compare方法
* @Version: 1.0
*/
public class SetTest {
//测试将重写了hashcode和equal方法的类放入HashSet中
@Test
public void test() {
HashSet<Employee> set = new HashSet<>();
set.add(new Employee("张三", new Employee.MyDate(1990, 1, 1)));
//重复元素无法添加因为MyDate和Employee重写了hashCode和equals方法
set.add(new Employee("张三", new Employee.MyDate(1990, 1, 1)));
set.add(new Employee("李四", new Employee.MyDate(1992, 2, 2)));
for (Employee object : set) {
System.out.println(object);
}
}
//测试LinkedHashSet
@Test
public void test1() {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("张三");
set.add("李四");
set.add("王五");
set.add("张三");
System.out.println("元素个数:" + set.size()); //3
for (String name : set) {
System.out.println(name); //张三 李四 王五
}
}
//测试TreeSet的自然排序
@Test
public void test2() {
TreeSet<String> set = new TreeSet<>();
set.add("zhangsan"); //String它实现了java.lang.Comparable接口
set.add("zisi");
set.add("wangwu");
set.add("hangsan");
System.out.println("元素个数:" + set.size());
for (String str : set) {
System.out.println(str);
}
}
//测试TreeSet的定制排序
@Test
public void test3() {
TreeSet<Student> set = new TreeSet(new Comparator<Student>(){
@Override
public int compare(Student o1, Student o2) {
return o1.getId() - o2.getId();
}
});
set.add(new Student(3,"张三"));
set.add(new Student(1,"李四"));
set.add(new Student(2,"王五"));
set.add(new Student(3,"张三风"));
System.out.println("元素个数:" + set.size()); //3
for (Student stu : set) {
System.out.println(stu); //Student [id=1, name=李四];Student [id=2, name=王五];Student [id=3, name=张三]
}
}
}
class Employee {
private String name;
private MyDate birthday;
public Employee(String name, MyDate birthday) {
super();
this.name = name;
this.birthday = birthday;
}
public Employee() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((birthday == null) ? 0 : birthday.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (birthday == null) {
if (other.birthday != null)
return false;
} else if (!birthday.equals(other.birthday))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "姓名:" + name + ", 生日:" + birthday;
}
static class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
super();
this.year = year;
this.month = month;
this.day = day;
}
public MyDate() {
super();
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + day;
result = prime * result + month;
result = prime * result + year;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyDate other = (MyDate) obj;
if (day != other.day)
return false;
if (month != other.month)
return false;
if (year != other.year)
return false;
return true;
}
@Override
public String toString() {
return year + "-" + month + "-" + day;
}
}
}
class Student{
private int id;
private String name;
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//......这里省略了name属性的get/set
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}

View File

@ -0,0 +1,102 @@
package com.markilue.java_learning.enumTest;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.enumTest
* @Author: dingjiawen
* @CreateTime: 2022-09-22 18:43
* @Description:
* TODO 测试枚举类
* 1枚举类的要求和特点:
* 1.枚举类的常量对象列表必须在枚举类的首行因为是常量所以建议大写
* 2.如果常量对象列表后面没有其他代码那么可以省略否则不可以省略
* 3.编译器给枚举类默认提供的是private的无参构造如果枚举类需要的是无参构造就不需要声明写常量对象列表时也不用加参数
* 4.如果枚举类需要的是有参构造需要手动定义private的有参构造调用有参构造的方法就是在常量对象名后面加(实参列表)就可以
* 5.枚举类默认继承的是java.lang.Enum类因此不能再继承其他的类型
* 6.JDK1.5之后switch提供支持枚举类型case后面可以写枚举常量名
* 7.enumeration是线程安全的
* 2)枚举类的常用方法:
* 1.toString(): 默认返回的是常量名对象名可以继续手动重写该方法
* 2.name():返回的是常量名对象名 很少使用
* 3.ordinal():返回常量的次序号默认从0开始
* 4.values():返回该枚举类的所有的常量对象返回类型是当前枚举的数组类型是一个静态方法
* 5.valueOf(String name)根据枚举常量对象名称获取枚举对象
* 3)枚举类也是类可以实现接口可以同时实现多个接口可以统一实现也可以用匿名内部类的形式单独给某个常量对象实现抽象方法
* @Version: 1.0
*/
public class enumTest {
//枚举类常用方法测试
@Test
public void test(){
Season[] values = Season.values();
System.out.println(Season.SPRING.equals(1) ); //false
System.out.println(values[1].ordinal()); //1
for (int i = 0; i < values.length; i++) {
switch(values[i]){
case SPRING:
System.out.println(values[i]+":春暖花开,万物复苏"); //SPRING:春暖花开万物复苏
break;
case SUMMER:
System.out.println(values[i]+":百花争艳,郁郁葱葱");
break;
case AUTUMN:
System.out.println(values[i]+":菊桂飘香,百树凋零");
break;
case WINTER:
System.out.println(values[i]+":梅花独开,大地一色");
break;
}
}
}
@Test
public void test1(){
Season[] values = Season.values();
System.out.println(Season.SPRING.equals(1) ); //false
System.out.println(values[1].ordinal()); //1
for (int i = 0; i < values.length; i++) {
switch(values[i]){
case SPRING:
System.out.println(values[i]+":春暖花开,万物复苏"); //SPRING:春暖花开万物复苏
break;
case SUMMER:
System.out.println(values[i]+":百花争艳,郁郁葱葱");
break;
case AUTUMN:
System.out.println(values[i]+":菊桂飘香,百树凋零");
break;
case WINTER:
System.out.println(values[i]+":梅花独开,大地一色");
break;
}
}
}
}
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
//枚举类实现抽象方法测试
interface Windiness{
void wind();
}
enum Season1 implements Windiness{
SPRING,SUMMER(){
@Override
public void wind() {
System.out.println("刮台风");
}
},AUTUMN,WINTER;
@Override
public void wind() {
System.out.println("刮风");
}
}

View File

@ -0,0 +1,62 @@
package com.markilue.java_learning.innerclass;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.innerclass
* @Author: dingjiawen
* @CreateTime: 2022-09-22 17:12
* @Description:
* TODO 测试静态内部类:
* 1)和外部类一样它只是定义在外部类中的另一个完整的类结构
* 1.可以继承自己的想要继承的父类实现自己想要实现的父接口们和外部类的父类和父接口无关
* 2.可以在静态内部类中声明属性方法构造器等结构包括静态成员
* 3.可以使用abstract修饰因此它也可以被其他类继承
* 4.可以使用final修饰表示不能被继承
* 5.编译后有自己的独立的字节码文件只不过在内部类名前面冠以外部类名和$符号
* 2)和外部类不同的是它可以允许四种权限修饰符publicprotected缺省private
* 1.外部类只允许public或缺省的
* 3)只可以在静态内部类中使用外部类的静态成员哪怕是私有的
* 1.在静态内部类中不能使用外部类的非静态成员哦
* 4)在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象
*
* TODO
* 值得注意的是:其实严格的讲在James Gosling等人编著的The Java Language Specification静态内部类不是内部类而是类似于C++的嵌套类的概念
* 外部类仅仅是静态内部类的一种命名空间的限定名形式而已所以接口中的内部类通常都不叫内部类因为接口中的内部成员都是隐式是静态的例如Map.Entry
* @Version: 1.0
*/
public class StaticInnerClass {
static boolean Paddy;
public static void main(String args[]){
System.out.println(Paddy);
}
@Test
public void test(){
Outer2.Inner in= new Outer2.Inner(); //4)
in.inMethod(); //非静态方法可以通过new了对象以后使用
Outer2.Inner.inTest(); //静态方法可以直接使用
}
}
class Outer2{
private static int a = 1;
private int b = 2;
protected static class Inner{
static int d = 4;//可以
void inMethod(){
System.out.println("out.a = " + a);
// System.out.println("out.b = " + b);//错误的 3)
}
static void inTest(){
System.out.println("out.a = " + a);
}
}
}

View File

@ -0,0 +1,114 @@
package com.markilue.java_learning.innerclass;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.innerclass
* @Author: dingjiawen
* @CreateTime: 2022-09-22 16:49
* @Description:
* TODO 测试非静态内部类:
* 1)和外部类一样它只是定义在外部类中的另一个完整的类结构
* 1.可以继承自己的想要继承的父类实现自己想要实现的父接口们和外部类的父类和父接口无关
* 2.可以在非静态内部类中声明属性方法构造器等结构但是**不允许声明静态成员**但是可以继承父类的静态成员而且可以声明静态常量
* 3.可以使用abstract修饰因此它也可以被其他类继承
* 4.可以使用final修饰表示不能被继承
* 5.编译后有自己的独立的字节码文件只不过在内部类名前面冠以外部类名和$符号
* 2)和外部类不同的是它可以允许四种权限修饰符publicprotected缺省private
* 1.外部类只允许public或缺省的
* 3)还可以在非静态内部类中使用外部类的所有成员哪怕是私有的
* 4)在外部类的静态成员中不可以使用非静态内部类哦
* 1.就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样
* 5)在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象
* 1.因此在非静态内部类的方法中有两个this对象一个是外部类的this对象一个是内部类的this对象
* @Version: 1.0
*/
public class UnStaticInnerClass {
@Test
public void test(){
Outer out = new Outer();
Outer.Inner in= out.new Inner(); //第五点5
in.inMethod();
Outer.Inner inner = out.getInner();
inner.inMethod();
}
}
class Father{
protected static int c = 3;
}
class Outer{
private static int a = 1;
private int b = 2;
protected class Inner extends Father{
// static int d = 4;//错误 1的第2点
int b = 5;
void inMethod(){
System.out.println("out.a = " + a); //3)
System.out.println("out.b = " + Outer.this.b);
System.out.println("in.b = " + b);
System.out.println("father.c = " + c);
}
}
public static void outMethod(){
// Inner in = new Inner();//错误的 //4)
}
public Inner getInner(){
return new Inner();
}
}
//简单面试题
class Test1{
public Test1(){
Inner s1 = new Inner();
s1.a = 10;
Inner s2 = new Inner();
s2.a = 20;
Test1.Inner s3 = new Test1.Inner();
System.out.println(s3.a);
}
class Inner{
public int a = 5;
}
public static void main(String[] args) {
Test1 t = new Test1();
Inner r = t.new Inner();
System.out.println(r.a); //5 5
}
}
//高难面试题
class TestInner{
public static void main(String[] args){
Outer1.Inner in = new Sub();
in.method();//输出 hello inner
}
}
class Outer1 {
abstract class Inner{
abstract void method();
}
}
class Sub extends Outer1.Inner{ //注意这里是Outer1.Inner
//不写这个构造方法,上面的Outer1.Inner会报错
static Outer1 out = new Outer1();
Sub(){
out.super();
}
@Override
void method() {
System.out.println("hello inner");
}
}

View File

@ -0,0 +1,61 @@
package com.markilue.java_learning.innerclass;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.innerclass
* @Author: dingjiawen
* @CreateTime: 2022-09-22 18:03
* @Description:
* TODO 测试局部内部类:
* 1)和外部类一样它只是定义在外部类的`某个方法`中的另一个完整的类结构
* 1.可以继承自己的想要继承的父类实现自己想要实现的父接口们和外部类的父类和父接口无关
* 2.可以在局部内部类中声明属性方法构造器等结构但不包括静态成员除非是从父类继承的或静态常量
* 3.可以使用abstract修饰因此它也可以被同一个方法的在它后面的其他内部类继承
* 4.可以使用final修饰表示不能被继承
* 5.编译后有自己的独立的字节码文件只不过在内部类名前面冠以外部类名$符号编号
* 这里有编号是因为同一个外部类中不同的方法中存在相同名称的局部内部类
* 2)和成员内部类不同的是它前面不能有权限修饰符等
* 3)局部内部类如同局部变量一样有作用域
* 4)局部内部类中使用能访问外部类的静态还是非静态的成员取决于所在的方法
* 5)局部内部类中还可以使用所在方法的局部常量即用final声明的局部变量
* 1.JDK1.8之后如果某个局部变量在局部内部类中被使用了自动加final
* 2.为什么一定要使用final? 因为如果c不是final的那么method方法执行完method的栈空间就释放了那么c也就消失了,如果方法返回值中包含这个类的这个值则无法取到
* @Version: 1.0
*/
public class partInnerClass {
}
class Outer3{
private static int a = 1;
private int b = 2;
public static void outMethod(){
final int c = 3; //5)
class Inner{ //2)
public void inMethod(){
System.out.println("out.a = " + a);
// System.out.println("out.b = " + b);//错误的因为outMethod是静态的 //4)
System.out.println("out.local.c = " + c);
}
}
Inner in = new Inner();
in.inMethod();
}
public void outTest(){
final int c = 3;
class Inner{ //5)
public void inMethod(){
System.out.println("out.a = " + a);
System.out.println("out.b = " + b);//可以因为outTest是飞静态的
System.out.println("method.c = " + c);
}
}
Inner in = new Inner();
in.inMethod();
}
}

View File

@ -0,0 +1,199 @@
package com.markilue.java_learning.interfaceTest;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.interfaceTest
* @Author: dingjiawen
* @CreateTime: 2022-09-22 15:28
* @Description:
* TODO 测试java中的接口
* 1那么接口的内部主要就是 封装了方法包含抽象方法JDK 7及以前默认方法和静态方法JDK 8私有方法JDK 9
* 2接口中有多个抽象方法时实现类必须重写所有抽象方法如果抽象方法有重名的只需要重写一次
* 3当一个类既继承一个父类又实现若干个接口时父类中的成员方法与接口中的抽象方法重名子类就近选择执行父类的成员方法参见ABCD
* 4)当一个类同时实现了多个接口而多个接口中包含方法签名相同的默认方法时,必须要作出选择(可以重写 也可以选择一个继承)参见EFGH
* 5一个接口能继承另一个或者多个接口接口的继承也使用 `extends` 关键字子接口继承父接口的方法
* 1.子接口重写默认方法时default关键字可以保留
* 2.子类重写默认方法时default关键字不可以保留
* 6接口中其他成员特点
* 1.接口中无法定义成员变量但是可以定义常量其值不可以改变默认使用public static final修饰
* 2.接口中没有构造方法不能创建对象
* 3.接口中没有静态代码块
* 7接口类型的变量与实现类的对象之间也可以构成多态引用通过接口类型的变量调用方法最终执行的是你new的实现类对象实现的方法体
* 8常用接口:java.lang.Comparablejava.util.Comparatorjava.lang.Cloneable
* 1.java.lang.Comparable实现这个接口的类需要实现compareTo(Object obj)方法 从而实现与另一个对象的比较;
* 如果当前比obj大则返回一个正整数等于返回0小于返回负整数
* 2.java.util.Comparator实现这个接口的类需要实现compare(Object o1,Object o2)方法 当我们修改不了源码,又希望这类对象能做比较的时候使用;
* 如果当前比obj大则返回一个正整数等于返回0小于返回负整数
* 3.java.lang.Cloneable实现这个接口的类需要实现clone()方法可以实现造一个和当前对象各种属性值一模一样的对象当然地址肯定不同
* @Version: 1.0
*/
public class InterfaceTest {
//测试第三点3
@Test
public void test(){
C c = new C();
c.methodA();
B b = new B();
b.methodA();
/*
打印结果:
DDDDDDDDDDDD
BBBBBBBBBBBB
*/
}
//测试第七点7
@Test
public void test1(){
A c = new C();
c.methodA();
A b = new B();
b.methodA();
/*
打印结果:
DDDDDDDDDDDD
BBBBBBBBBBBB
*/
}
//测试第八点8)
@Test
public void test2() throws CloneNotSupportedException {
Teacher src = new Teacher(1,"柴老师");
Object clone = src.clone();
System.out.println(clone);
System.out.println(src == clone);
System.out.println(src.equals(clone));
System.out.println(new Object().getClass());
System.out.println(clone.getClass());
/*
结果:
Teacher [id=1, name=柴老师]
false
true
class java.lang.Object
class com.markilue.java_learning.interfaceTest.Teacher
*/
}
}
interface A {
public default void methodA(){
System.out.println("AAAAAAAAAAAA");
}
}
class D {
public void methodA(){
System.out.println("DDDDDDDDDDDD");
}
}
class C extends D implements A {
// 未重写methodA方法
}
class B extends D implements A{
//当然也可以选择重写
public void methodA(){
System.out.println("BBBBBBBBBBBB");
}
}
interface E{
public default void d(){
System.out.println("今晚7点-8点陪我吃饭看电影");
}
}
interface F{
public default void d(){
System.out.println("今晚7点-8点陪我逛街吃饭");
}
}
//选择保留其中一个通过接口名.super.方法名"的方法选择保留哪个接口的默认方法。
class G implements E,F{
@Override
public void d() {
E.super.d();
}
}
//选择自己完全重写
class H implements E,F{
@Override
public void d() {
System.out.println("自己待着");
}
}
class Teacher implements Cloneable{
private int id;
private String name;
public Teacher(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Teacher() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + "]";
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Teacher other = (Teacher) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

View File

@ -0,0 +1,80 @@
package com.markilue.java_learning.interfaceTest;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.interfaceTest
* @Author: dingjiawen
* @CreateTime: 2022-09-22 16:07
* @Description:
* TODO 接口常见面试题排错
* 1第一题参见ZXS类
* @Version: 1.0
*/
public class Problem {
}
interface Z{
int x=0;
}
class X{
int x=1;
}
class S extends X implements Z{
public void px(){
// System.out.println(x);
}
public static void main(String[] args) {
new S().px();
//这里直接报错了 com.markilue.java_learning.interfaceTest.X 中的变量 x com.markilue.java_learning.interfaceTest.Z 中的变量 x 都匹配
}
}
interface Playable{
void play();
}
interface Bounceable{
void play();
}
interface Rollable extends Playable,Bounceable{
Ball ball=new Ball("PingPang");
}
class Ball implements Rollable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Ball(String name) {
this.name = name;
}
public Ball() {
}
@Override
public void play() {
Ball football = new Ball("football");
System.out.println(ball.getName());
}
public static void main(String[] args) {
new Ball().play();//PingPang
}
}

View File

@ -0,0 +1,120 @@
package com.markilue.java_learning.mutiType;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.mutiType
* @Author: dingjiawen
* @CreateTime: 2022-09-20 17:35
* @Description:
* TODO 测试实例初始化
* 1)初始化顺序:
* 1super()或super(实参列表) 这里选择哪个看原来构造器首行是哪句没写默认就是super()
* 2非静态实例变量的显示赋值语句
* 3非静态代码块
* 4对应构造器中的代码
* 特别说明其中23是按顺序合并的1一定在最前面4一定在最后面
* 2)执行特点
* 1.创建对象时才会执行
* 2.调用哪个构造器就是指定它对应的实例初始化方法
* 3.创建子类对象时父类对应的实例初始化会被先执行执行父类哪个实例初始化方法看用super()还是super(实参列表)
* 3)非静态代码块和构造器
* 1.从某种程度上来看非静态代码块是对构造器的补充非静态代码块总是在构造器执行之前执行
* 与构造器不同的是非静态代码块是一段固定执行的代码它不能接收任何参数
* 因此非静态代码块对同一个类的所有对象所进行的初始化处理完全相同
* 基于这个原因不难发现非静态代码块的基本用法:如果有一段初始化处理代码对所有对象完全相同且无须接收任何参数就可以把这段初始化处理代码提取到非静态代码块中
* 2.即如果每个构造器中有相同的初始化代码且这些初始化代码无须接收参数就可以把它们放在非静态代码块中定义
* 通过把多个构造器中相同代码提取到非静态代码块中定义能更好地提高初始代码的复用提高整个应用的可维护性
* @Version: 1.0
*/
public class InitTest {
//测试子父类继承方法有重写时初始化顺序
@Test
public void test(){
Son s1 = new Son();
System.out.println("-----------------------------");
Son s2 = new Son("atguigu");
/*
运行结果
Son:getNumber() //子类重写getNumber()方法那么创建子类的对象就是调用子类的getNumber()方法因为当前对象this是子类的对象
Father(1)
Son:getNumber()
Father(2)
Father()无参构造
Son:getNumber()
Son(1)
Son:getNumber()
Son(2)
Son()无参构造
-----------------------------
Son:getNumber()
Father(1)
Son:getNumber()
Father(2)
Father(info)有参构造
Son:getNumber()
Son(1)
Son:getNumber()
Son(2)
Son(info)有参构造
*/
}
private class Father{
private int a = getNumber();
private String info;
{
System.out.println("Father(1)");
}
Father(){
System.out.println("Father()无参构造");
}
Father(String info){
this.info = info;
System.out.println("Father(info)有参构造");
}
private int b = getNumber();
{
System.out.println("Father(2)");
}
public int getNumber(){
System.out.println("Father:getNumber()");
return 1;
}
}
private class Son extends Father {
private int a = getNumber();
{
System.out.println("Son(1)");
}
private int b = getNumber();
{
System.out.println("Son(2)");
}
public Son(){
System.out.println("Son():无参构造");
}
public Son(String info){
super(info);
System.out.println("Son(info):有参构造");
}
public int getNumber(){
System.out.println("Son:getNumber()");
return 1;
}
}
}

View File

@ -0,0 +1,46 @@
package com.markilue.java_learning.mutiType;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.mutiType
* @Author: dingjiawen
* @CreateTime: 2022-09-20 16:50
* @Description: TODO 测试多态的特性:
* 1)属性没有多态
* 2)静态方法没有多态性
* 1.静态方法不能被重写
* 2.调用静态方法最好使用类名.
* @Version: 1.0
*/
public class StaticTest {
//测试静态方法没有多态性
@Test
public void test() {
Father f = new Son();
f.test();//只看编译时类型 =>father
}
//测试属性没有多态性
@Test
public void test1() {
Father f = new Son();
System.out.println(f.x);//只看编译时类型 =>1
}
}
class Father {
int x=1;
public static void test() {
System.out.println("father");
}
}
class Son extends Father {
int x=2;
public static void test() {
System.out.println("son");
}
}

View File

@ -0,0 +1,75 @@
package com.markilue.java_learning.mutiType;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.mutiType
* @Author: dingjiawen
* @CreateTime: 2022-09-20 17:43
* @Description:
* TODO 测试类初始化和实例初始化:
* 1)类初始化肯定优先于实例初始化
* 2)类初始化只做一次
* 3)实例初始化是每次创建对象都要进行
*
* @Version: 1.0
*/
public class clinitAndInitTest {
public static void main(String[] args) {
Son s1 = new Son();
System.out.println("----------------------------");
Son s2 = new Son();
/*
运行结果
Father:static
Son:static
Father:not_static
Father()无参构造
Son:not_static
Son()无参构造
----------------------------
Father:not_static
Father()无参构造
Son:not_static
Son()无参构造
*/
}
@Test
public void test(){
}
// private class Father{
// static{
// System.out.println("Father:static");
// }
// {
// System.out.println("Father:not_static");
// }
// Father(){
// System.out.println("Father()无参构造");
// }
// }
// private class Son extends Father{
// static{
// System.out.println("Son:static");
// }
// {
// System.out.println("Son:not_static");
// }
// Son(){
// System.out.println("Son()无参构造");
// }
// }
}

View File

@ -0,0 +1,125 @@
package com.markilue.java_learning.string;
import org.junit.Test;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.string
* @Author: dingjiawen
* @CreateTime: 2022-09-13 18:45
* @Description: TODO 解释string中的==和equal方法,以及string的intern()方法:
* 1)String类型不是基本类型,其构造方法有String s=;new String()/new String("")/new String(char[] a)
* 2String由于其不是基本类型因此使用构造方法new String(abc)创建对象其对象会放在堆中(对象引用放在栈里),"abc"存放在对象的value数组中(源码可知)
* 3使用String s=abc创建的String,由于abc是一个不可变常量(在常量池中只会维护一份,并且可以共享),因此"abc"会存在常量池中,s的对象引用也是指向常量池
* 4) String调用的equal()方法,本身是调用了Object类的equal()方法比较内存但是String类重写了equal()方法变成了比较值
* 4.1)String的intern()方法就是将string放入常量池中,并返回他的索引,如果在常量池中则直接返回他的索引
* 5)关于拼接和存储问题
* 1常量+常量结果是常量池
* 2常量与变量 变量与变量结果是堆
* 3拼接后调用intern方法结果在常量池
* 4在初始化时使用String s=new String("abc")+new String("def")进行拼接时不会马上解析只有当这个字面量s被调用或者abcdef被调用时,采用创建对应的String实例
* @Version: 1.0
*/
public class Equal {
@Test
public void test1() {
//参考23
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);//true
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str1 == str4); //false
System.out.println(str3 == str4); //false
}
@Test
public void test2() {
//参考5)的第三点
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = (s1 + "world").intern();//把拼接的结果放到常量池中
String s5 = (s1 + s2).intern();
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
}
@Test
public void test05() {
//参考5的第一点
//用final修饰存放在常量池中
final String s1 = "hello";
final String s2 = "world";
String s3 = "helloworld";
String s4 = s1 + "world";//s4字符串内容也helloworlds1是常量"world"常量常量+ 常量 结果在常量池中
String s5 = s1 + s2;//s5字符串内容也helloworlds1和s2都是常量常量+ 常量 结果在常量池中
String s6 = "hello" + "world";//常量+ 常量 结果在常量池中因为编译期间就可以确定结果
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true
}
@Test
public void test04() {
//参考5的第2点
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = s1 + "world";//s4字符串内容也helloworlds1是变量"world"常量变量 + 常量的结果在堆中
String s5 = s1 + s2;//s5字符串内容也helloworlds1和s2都是变量变量 + 变量的结果在堆中
String s6 = "hello" + "world";//常量+ 常量 结果在常量池中因为编译期间就可以确定结果
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//true
}
@Test
public void test03() {
//参考5的第4点 存疑?现在运行好像是false
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1 == s2);//false
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//true =>现在运行好像是false了?
}
@Test
public void test07() {
//参考5的第4点
String s1 = new String("mark") + new String("ilue"); //这一步会将mark和ilue放入常量池中
s1.intern(); //这里拼接的字符串markilue被调用,将markilue放入常量池中,这时才返回markilue索引给s1
String s2 = new StringBuilder("marki").append("lue").toString(); //这里将marki和lue放入常量池中,然后在toString中被调用,所以创建markilue的String对象(存放在堆中)然后返回这个堆索引
System.out.println(s1 == s2);//false 这里一个是返回的在堆对象中的索引一个是返回的在常量池中的索引,因此不相等
System.out.println(s1 == s2.intern());//true 这里调用intern()方法后获取到了在常量池中的索引因此两者相等
}
//面试题
private static final String MESSAGE = "taobao";
@Test
public void test08() {
String a = "tao" + "bao";
String b = "tao";
String c = "bao";
System.out.println(a == MESSAGE); //true
System.out.println((b + c) == MESSAGE); //false
}
}

View File

@ -0,0 +1,42 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-13 19:54
* @Description:
* TODO 守护线程:
* 有一种线程它是在后台运行的它的任务是为其他线程提供服务的这种线程被称为守护线程JVM的垃圾回收线程就是典型的守护线程
* 守护线程有个特点就是如果所有非守护线程都死亡那么守护线程自动死亡
* 调用setDaemon(true)方法可将指定线程设置为守护线程必须在线程启动之前设置否则会报IllegalThreadStateException异常
* 调用isDaemon()可以判断线程是否是守护线程
* @Version: 1.0
*/
public class Daemon {
public static void main(String[] args) {
MyDaemon m = new MyDaemon();
m.setDaemon(true);
m.start();
for (int i = 1; i <= 100; i++) {
System.out.println("main:" + i);
}
}
public static class MyDaemon extends Thread {
public void run() {
while (true) {
System.out.println("我一直守护者你...");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,84 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 17:27
* @Description:
* TODO 死锁问题:
* 不同的线程分别锁住对方需要的同步监视器对象不释放都在等待对方先放弃时就形成了线程的死锁
* 一旦出现死锁整个程序既不会发生异常也不会给出任何提示只是所有线程处于阻塞状态无法继续
* @Version: 1.0
*/
public class DeadLock {
public static void main(String[] args) {
Object g = new Object();
Object m = new Object();
//两个人彼此都需要对方的同步锁释放才能运行下面的同步锁内容,但是对方都没有释放锁的操作
Owner s = new Owner(g,m);
Customer c = new Customer(g,m);
new Thread(s).start();
new Thread(c).start();
/*
先给钱
先发货
*/
}
public static class Owner implements Runnable{
private Object goods;
private Object money;
public Owner(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (goods) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("先给钱");
synchronized (money) {
System.out.println("发货");
}
}
}
}
public static class Customer implements Runnable{
private Object goods;
private Object money;
public Customer(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (money) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("先发货");
synchronized (goods) {
System.out.println("再给钱");
}
}
}
}
}

View File

@ -0,0 +1,141 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 16:05
* @Description:
* TODO 线程之间的通信:
* 当线程之间彼此额操作具有先后顺序比如线程A做包子,线程B吃包子,需要在A做完以后B在做时两个线程之间需要怎么进行通信呢? ->等待唤醒机制
* 等待唤醒机制:就是在一个线程进行了规定操作后就进入等待状态wait() 等待其他线程执行完他们的指定代码过后 再将其唤醒notify();
* 在有多个线程进行等待时 如果需要可以使用 notifyAll()来唤醒所有的等待线程wait/notify 就是线程间的一种协作机制
* 1) wait线程不再活动不再参与调度进入 wait set 因此不会浪费 CPU 资源也不会去竞争锁了这时的线程状态即是 WAITING它还要等着别的线程执行一个特别的动作也即是通知notify在这个对象上等待的线程从wait set 中释放出来重新进入到调度队列ready queue
* 2) notify则选取所通知对象的 wait set 中的一个线程释放
* 3) notifyAll则释放所通知对象的 wait set 上的全部线程
* 需要注意的是:并不是notify以后就一定直接从wait的地方恢复运行,而是需要看是否存在同步锁的竞争问题,只有不存在或者其他线程的该同步锁代码运行完才恢复
* 如果能获取锁线程就从 WAITING 状态变成 RUNNABLE可运行 状态
* 否则线程就从 WAITING 状态又变成 BLOCKED等待锁 状态
* 还需要注意的是:
* 1. wait方法与notify方法必须要由同一个锁对象调用因为对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
* 2. wait方法与notify方法是属于Object类的方法的因为锁对象可以是任意对象而任意对象的所属类都是继承了Object类的
* 3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用因为必须要通过锁对象调用这2个方法
* @Version: 1.0
*/
public class ThreadCommunicate {
public static void main(String[] args) {
//等待唤醒案例
BaoZi bz = new BaoZi();
//这里使用同一个bz,同时在同步锁中也是加入bz,以此保证注意123
ChiHuo ch = new ChiHuo("吃货",bz);
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
ch.start();
bzp.start();
/*
包子铺开始做包子
包子造好了厚皮牛肉大葱
吃货来吃吧
吃货正在吃厚皮,牛肉大葱包子
包子铺开始做包子
包子造好了薄皮蟹黄灌汤
吃货来吃吧
吃货正在吃薄皮,蟹黄灌汤包子
包子铺开始做包子
包子造好了厚皮牛肉大葱
吃货来吃吧
吃货正在吃厚皮,牛肉大葱包子
包子铺开始做包子
*/
}
public static class BaoZi {
String pier ;
String xianer ;
boolean flag = false ;//包子资源 是否准备好 包子资源状态
}
public static class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(String name,BaoZi bz){
super(name);
this.bz = bz;
}
@Override
public void run() {
while(true){
synchronized (bz){
if(bz.flag == false){//没包子
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃货正在吃:"+bz.pier+","+bz.xianer+"包子");
bz.flag = false;
bz.notify();
}
}
}
}
public static class BaoZiPu extends Thread {
private BaoZi bz;
public BaoZiPu(String name,BaoZi bz){
super(name);
this.bz = bz;
}
@Override
public void run() {
int count = 0;
//造包子
while(true){
//同步
synchronized (bz){
if(bz.flag == true){//包子资源 存在
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 没有包子 造包子
System.out.println("包子铺开始做包子");
if(count%2 == 0){
// 薄皮 蟹黄包
bz.pier = "薄皮";
bz.xianer = "蟹黄灌汤";
}else{
// 厚皮 牛肉大葱
bz.pier = "厚皮";
bz.xianer = "牛肉大葱";
}
count++;
bz.flag=true;
System.out.println("包子造好了:"+bz.pier+bz.xianer);
System.out.println("吃货来吃吧");
//唤醒等待线程 吃货
bz.notify();
}
}
}
}
}

View File

@ -0,0 +1,130 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 16:05
* @Description:
* TODO 线程之间的通信:等待唤醒机制之多对多问题:生产者消费者问题
* 案例有家餐馆的取餐口比较小只能放10份快餐厨师做完快餐放在取餐口的工作台上服务员从这个工作台取出快餐给顾客现在有多个厨师和多个服务员
* @Version: 1.0
*/
public class ThreadCommunicate1 {
public static void main(String[] args) {
Workbench bench = new Workbench();
Cook c1 = new Cook("大拿",bench);
Cook c2 = new Cook("吉祥",bench);
Waiter w1 = new Waiter("翠花",bench);
Waiter w2 = new Waiter("如意",bench);
c1.start();
c2.start();
w1.start();
w2.start();
/*
大拿厨师制作了一份快餐现在工作台上有1份快餐
大拿厨师制作了一份快餐现在工作台上有2份快餐
大拿厨师制作了一份快餐现在工作台上有3份快餐
大拿厨师制作了一份快餐现在工作台上有4份快餐
大拿厨师制作了一份快餐现在工作台上有5份快餐
如意服务员取走了一份快餐现在工作台上有4份快餐
如意服务员取走了一份快餐现在工作台上有3份快餐
翠花服务员取走了一份快餐现在工作台上有2份快餐
吉祥厨师制作了一份快餐现在工作台上有3份快餐
吉祥厨师制作了一份快餐现在工作台上有4份快餐
吉祥厨师制作了一份快餐现在工作台上有5份快餐
翠花服务员取走了一份快餐现在工作台上有4份快餐
翠花服务员取走了一份快餐现在工作台上有3份快餐
翠花服务员取走了一份快餐现在工作台上有2份快餐
翠花服务员取走了一份快餐现在工作台上有1份快餐`
*/
}
public static class Workbench {
private static final int MAX_VALUE = 10;
private int num;
public synchronized void put() {
while(num >= MAX_VALUE){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
//加入睡眠时间是放大问题现象去掉同步和wait等可观察问题
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
System.out.println(Thread.currentThread().getName()+ "厨师制作了一份快餐,现在工作台上有:" + num + "份快餐");
this.notifyAll();
}
public synchronized void take() {
while(num <=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
//加入睡眠时间是放大问题现象去掉同步和wait等可观察问题
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
System.out.println(Thread.currentThread().getName()+"服务员取走了一份快餐,现在工作台上有:" + num + "份快餐");
this.notifyAll();
}
}
public static class Waiter extends Thread{
private Workbench workbench;
public Waiter(String name,Workbench workbench) {
super(name);
this.workbench = workbench;
}
public void run(){
for (int i = 1; i <= 10; i++) {
workbench.take();
}
}
}
public static class Cook extends Thread{
private Workbench workbench;
public Cook(String name,Workbench workbench) {
super(name);
this.workbench = workbench;
}
public void run(){
for (int i = 1; i <= 10; i++) {
workbench.put();
}
}
}
}

View File

@ -0,0 +1,93 @@
package com.markilue.java_learning.thread;
import java.util.Scanner;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning
* @Author: dingjiawen
* @CreateTime: 2022-09-13 18:32
* @Description:
* TODO 多线程尝试强行加塞:
* Thread.sleep方法使得线程沉睡等到了指定时间重新开始执行
* Thread.start方法使得线程开始执行
* Thread,join方法等待该线程终止只有终止程序才会继续往下执行
* @Version: 1.0
*/
public class ThreadDemo1 {
public static void main(String[] args) {
ChatThread t = new ChatThread();
t.start();
for (int i = 1; i < 10; i++) {
System.out.println("main:" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当main打印到5之后需要等join进来的线程停止后才会继续了
if(i==5){
try {
//join以后如果这个线程不停程序就不会往下运行所以start是开始运行join以后是必须等他运行完再往后运行
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
实现的结果:
main:1
main:2
main:3
main:4
main:5
是否结束YN
y
main:6
main:7
main:8
main:9
===========================================
第二次结果:
main:1
main:2
是否结束YN
main:3
main:4
main:5
n
是否结束YN
n
是否结束YN
y
main:6
main:7
main:8
main:9
*/
}
public static class ChatThread extends Thread{
public void run(){
Scanner input = new Scanner(System.in);
while(true){
System.out.println("是否结束Y、N");
char confirm = input.next().charAt(0);
if(confirm == 'Y' || confirm == 'y'){
break;
}
}
input.close();
}
}
}

View File

@ -0,0 +1,80 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 14:42
* @Description:
* TODO 线程安全问题演示:
* 电影院要卖票我们模拟电影院的卖票过程假设要播放的电影是 葫芦娃大战奥特曼本次电影的座位共100个
* (本场电影只能卖100张票)
* 我们来模拟电影院的售票窗口实现多个窗口同时卖 葫芦娃大战奥特曼这场电影票(多个窗口一起卖这100张票)
*
*
* @Version: 1.0
*/
public class ThreadSafe {
public static void main(String[] args) {
// 创建线程任务对象
Ticket ticket = new Ticket();
// 创建三个窗口对象
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
// 同时卖票
t1.start();
t2.start();
t3.start();
/*
线程安全问题在于:多个线程在操作同一个共享变量时可能出现多重篡改的问题
窗口1正在卖:5
窗口3正在卖:4
窗口2正在卖:5
窗口2正在卖:3
窗口3正在卖:1
窗口1正在卖:2
*/
}
//第一步 创建资源类在资源类中定义属性和方法
public static class Ticket implements Runnable {
private int ticket = 100;
//第二步在资源操作方法中进行判断和干活
private void saleTicket(){
if (ticket > 0) { // 有票可以卖
// 出票操作
// 使用sleep模拟一下出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
}else {
}
}
/*
* 执行卖票操作
*/
@Override
public void run() {
// 每个窗口卖票的操作
// 窗口永远开启
while (true) {
saleTicket();
}
}
}
}

View File

@ -0,0 +1,106 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 14:52
* @Description: TODO 线程安全问题演示:
* 第二种情况演示 :使用一个static变量ticket来作为共享变量放入常量池中因此new ticket操作的都是同一个ticket变量
* <p>
* TODO 解决线程安全问题的方法:同步代码块或者同步方法或者锁机制(暂时不管)
* 1)同步代码块:
* synchronized (需要锁住的东西如 ThreadSafe1.class){需要锁的代码
* }
* 2)同步方法:
* 在方法名前加synchronized关键字
* 如public synchronized void method(){
* 可能会产生线程安全问题的代码
* }
* 同步方法的锁对象
* 1静态方法当前类的Class对象
* 2非静态方法this
* 所以当需要创建多个对象时需要锁住的方法必须使用static关键字
* @Version: 1.0
*/
public class ThreadSafe1 {
public static void main(String[] args) {
Ticket t1 = new Ticket("窗口一");
Ticket t2 = new Ticket("窗口二");
Ticket t3 = new Ticket("窗口三");
t1.start();
t2.start();
t3.start();
/*
窗口一正在卖:10
窗口二正在卖:10
窗口三正在卖:9
窗口一正在卖:7
窗口二正在卖:8
窗口三正在卖:5
窗口一正在卖:4
窗口二正在卖:6
窗口二正在卖:3
窗口三正在卖:3
窗口一正在卖:3
窗口三正在卖:1
窗口二正在卖:0
窗口一正在卖:2
加了同步代码块以后,连顺序都是对的:
窗口二正在卖:47
窗口二正在卖:46
窗口二正在卖:45
窗口三正在卖:44
窗口三正在卖:43
窗口三正在卖:42
窗口一正在卖:41
*/
}
public static class Ticket extends Thread {
private static int ticket = 100;
public Ticket() {
super();
}
public Ticket(String name) {
super(name);
}
/*
* 执行卖票操作
*/
@Override
public void run() {
// 每个窗口卖票的操作
// 窗口永远开启
while (true) {
synchronized (Ticket.class) { //这里不能选用this作为锁因为这几个线程的this不是同一个,如果是同一个ticket对象才可以使用
if (ticket > 0) { // 有票可以卖
// 出票操作
// 使用sleep模拟一下出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
System.out.println(getName() + "正在卖:" + ticket--);
} else {
break;
}
}
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.markilue.java_learning.thread;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.thread
* @Author: dingjiawen
* @CreateTime: 2022-09-16 17:33
* @Description:
* TODO sleep()和wait()的区别
* 1sleep()不释放锁wait()释放锁
* 2sleep()指定休眠的时间wait()可以指定时间也可以无限等待直到notify或notifyAll
* 3sleep()在Thread类中声明的静态方法wait方法在Object类中声明
* 因为我们调用wait方法是由锁对象调用而锁对象的类型是任意类型的对象那么希望任意类型的对象都要有的方法只能声明在Object类中
* @Version: 1.0
*/
public class sleepAndWait {
}

View File

@ -17,6 +17,15 @@ object Scala04_Function_Normal_1 {
//调用时如果不想使用默认值直接传值即可 //调用时如果不想使用默认值直接传值即可
fun3("111111") //111111 fun3("111111") //111111
def fun4(username:String="dingjiawen",password:String="123456")={
println(s"username:$username,password:$password")
}
fun4()
fun4(password = "qazwsx")
fun4(password = "qazwsx",username = "123")
} }
} }

View File

@ -28,6 +28,7 @@ object Scala05_Object_Field {
//而在scala中给属性提供的set,set方法不遵循bean规范为了统一用于则有了下述写法 //而在scala中给属性提供的set,set方法不遵循bean规范为了统一用于则有了下述写法
test.setEmail("xxx") test.setEmail("xxx")
test.getEmail() test.getEmail()
println(test.email)
} }

View File

@ -29,7 +29,8 @@ object Scala12_Object_Trait_4 {
trait Log extends Operator{ trait Log extends Operator{
override def operDate(): Unit ={ override def operDate(): Unit ={
print("向文件中") print("向文件中")
super.operDate() //如果想跳过DB直接访问Operator则使用super[Operator].operDate() // super[Operator].operDate() //如果想跳过DB直接访问Operator则使用super[Operator].operDate() //向文件中操作数据...
super.operDate() //如果想跳过DB直接访问Operator则使用super[Operator].operDate() //向文件中向数据库中操作数据
} }
} }
class MySQL extends DB with Log { class MySQL extends DB with Log {

View File

@ -1,5 +1,7 @@
package com.atguigu.scala.chapter07 package com.atguigu.scala.chapter07
import scala.+:
object Scala01_Collection_1 { object Scala01_Collection_1 {
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
@ -27,9 +29,13 @@ object Scala01_Collection_1 {
//val ints1: Array[Int] = array1.:+(5) //val ints1: Array[Int] = array1.:+(5)
val ints1 = array1 :+ 5 val ints1 = array1 :+ 5
val ints3 =5 +: array1
val ints2 = array1 ++ array3 val ints2 = array1 ++ array3
//val ints3 = array1 ++: array3 //val ints3 = array1 ++: array3
// println(ints)
// println(ints1)
// println(ints2)
println(array1 eq ints) //false println(array1 eq ints) //false
println(array1 eq ints1) //false println(array1 eq ints1) //false
@ -38,9 +44,9 @@ object Scala01_Collection_1 {
// 遍历 // 遍历
//把集合中的元素用逗号分割形成一个字符串 //把集合中的元素用逗号分割形成一个字符串
// println(ints.mkString(",")) //5,1,2,3,4 println(ints.mkString(",")) //5,1,2,3,4
// println(ints1.mkString(",")) //1,2,3,4,5 println(ints1.mkString(",")) //1,2,3,4,5
// println(ints2.mkString(",")) //1,2,3,4,5,6,7,8 println(ints2.mkString(",")) //1,2,3,4,5,6,7,8
//foreach方法是一个循环的方法需要穿第一个函数这个传参数的类型是函数类型 //foreach方法是一个循环的方法需要穿第一个函数这个传参数的类型是函数类型
// 函数类型 Int => U // 函数类型 Int => U

View File

@ -21,7 +21,8 @@ object Scala06_Collection_Seq_2 {
// println(list) //ListBuffer(5, 3, 4, 2, 1) // println(list) //ListBuffer(5, 3, 4, 2, 1)
// //
// list.remove(1) // list.remove(1)
// list.remove(1,2) println(list)
println(list.remove(1,2))
// //
// list.mkString() // list.mkString()
// list.iterator // list.iterator

View File

@ -18,7 +18,7 @@ object Scala01_Transform {
} }
val age :Int = thirdPart() //Double ? Int? val age :Int = thirdPart() //Double ? Int?
println(age) println(age.isInstanceOf[Double])
} }
def thirdPart(): Double ={ def thirdPart(): Double ={

View File

@ -3,13 +3,13 @@ package com.atguigu.scala.test;
public class TestAdd { public class TestAdd {
public static void main(String[] args) { public static void main(String[] args) {
//int i=0; int i=0;
// int j=i++; // int j=i++;
//赋值是 等号右边的计算结果给左边 //赋值是 等号右边的计算结果给左边
//i++不是原子性操作中间会有临时的结果 //i++不是原子性操作中间会有临时的结果
//i=i++; //print之后是1 _tmp=0; i=1; i=_tmp=0 i=i++; //print之后是1 _tmp=0; i=1; i=_tmp=0
//System.out.println("i="+i); // 1 System.out.println("i="+i); // 1
// System.out.println("j="+j); // 0 // System.out.println("j="+j); // 0
//TODO 阶乘一个大于1的数的阶乘等于这个数乘以他减一的数的阶乘 //TODO 阶乘一个大于1的数的阶乘等于这个数乘以他减一的数的阶乘

View File

@ -2,6 +2,8 @@ package com.atguigu.scala.test;
//import com.atguigu.scala.chapter09.Scala02_Exception; //import com.atguigu.scala.chapter09.Scala02_Exception;
import com.atguigu.scala.chapter09.Scala02_Exception;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -38,7 +40,7 @@ public class TestException {
// } // }
// System.out.println("xxxxxx"); // System.out.println("xxxxxx");
// Scala02_Exception.test(); Scala02_Exception.test();
} }

View File

@ -88,7 +88,6 @@ object ToBitmapApp {
ClickhouseUtils.executeSql(clearSQL) ClickhouseUtils.executeSql(clearSQL)
if (tagList.size > 0) { if (tagList.size > 0) {
//('gender',gender),('agegroup',agegroup),('favor',favor) //('gender',gender),('agegroup',agegroup),('favor',favor)
val tagCodeSQL: String = tagList.map( val tagCodeSQL: String = tagList.map(
@ -113,7 +112,6 @@ object ToBitmapApp {
} }
} }
} }

View File

@ -34,6 +34,7 @@ public class ZooKeeperTest {
* @throws KeeperException * @throws KeeperException
* @throws InterruptedException * @throws InterruptedException
*/ */
@Test
public void deleteAll(String path,ZooKeeper zk) throws KeeperException, InterruptedException { public void deleteAll(String path,ZooKeeper zk) throws KeeperException, InterruptedException {
//判断当前节点是否存在获取stat //判断当前节点是否存在获取stat

View File

@ -7,7 +7,47 @@
<groupId>org.example</groupId> <groupId>org.example</groupId>
<artifactId>Leecode</artifactId> <artifactId>Leecode</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<!-- <dependency>-->
<!-- <groupId>org.projectlombok</groupId>-->
<!-- <artifactId>lombok</artifactId>-->
<!-- <version>RELEASE</version>-->
<!--&lt;!&ndash; <scope>compile</scope>&ndash;&gt;-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>

View File

@ -0,0 +1,98 @@
package com.markilue.leecode.array;
/**
* 力扣题号704二分查找
* 描述:给定一个n个元素有序的升序整型数组nums 和一个目标值target 写一个函数搜索nums中的 target如果目标值存在返回下标否则返回 -1
*
*/
public class BinarySearch {
public static void main(String[] args) {
int[] nums = {-1,0,3,5,9,12};
int target = 9;
System.out.println(search(nums,target));
}
/**
* 非递归法
* @param nums
* @param target
* @return
*/
public static int search(int[] nums, int target) {
int left=0;
int right = nums.length;
while (left<right){
//位运算加快速度同时不用(right+left)>>1避免right+left超过了int最大值
//由于舍入问题mid永远小于right因此 可以写right = nums.length
int mid=left+((right-left)>>1);
if(nums[mid]<target){
left=mid+1;
}else if(nums[mid]>target){
right=mid;
}else {
return mid;
}
}
return -1;
}
/**
* 递归法
* @param nums
* @param target
* @return
*/
public static int search1(int[] nums, int target) {
//记录数组长度
return find(nums,0,nums.length-1,target);
}
public static int find(int[] nums,int start,int stop, int target){
/**
* 解决N.5只能舍不能入最后可能无限循环的问题
*/
if(nums[start]==target){
return start;
}
if(nums[stop]==target){
return stop;
}
if(start==stop-1&&nums[stop]!=target){
return -1;
}
if(start==stop){
if(nums[start]==target){
return start;
}else {
return -1;
}
}
int mid = start+(stop-start)/2;
if(nums[mid]<target){
return find(nums,mid,stop,target);
}else if(nums[mid]>target){
return find(nums,start,mid,target);
}else {
return mid;
}
}
}

View File

@ -0,0 +1,139 @@
package com.markilue.leecode.array;
import org.junit.Test;
import javax.management.remote.rmi._RMIConnection_Stub;
/**
* 寻找长度最小的子数组
*/
public class MinSubArrayLen {
@Test
public void test() {
int target = 3;
int[] nums = {1, 1, 1};
System.out.println(minSubArrayLen2(target, nums));
}
/**
* 代码随想录中解法:可伸缩的滑动窗口
* 看了思路之后自己尝试
* 速度击败20% 内存击败60%
* @param target
* @param nums
* @return
*/
public int minSubArrayLen(int target, int[] nums) {
int start = 0;
int stop = 0;
//记录窗口总和
int sum = nums[start];
//记录窗口长度
int len = 0;
//记录结果
int result = nums.length;
boolean flag = false;
while (stop <= nums.length) {
while (sum >= target) {
flag = true;
len = stop - start + 1;
if (result > len) {
result = len;
}
if (len == 1) {
break;
}
sum -= nums[start++];
}
if (len == 1 && sum > target) {
return 1;
}
if (stop == nums.length - 1) {
break;
}
sum += nums[++stop];
}
if (flag) {
return result;
} else {
return 0;
}
}
/**
* 代码随想录中解法:可伸缩的滑动窗口
* 官方实现速度击败99.99%内存击败58.84%
*
* @param target
* @param nums
* @return
*/
public int minSubArrayLen1(int target, int[] nums) {
int start = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
int len = 0;
for (int stop = 0; stop < nums.length; stop++) {
sum += nums[stop];
while (sum >= target) {
len = stop - start + 1;
result = result > len ? len : result;
sum -= nums[start++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
/**
* 评论里给的滑动窗口法优化
* 目前没看出优化在哪里感觉没必要
* @param s
* @param nums
* @return
*/
public int minSubArrayLen2(int s, int[] nums) {
if (nums.length == 0) {
return 0;
}
int count = 0;
int left = 0;
int right = 0;
while (count < s) {
if (right == nums.length) {
return 0;
}
count += nums[right];
right++;
}
while (right < nums.length) {
if (count < s) {
count += nums[right];
right++;
}
count -= nums[left];
left++;
}
while (count >= s) {
count -= nums[left];
left++;
}
return right - left + 1;
}
}

View File

@ -0,0 +1,106 @@
package com.markilue.leecode.array;
import org.junit.Test;
import java.util.Arrays;
/**
* 移除元素:
* 给你一个数组 nums和一个值 val你需要 原地 移除所有数值等于val的元素并返回移除后数组的新长度
* 不要使用额外的数组空间你必须仅使用 O(1) 额外空间并 原地 修改输入数组
* 元素的顺序可以改变你不需要考虑数组中超出新长度后面的元素
*/
public class RemoveElement {
@Test
public void test(){
int[] nums = {3,2,2,3};
int val = 3;
int length = removeElement3(nums, val);
System.out.println("数组:"+ Arrays.toString(nums));
System.out.println("数组长度:"+length);
}
public int removeElement(int[] nums, int val) {
int deleteNum = 0;
for (int i = 0; i < nums.length-deleteNum; i++) {
if(nums[i]==val){
int flag = i;
for (int j = nums.length-1-deleteNum; j > i; j--) {
if(nums[j]!=val){
flag=j;
break;
}
deleteNum+=1;
}
if(flag==i){
deleteNum+=1;
break;
}
exchange(nums,i,flag);
deleteNum+=1;
}
}
return nums.length-deleteNum;
}
/**
* 快慢指针法
* 由于不考虑后面是什么东西因此可以直接将其覆盖
* @param nums
* @param val
* @return
*/
public int removeElement1(int[] nums, int val){
int slowIndex=0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if(val!=nums[fastIndex]){
nums[slowIndex++]=nums[fastIndex];
}
}
return slowIndex;
}
/**
* 官方的快慢指针优化双向奔赴
* @param nums
* @param val
* @return
*/
public int removeElement3(int[] nums, int val) {
int left = 0;
int right = nums.length;
while (left < right) {
if (nums[left] == val) {
nums[left] = nums[right - 1];
right--;
} else {
left++;
}
}
return left;
}
/**
* 交换i,j
* @param nums
* @param i
* @param j
*/
public void exchange(int[] nums,int i,int j){
int temp = nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}

View File

@ -0,0 +1,62 @@
package com.markilue.leecode.array;
import org.junit.Test;
import java.util.Arrays;
/**
* 螺旋矩阵:
* 给你一个正整数 n 生成一个包含 1 n2 所有元素且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
*/
public class generateMatrix {
@Test
public void test(){
int[][] ints = generateMatrix(6);
for (int[] anInt : ints) {
System.out.println(Arrays.toString(anInt));
}
}
/**
* 看了代码随想录之后的自己编程:寻找循环不变量
* 速度击败100%内存击败87%
* 不会就先把第一层循环写出来在找规律
* @param n
* @return
*/
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int count = 1;
int i=0;
int j=0;
for (int k = 0; k < n / 2; k++) {
for (; i < result[k].length-k-1; i++) {
result[j][i]=count++;
}
for (; j < result[k].length-k-1; j++) {
result[j][i]=count++;
}
for (;i>k;i--){
result[j][i]=count++;
}
for (;j>k;j--){
result[j][i]=count++;
}
i+=1;
j+=1;
}
//如果是奇数,单独填充中间的
if(n%2==1){
result[n/2][n/2]=count++;
}
return result;
}
}

View File

@ -0,0 +1,180 @@
package com.markilue.leecode.hashtable;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.hashtable
* @Author: dingjiawen
* @CreateTime: 2022-09-08 12:24
* @Description: TODO 力扣18题:四数之和
* 给你一个由 n 个整数组成的数组nums 和一个目标值 target 请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]]若两个四元组元素一一对应则认为两个四元组重复
* 10 <= a, b, c, d< n
* 2abc d 互不相同
* 3nums[a] + nums[b] + nums[c] + nums[d] == target
* 你可以按 任意顺序 返回答案
* @Version: 1.0
*/
public class FourSum {
@Test
public void test() {
int[] nums = {2, 2, 2, 2, 2};
int target = 8;
System.out.println(fourSum1(nums, target));
}
@Test
public void test1() {
int[] nums = {1,0,-1,0,-2,2};
int target = 0;
System.out.println(fourSum1(nums, target));
}
@Test
public void test2() {
int[] nums = {-1,0,1,2,-1,-4};
int target = -1;
System.out.println(fourSum1(nums, target));
}
@Test
public void test3() {
int[] nums = {1,-2,-5,-4,-3,3,3,5};
int target = -11;
System.out.println(fourSum1(nums, target));
}
@Test
public void test4() {
int[] nums = {1000000000,1000000000,1000000000,1000000000};
int target = -294967296;
System.out.println(1000000000+1000000000);
System.out.println(-294967296-(1000000000+1000000000));
System.out.println(fourSum1(nums, target));
}
/**
* 本人思路:这里如果还想使用双指针法时间复杂度会是O(n^3),考虑还是使用hash法
* 维护一个hash表<两数之和,list<两数>> 如何避免两数重复? 先排序在加入,但是有出现了问题虽然不重复但是会存在覆盖问题
* 如下面的实现所示
*
* @param nums
* @param target
* @return
*/
public List<List<Integer>> fourSum(int[] nums, int target) {
if (nums.length < 4) {
return new ArrayList<>();
}
Arrays.sort(nums);
HashMap<Integer, List<Integer>> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
map.put(nums[i] + nums[j], new ArrayList<>(Arrays.asList(nums[i], nums[j])));
}
}
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if (map.containsKey(target - nums[i] - nums[j])) {
map.get(target - nums[i] - nums[j]).add(nums[i]);
map.get(target - nums[i] - nums[j]).add(nums[j]);
lists.add(map.get(target - nums[i] - nums[j]));
map.get(target - nums[i] - nums[j]).remove(3);
map.get(target - nums[i] - nums[j]).remove(4);
}
map.put(nums[i] + nums[j], new ArrayList<>(Arrays.asList(nums[i], nums[j])));
}
}
Collection<List<Integer>> values = map.values();
for (List<Integer> value : values) {
lists.add(value);
}
return lists;
}
/**
* 代码思想录思路:还是使用双指针法在外面在套一层循环将四数问题转化为3数问题
* 如下所示
* 速度超过49.9%内存超过93.76%
* @param nums
* @param target
* @return
*/
public List<List<Integer>> fourSum1(int[] nums, int target) {
if (nums.length < 4) {
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int mid = j + 1;
int right = nums.length - 1;
while (mid < right) {
//解决int超过限制的问题
long sum = (long) nums[i] + nums[j] + nums[mid] + nums[right];
if (sum < target) {
mid++;
} else if (sum > target) {
right--;
} else {
lists.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[mid], nums[right])));
while (mid < right && nums[mid] == nums[mid + 1]) {
mid++;
}
while (mid < right && nums[right] == nums[right - 1]) {
right--;
}
mid++;
right--;
}
}
}
}
return lists;
}
}

View File

@ -0,0 +1,186 @@
package com.markilue.leecode.hashtable;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.hashtable
* @Author: dingjiawen
* @CreateTime: 2022-09-08 09:09
* @Description:
* TODO leecode454题:四数相加II:
* 给你四个整数数组 nums1nums2nums3 nums4 数组长度都是 n 请你计算有多少个元组 (i, j, k, l) 能满足
* 1) 0 <= i, j, k, l < n
* 2) nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
* @Version: 1.0
*/
public class FourSumCount {
@Test
public void test(){
int[] nums1 = {1, 2};
int[] nums2 = {-2, -1};
int[] nums3 = {-1, 2};
int[] nums4 = {0, 2};
System.out.println(fourSumCount1(nums1, nums2, nums3, nums4));
}
@Test
public void test2(){
int[] nums1 = {1, 2};
int[] nums2 = {-2, -1};
int[] nums3 = {0, 2};
int[] nums4 = {0, 2};
System.out.println(fourSumCount1(nums1, nums2, nums3, nums4));
}
@Test
public void test1(){
int[] nums1 = {0};
int[] nums2 = {0};
int[] nums3 = {0};
int[] nums4 = {0};
System.out.println(fourSumCount1(nums1, nums2, nums3, nums4));
}
/**
* 自己的思路:
* 1)使用递归来解决,把四数相加转换为三数相加转为两数相加,这种方式时间复杂度直接O(n^4)
* 2)构建两个hash表,分别遍历两个数组,时间复杂度O(n^2)
* 以下是第二种思路hashset暂时还有问题没有解决如test2的问题
* @param nums1
* @param nums2
* @param nums3
* @param nums4
* @return
*/
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//构建一个hash表
HashSet<Integer> set1 = new HashSet<>();
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
set1.add(nums1[i]+nums2[j]);
}
}
HashSet<Integer> set2 = new HashSet<>();
for (int i = 0; i < nums3.length; i++) {
for (int j = 0; j < nums4.length; j++) {
set2.add(nums3[i]+nums4[j]);
}
}
int count=0;
for (Integer integer : set1) {
if(set2.contains(0-integer)){
count++;
}
}
return count;
}
/**
* 代码思想录的思路:
* 2)构建两个hash表<两数之和,这两数之和出现的次数>,分别遍历两个数组,时间复杂度O(n^2)
* 速度击败20.33%内存击败13.78%
* @param nums1
* @param nums2
* @param nums3
* @param nums4
* @return
*/
public int fourSumCount1(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//构建一个hash表<两数之和,这两数之和出现的次数>
HashMap<Integer, Integer> map1 = new HashMap<>();
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
map1.put(nums1[i]+nums2[j],map1.getOrDefault(nums1[i]+nums2[j],0)+1);
}
}
HashMap<Integer, Integer> map2 = new HashMap<>();
for (int i = 0; i < nums3.length; i++) {
for (int j = 0; j < nums4.length; j++) {
map2.put(nums3[i]+nums4[j],map2.getOrDefault(nums3[i]+nums4[j],0)+1);
}
}
int count=0;
for (Integer integer : map1.keySet()) {
if(map2.containsKey(0-integer)){
count+=map1.get(integer)*map2.get(0-integer);
}
}
return count;
}
/**
* 官方的思路:
* 2)构建一个hash表<两数之和,这两数之和出现的次数>,分别遍历两个数组,时间复杂度O(n^2)
* 速度击败28.59%内存击败91.98% 时间空间复杂度均为O(n^2)
* @param nums1
* @param nums2
* @param nums3
* @param nums4
* @return
*/
public int fourSumCount2(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//构建一个hash表<两数之和,这两数之和出现的次数>
HashMap<Integer, Integer> map1 = new HashMap<>();
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
map1.put(nums1[i]+nums2[j],map1.getOrDefault(nums1[i]+nums2[j],0)+1);
}
}
int count=0;
for (int i = 0; i < nums3.length; i++) {
for (int j = 0; j < nums4.length; j++) {
if(map1.containsKey(0-nums3[i]-nums4[j])){
count+=map1.get(0-nums3[i]-nums4[j]);
}
}
}
return count;
}
}

View File

@ -0,0 +1,92 @@
package com.markilue.leecode.hashtable;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.hashtable
* @Author: dingjiawen
* @CreateTime: 2022-09-07 11:47
* @Description:
* TODO 力扣349题:求两个数组的交集
* 给定两个数组 nums1 nums2 返回 它们的交集 输出结果中的每个元素一定是 唯一 我们可以 不考虑输出结果的顺序
* @Version: 1.0
*/
public class Intersection {
@Test
public void test(){
int[] nums1 = {1, 2, 2, 1};
int[] nums2 = {2, 2};
System.out.println(Arrays.toString(intersection(nums1, nums2)));
}
/**
* 维护一个HashSet存储一个nums数组的数据,遍历另一个数组,如果有就保留
* 速度击败99.13%内存击败83.58%
* @param nums1
* @param nums2
* @return
*/
public int[] intersection(int[] nums1, int[] nums2) {
HashSet<Integer> integers = new HashSet<>();
for (int i : nums1) {
integers.add(i);
}
int[] result=new int[integers.size()];
int count=0;
for (int i : nums2) {
if(integers.contains(i)){
integers.remove(i);
result[count++]=i;
}
}
int[] result1=new int[count];
for (int i = 0; i < result1.length; i++) {
result1[i]=result[i];
}
return result1;
}
/**
* 代码随想录方法.使用两个HashSet
* 速度击败26%内存击败68.81%
* @param nums1
* @param nums2
* @return
*/
public int[] intersection1(int[] nums1, int[] nums2) {
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) {
return new int[0];
}
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> resSet = new HashSet<Integer>();
//遍历数组1
for (int i : nums1) {
set1.add(i);
}
//遍历数组2的过程中判断哈希表中是否存在该元素
for (int i : nums2) {
if (set1.contains(i)) {
resSet.add(i);
}
}
//将结果几何转为数组
return resSet.stream().mapToInt(x -> x).toArray();
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,81 @@
package com.markilue.leecode.hashtable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.hashtable
* @Author: dingjiawen
* @CreateTime: 2022-09-08 10:49
* @Description:
* TODO 力扣15题:三数之和:
* 给你一个整数数组 nums 判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k j != k 同时还满足 nums[i] + nums[j] + nums[k] == 0
* 你返回所有和为 0 且不重复的三元组
* 注意答案中不可以包含重复的三元组
* @Version: 1.0
*/
public class ThreeSum {
/**
* 这道题使用哈希法的思路:
* 使用两层循环先计算出a+b,最后再遍历一遍找到-a-b的值,最后再进行去重,整体时间复杂度是O(n^2),但是仍然是比较费时的操作,因为去重比较麻烦
* 这里可以使用双指针法:先对数组进行整体的排序,作为一个有序的数组使用头尾指针寻找中间的数据,时间复杂度O(n^2)
* @param nums
* @return
*/
public List<List<Integer>> threeSum(int[] nums) {
if(nums.length<3){
return new ArrayList<>();
}
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int left = 0; left < nums.length - 2; left++) {
if(nums[left]>0){
break;
}
//避免重复
if(left>0&&nums[left]==nums[left-1]){
continue;
}
//定义尾指针
int right= nums.length-1;
//定义中间指针
int mid =left+1;
while (mid<right){
int sum=nums[left]+nums[mid]+nums[right];
if(sum>0){
right--;
}else if(sum<0){
mid++;
}else {
//sum==0
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(nums[left],nums[mid],nums[right]));
//判断一下左右避免重复
while (mid<right&&nums[mid+1]==nums[mid]) {
mid++;
}
while (mid<right&&nums[right-1]==nums[right]) {
right--;
}
mid++;
right--;
}
}
}
return lists;
}
}

View File

@ -0,0 +1,123 @@
package com.markilue.leecode.hashtable;
import org.junit.Test;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashMap;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.hashtable
* @Author: dingjiawen
* @CreateTime: 2022-09-07 12:45
* @Description:
* TODO leecode第1题:两数之和
* 给定一个整数数组 nums和一个整数目标值 target请你在该数组中找出 和为目标值 target 的那两个整数并返回它们的数组下标
* 你可以假设每种输入只会对应一个答案但是数组中同一个元素在答案里不能重复出现
* 你可以按任意顺序返回答案
* @Version: 1.0
*/
public class TwoSum {
@Test
public void test(){
int[] nums = {2, 7, 11, 15};
int target = 9;
System.out.println(Arrays.toString(twoSum(nums, target)));
}
@Test
public void test1(){
int[] nums = {3,2,4};
int target = 6;
System.out.println(Arrays.toString(twoSum(nums, target)));
}
@Test
public void test2(){
int[] nums = {3,3};
int target = 6;
System.out.println(Arrays.toString(twoSum(nums, target)));
}
@Test
public void test3(){
int[] nums = {3,2,3};
int target = 6;
System.out.println(Arrays.toString(twoSum(nums, target)));
}
/**
* 自己的思路:维护一个hashMap<数组位置,需要的值>
* 速度击败53.1%内存击败7.31%
* @param nums
* @param target
* @return
*/
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
int[] result=new int[2];
for (int i = 0; i < nums.length; i++) {
map.put(target-nums[i],i);
}
int last=0;
int index=0;
boolean flag=false;
for (int i = 0; i < nums.length; i++) {
if(flag){
if(nums[i]==last){
result[0]=i;
result[1]=index;
return result;
}
}
if (map.containsKey(nums[i])) {
//如果进了这里还没有返回那么可能是两数相等的情况
flag=true;
last=nums[i];
index=i;
if(map.get(nums[i])!=map.get(target-nums[i])){
result[0]=map.get(nums[i]);
result[1]=map.get(target-nums[i]);
return result;
}
}
}
return result;
}
/**
* 官方的思路:维护一个hashMap<数组位置,需要的值>代码更加简洁,只使用一次循环,避免了很多的重复判断
* 速度击败73.35%内存击败54.57%
* @param nums
* @param target
* @return
*/
public int[] twoSum1(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target-nums[i])) {
return new int[]{i,map.get(target-nums[i])};
}
map.put(nums[i],i);
}
return new int[0];
}
}

View File

@ -0,0 +1,90 @@
package com.markilue.leecode.listnode;
import org.junit.Test;
import java.util.HashSet;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.listnode
* @Author: dingjiawen
* @CreateTime: 2022-09-06 11:49
* @Description:
* TODO 力扣142环形链表II:
* 给定一个链表的头节点 head返回链表开始入环的第一个节点如果链表无环则返回null
* 如果链表中有某个节点可以通过连续跟踪 next 指针再次到达则链表中存在环
* 为了表示给定链表中的环评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置索引从 0 开始
* 如果 pos -1则在该链表中没有环注意pos 不作为参数进行传递仅仅是为了标识链表的实际情况
* 不允许修改 链表
* @Version: 1.0
*/
public class DetectCycle {
@Test
public void test(){
}
/**
* 代码随想录思路:快慢指针判断相遇
* 内存击败60%速度击败100%
* 时间复杂度O(N)空间复杂度O(1)
* @param head
* @return
*/
public ListNode detectCycle(ListNode head) {
if(head==null||head.next==null){
return null;
}
ListNode fast=head;
ListNode slow=head;
while (fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
//如果快指针和慢指针相等那么有环且两者正好相遇了
if(fast==slow){
//定义一个从头结点出发的点
ListNode x=head;
//定义一个从相遇节点出发的点
ListNode z=fast;
while (x!=z){
x=x.next;
z=z.next;
}
return x; //返回环的入口
}
}
return null;
}
/**
* 哈希表法:维护一个哈希表,当哈希表中出现重复元素时则是环的入口
* 时间复杂度O(N)空间复杂度O(N)
* @param head
* @return
*/
public ListNode detectCycle1(ListNode head) {
if (head == null || head.next == null) {
return null;
}
HashSet<ListNode> nodeHashSet = new HashSet<ListNode>();
ListNode temp=head;
while (temp!=null){
if(nodeHashSet.contains(temp)){
return temp;
}else {
nodeHashSet.add(temp);
}
temp=temp.next;
}
return null;
}
}

View File

@ -0,0 +1,301 @@
package com.markilue.leecode.listnode;
import org.junit.Test;
/**
* 设计链表
* 设计链表的实现您可以选择使用单链表或双链表单链表中的节点应该具有两个属性val和nextval是当前节点的值next是指向下一个节点的指针/引用如果要使用双向链表则还需要一个属性prev以指示链表中的上一个节点假设链表中的所有节点都是 0-index
*
* 在链表类中实现这些功能
*
* get(index)获取链表中第index个节点的值如果索引无效则返回-1
* addAtHead(val)在链表的第一个元素之前添加一个值为val的节点插入后新节点将成为链表的第一个节点
* addAtTail(val)将值为val 的节点追加到链表的最后一个元素
* addAtIndex(index,val)在链表中的第index个节点之前添加值为val 的节点如果index等于链表的长度则该节点将附加到链表的末尾如果 index 大于链表长度则不会插入节点如果index小于0则在头部插入节点
* deleteAtIndex(index)如果索引index 有效则删除链表中的第index 个节点
*
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
public class MyLinkedList {
public ListNode head;
public MyLinkedList() {
}
public int get(int index) {
int count = 0;
//避免head发生变化
ListNode temp=head;
while (count!=index&&temp!=null){
temp = temp.next;
count++;
}
//找到了对应的index
if(count==index){
if(temp!=null){
return temp.val;
}
}
return -1;
}
public void addAtHead(int val) {
if(head==null){
head=new ListNode(val);
return;
}
ListNode neededAddNode = new ListNode(val);
ListNode tempHead=new ListNode(0);
tempHead.next=neededAddNode;
neededAddNode.next=head;
head=tempHead.next;
}
public void addAtTail(int val) {
if(head==null){
head=new ListNode(val);
return;
}
ListNode temp=head;
while (temp.next!=null){
temp=temp.next;
}
temp.next=new ListNode(val);
}
public void addAtIndex(int index, int val) {
if(index>getSize()){
return;
}
if(index<=0){
addAtHead(val);
}
int count = 0;
//避免head发生变化
ListNode temp=head;
while (count!=index-1&&temp.next!=null){
temp = temp.next;
count++;
}
//找到了对应的index
if(count==index-1){
ListNode need = new ListNode(val);
need.next=temp.next;
temp.next=need;
}else {
System.out.println("index超过限制");
}
}
public void deleteAtIndex(int index) {
if(head==null){
return;
}
if(index==0){
head=head.next;
return;
}
int count = 0;
//避免head发生变化
ListNode temp=head;
while (count!=index-1&&temp.next!=null){
temp = temp.next;
count++;
}
//找到了对应的index
if(count==index-1){
if(temp.next!=null){
temp.next=temp.next.next;
}
}else {
System.out.println("index超过限制");
}
}
public void printList(ListNode head) {
if(head==null){
System.out.println("链表为空");
return;
}
while (head!=null){
System.out.printf(head.val+"->");
head=head.next;
}
System.out.println();
}
public int getSize(){
int size=0;
ListNode temp=head;
while (temp!=null){
size+=1;
temp=temp.next;
}
return size;
}
@Test
public void test(){
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
printList(linkedList.head);
System.out.println();
linkedList.addAtTail(3);
printList(linkedList.head);
System.out.println();
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
printList(linkedList.head);
System.out.println();
System.out.println(linkedList.get(1));
// printList(linkedList.head);
System.out.println();
linkedList.deleteAtIndex(1); //现在链表是1-> 3
printList(linkedList.head);
System.out.println();
System.out.println(linkedList.get(1));
}
@Test
public void test1(){
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(7);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.addAtIndex(3,0);
printList(linkedList.head);
linkedList.deleteAtIndex(2);
printList(linkedList.head);
linkedList.addAtHead(6);
printList(linkedList.head);
linkedList.addAtTail(4);
printList(linkedList.head);
System.out.println(linkedList.get(4));
linkedList.addAtHead(4);
printList(linkedList.head);
linkedList.addAtIndex(5,0);
printList(linkedList.head);
linkedList.addAtHead(6);
printList(linkedList.head);
// System.out.println(linkedList.get(1));
//// printList(linkedList.head);
// System.out.println();
// linkedList.deleteAtIndex(1); //现在链表是1-> 3
// printList(linkedList.head);
// System.out.println();
// System.out.println(linkedList.get(1));
}
@Test
public void test2(){
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.deleteAtIndex(1);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(7);
printList(linkedList.head);
linkedList.addAtHead(3);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(5);
printList(linkedList.head);
linkedList.addAtTail(5);
printList(linkedList.head);
System.out.println(linkedList.get(5));
linkedList.deleteAtIndex(6);
printList(linkedList.head);
linkedList.deleteAtIndex(4);
printList(linkedList.head);
}
@Test
public void test3(){
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.addAtTail(3);
printList(linkedList.head);
linkedList.addAtIndex(1,2);
printList(linkedList.head);
System.out.println(linkedList.get(1));
linkedList.deleteAtIndex(1);
printList(linkedList.head);
System.out.println(linkedList.get(1));
}
/*
错了四五次之后通过
内存超过79.95%速度超过9.96%慢的原因:每次都要算一遍getSize
改进:维护一个变量size,每次容量变化都让size也跟着变化一次;此外还可以维护一个虚拟的头结点不存放任何数据
具体操作参考MyLinkedList1
*/
}

View File

@ -0,0 +1,313 @@
package com.markilue.leecode.listnode;
import org.junit.Test;
/**
* 设计链表
* 设计链表的实现您可以选择使用单链表或双链表单链表中的节点应该具有两个属性val和nextval是当前节点的值next是指向下一个节点的指针/引用如果要使用双向链表则还需要一个属性prev以指示链表中的上一个节点假设链表中的所有节点都是 0-index
* <p>
* 在链表类中实现这些功能
* <p>
* get(index)获取链表中第index个节点的值如果索引无效则返回-1
* addAtHead(val)在链表的第一个元素之前添加一个值为val的节点插入后新节点将成为链表的第一个节点
* addAtTail(val)将值为val 的节点追加到链表的最后一个元素
* addAtIndex(index,val)在链表中的第index个节点之前添加值为val 的节点如果index等于链表的长度则该节点将附加到链表的末尾如果 index 大于链表长度则不会插入节点如果index小于0则在头部插入节点
* deleteAtIndex(index)如果索引index 有效则删除链表中的第index 个节点
* <p>
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
public class MyLinkedList1 {
//该head为虚拟节点,不存放任何数据
public ListNode head;
public int size;
public MyLinkedList1() {
head = new ListNode(0);
size = 0;
}
public int get(int index) {
if (size < index+1 || index < 0) {
return -1;
}
//避免head发生变化
ListNode temp = head.next;
while (index-- > 0) {
temp = temp.next;
}
return temp.val;
}
public void addAtHead(int val) {
ListNode need = new ListNode(val);
need.next = head.next;
head.next = need;
size++;
}
public void addAtTail(int val) {
ListNode need = new ListNode(val);
ListNode temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = need;
size++;
}
public void addAtIndex(int index, int val) {
if (index > size || index < 0) {
return;
}
ListNode temp = head;
while (index-- > 0) {
temp = temp.next;
}
ListNode need = new ListNode(val);
need.next = temp.next;
temp.next = need;
size++;
}
public void deleteAtIndex(int index) {
if (index > size-1 || index < 0) {
return;
}
ListNode temp = head;
while (index-- > 0) {
temp = temp.next;
}
temp.next = temp.next.next;
size--;
}
public void printList(ListNode head) {
ListNode temp=head.next;
while (temp!=null){
System.out.printf(temp.val+"->");
temp=temp.next;
}
System.out.println();
}
public int getSize() {
return size;
}
@Test
public void test() {
MyLinkedList1 linkedList = new MyLinkedList1();
linkedList.addAtHead(1);
printList(linkedList.head);
System.out.println();
linkedList.addAtTail(3);
printList(linkedList.head);
System.out.println();
linkedList.addAtIndex(1, 2); //链表变为1-> 2-> 3
printList(linkedList.head);
System.out.println();
System.out.println(linkedList.get(1));
// printList(linkedList.head);
System.out.println();
linkedList.deleteAtIndex(1); //现在链表是1-> 3
printList(linkedList.head);
System.out.println();
System.out.println(linkedList.get(1));
}
@Test
public void test1() {
MyLinkedList1 linkedList = new MyLinkedList1();
linkedList.addAtHead(7);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.addAtIndex(3, 0);
printList(linkedList.head);
linkedList.deleteAtIndex(2);
printList(linkedList.head);
linkedList.addAtHead(6);
printList(linkedList.head);
linkedList.addAtTail(4);
printList(linkedList.head);
System.out.println(linkedList.get(4));
linkedList.addAtHead(4);
printList(linkedList.head);
linkedList.addAtIndex(5, 0);
printList(linkedList.head);
linkedList.addAtHead(6);
printList(linkedList.head);
// System.out.println(linkedList.get(1));
//// printList(linkedList.head);
// System.out.println();
// linkedList.deleteAtIndex(1); //现在链表是1-> 3
// printList(linkedList.head);
// System.out.println();
// System.out.println(linkedList.get(1));
}
@Test
public void test2() {
MyLinkedList1 linkedList = new MyLinkedList1();
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.deleteAtIndex(1);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(7);
printList(linkedList.head);
linkedList.addAtHead(3);
printList(linkedList.head);
linkedList.addAtHead(2);
printList(linkedList.head);
linkedList.addAtHead(5);
printList(linkedList.head);
linkedList.addAtTail(5);
printList(linkedList.head);
System.out.println(linkedList.get(5));
linkedList.deleteAtIndex(6);
printList(linkedList.head);
linkedList.deleteAtIndex(4);
printList(linkedList.head);
}
@Test
public void test3() {
MyLinkedList1 linkedList = new MyLinkedList1();
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.addAtTail(3);
printList(linkedList.head);
linkedList.addAtIndex(1, 2);
printList(linkedList.head);
System.out.println(linkedList.get(1));
linkedList.deleteAtIndex(1);
printList(linkedList.head);
System.out.println(linkedList.get(1));
}
@Test
public void test4() {
MyLinkedList1 linkedList = new MyLinkedList1();
linkedList.addAtHead(4);
printList(linkedList.head);
System.out.println(linkedList.get(1));
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.addAtHead(5);
printList(linkedList.head);
linkedList.deleteAtIndex(3);
printList(linkedList.head);
linkedList.addAtHead(7);
printList(linkedList.head);
System.out.println(linkedList.get(3));
System.out.println(linkedList.get(3));
System.out.println(linkedList.get(3));
linkedList.addAtHead(1);
printList(linkedList.head);
linkedList.deleteAtIndex(4);
printList(linkedList.head);
}
/*
内存超过99.29%速度超过30.86%
*/
}
class ListNode {
public int val;
public ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}

View File

@ -0,0 +1,157 @@
package com.markilue.leecode.listnode;
import org.junit.Test;
/**
* 移除链表元素:
* 给你一个链表的头节点 head 和一个整数 val 请你删除链表中所有满足 Node.val == val 的节点并返回 新的头节点
*/
public class RemoveElement {
@Test
public void test1() {
ListNode listNode = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(6);
ListNode listNode4 = new ListNode(3);
ListNode listNode5 = new ListNode(4);
ListNode listNode6 = new ListNode(5);
ListNode listNode7 = new ListNode(6);
listNode.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
listNode5.next = listNode6;
listNode6.next = listNode7;
ListNode result = removeElements(listNode, 6);
print(result);
}
@Test
public void test2() {
ListNode listNode = removeElements(null, 1);
print(listNode);
}
@Test
public void test3() {
ListNode listNode = new ListNode(7);
ListNode listNode2 = new ListNode(7);
ListNode listNode3 = new ListNode(7);
ListNode listNode4 = new ListNode(7);
listNode.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
ListNode result = removeElements1(listNode, 7);
print(result);
}
/**
* 内存击败45%速度击败50%
* @param head
* @param val
* @return
*/
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
//创建一个虚拟头节点用于返回
ListNode listNode = new ListNode();
listNode.next = head;
ListNode recu = listNode;
while (recu.next != null) {
print(listNode);
System.out.println();
if (recu.next.val == val) {
recu.next = recu.next.next;
} else {
recu = recu.next;
}
}
return listNode.next;
}
/**
* 改进版不一定要直接找下一个可以找下一个不是val的元素
* 速度还是击败50%内存击败84%
* @param head
* @param val
* @return
*/
public ListNode removeElements1(ListNode head, int val) {
if (head == null) {
return head;
}
//创建一个虚拟头节点用于返回
ListNode listNode = new ListNode();
listNode.next = head;
ListNode recu = listNode;
while (recu.next != null) {
print(listNode);
System.out.println();
if (recu.next.val == val) {
while (recu.next.next!=null&&recu.next.next.val==val){
recu.next.next=recu.next.next.next;
}
recu.next = recu.next.next;
} else {
recu = recu.next;
}
}
return listNode.next;
}
public class ListNode {
public int val;
public ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public void print(ListNode listNode) {
if (listNode == null) {
System.out.println("链表为空");
return;
}
while (listNode != null) {
System.out.printf(listNode.val + "->");
listNode = listNode.next;
}
}
}

View File

@ -0,0 +1,104 @@
package com.markilue.leecode.listnode;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.listnode
* @Author: dingjiawen
* @CreateTime: 2022-09-06 09:21
* @Description: TODO leecode第206题翻转链表给你单链表的头节点 head ,请你反转链表并返回反转后的链表
* @Version: 1.0
*/
public class ReverseList {
@Test
public void test() {
ListNode listNode = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
// ListNode listNode6 = new ListNode(5);
// ListNode listNode7 = new ListNode(6);
listNode.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
// listNode5.next = listNode6;
// listNode6.next = listNode7;
ListNode result = reverseList1(listNode);
print(result);
}
public void print(ListNode listNode) {
if (listNode == null) {
System.out.println("链表为空");
return;
}
while (listNode != null) {
System.out.printf(listNode.val + "->");
listNode = listNode.next;
}
}
/**
* 自己的实现和代码随想录中的双指针法类似
* 内存超过85.07%,速度超过100%
* @param head
* @return
*/
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode fakeHead = new ListNode(0);
ListNode temp = head;
ListNode temp1 ;
while (temp != null) {
temp1=temp.next;
temp.next=fakeHead.next;
fakeHead.next=temp;
temp=temp1;
}
return fakeHead.next;
}
/**
* 递归法:
* 内存超过78%速度超过100%
*/
public ListNode reverseList1(ListNode head) {
// ListNode fakeHead = new ListNode(0);
return reverseList2(null,head);
}
public ListNode reverseList2(ListNode fakeHead,ListNode head) {
if (head==null){
return fakeHead;
}
ListNode temp=head.next;
//这个head就正是翻转之后的存放的作为下一次的fakeHead
head.next=fakeHead;
return reverseList2(head,temp);
}
}

View File

@ -1,4 +1,4 @@
package listnode; package com.markilue.leecode.listnode;
/** /**
* Definition for singly-linked list. * Definition for singly-linked list.
* public class ListNode { * public class ListNode {
@ -10,8 +10,6 @@ package listnode;
* } * }
*/ */
import com.sun.corba.se.impl.resolver.SplitLocalResolverImpl;
/** /**
* 2.两数相加 * 2.两数相加
*/ */

View File

@ -1,4 +1,4 @@
package listnode; package com.markilue.leecode.listnode;
public class mergeKLists { public class mergeKLists {

View File

@ -1,4 +1,4 @@
package listnode; package com.markilue.leecode.listnode;
public class mergeTwoLists { public class mergeTwoLists {

View File

@ -1,4 +1,4 @@
package listnode; package com.markilue.leecode.listnode;
/** /**
* 删除链表的倒数第N个节点 * 删除链表的倒数第N个节点
@ -8,22 +8,34 @@ public class removeNthFromEnd {
public static void main(String[] args) { public static void main(String[] args) {
ListNode l2 = new ListNode(1); ListNode l2 = new ListNode(1);
// l2.next = new ListNode(2); l2.next = new ListNode(2);
// l2.next.next = new ListNode(3); l2.next.next = new ListNode(3);
// l2.next.next.next =new ListNode(4); l2.next.next.next =new ListNode(4);
// l2.next.next.next.next =new ListNode(5); l2.next.next.next.next =new ListNode(5);
// l2.next.next.next.next.next = new ListNode(9); // l2.next.next.next.next.next = new ListNode(9);
// l2.next.next.next.next.next.next =new ListNode(9); // l2.next.next.next.next.next.next =new ListNode(9);
// l2.next.next.next.next.next.next.next = new ListNode(9); // l2.next.next.next.next.next.next.next = new ListNode(9);
// l2.next.next.next.next.next.next.next.next =new ListNode(9); // l2.next.next.next.next.next.next.next.next =new ListNode(9);
// l2.next.next.next.next.next.next.next.next.next =new ListNode(9); // l2.next.next.next.next.next.next.next.next.next =new ListNode(9);
removeNthFromEnd3(l2, 1); ListNode listNode = removeNthFromEnd(l2, 2);
print(listNode);
getLength(l2); // getLength(l2);
} }
public static void print(ListNode listNode) {
if (listNode == null) {
System.out.println("链表为空");
return;
}
while (listNode != null) {
System.out.printf(listNode.val + "->");
listNode = listNode.next;
}
}
/** /**
* 获取长度length之后遍历到第length-n的位置删除 * 获取长度length之后遍历到第length-n的位置删除
* *
@ -122,6 +134,40 @@ public class removeNthFromEnd {
} }
/**
* 快慢指针再次尝试
* @param head
* @param n
* @return
*/
public static ListNode removeNthFromEnd(ListNode head, int n) {
if(head==null){
return head;
}
ListNode fakeHead=new ListNode(0);
fakeHead.next=head;
ListNode fast=fakeHead;
ListNode slow=fakeHead;
//慢指针一直移动快指针只在满指针移了n次以后再移动则慢指针到达末尾时快指针就到了倒数第N的前一个位置直接删除即可
while (slow.next!=null){
slow=slow.next;
n--;
if(n<0){
fast=fast.next;
}
}
fast.next=fast.next.next;
return fakeHead.next;
}

View File

@ -1,6 +1,4 @@
package listnode.selftry; package com.markilue.leecode.listnode.selftry;
import listnode.mergeTwoLists;
public class mergeKLists { public class mergeKLists {

View File

@ -1,7 +1,4 @@
package listnode.selftry; package com.markilue.leecode.listnode.selftry;
import listnode.addTwoNumbers;
import listnode.removeNthFromEnd;
public class mergeTwoLists { public class mergeTwoLists {

View File

@ -1,4 +1,4 @@
package listnode.selftry; package com.markilue.leecode.listnode.selftry;
/** /**
* 删除链表的倒数第N个节点 * 删除链表的倒数第N个节点

View File

@ -1,6 +1,6 @@
package listnode.selftry; package com.markilue.leecode.listnode.selftry;
import listnode.swapPairs; import com.markilue.leecode.listnode.swapPairs;
public class reverseKGroup { public class reverseKGroup {

View File

@ -1,4 +1,4 @@
package listnode.selftry; package com.markilue.leecode.listnode.selftry;

View File

@ -1,4 +1,4 @@
package listnode; package com.markilue.leecode.listnode;

View File

@ -1,6 +1,4 @@
package sort; package com.markilue.leecode.sort;
import com.sun.corba.se.impl.ior.FreezableList;
import java.util.*; import java.util.*;
@ -34,7 +32,7 @@ public class ThreeSum {
public static List<List<Integer>> threeSum(int[] nums) { public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> parentList = new ArrayList<>(); List<List<Integer>> parentList = new ArrayList<List<Integer>>();
if (nums.length < 3) { if (nums.length < 3) {
return parentList; return parentList;
@ -78,7 +76,7 @@ public class ThreeSum {
} }
//如果是第二个条件跳出的循环 //如果是第二个条件跳出的循环
if (nums[j] + nums[k] == need) { if (nums[j] + nums[k] == need) {
List<Integer> list = new ArrayList<>(); List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]); list.add(nums[i]);
list.add(nums[j]); list.add(nums[j]);
list.add(nums[k]); list.add(nums[k]);
@ -99,7 +97,7 @@ public class ThreeSum {
//更简洁一点的思路 //更简洁一点的思路
public static List<List<Integer>> threeSum1(int[] nums) { public static List<List<Integer>> threeSum1(int[] nums) {
List<List<Integer>> parentList = new ArrayList<>(); List<List<Integer>> parentList = new ArrayList<List<Integer>>();
if (nums.length < 3) { if (nums.length < 3) {
return parentList; return parentList;

View File

@ -0,0 +1,123 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 12:06
* @Description:
* TODO 力扣150题 逆波兰表达式求值:
* 根据 逆波兰表示法求表达式的值
* 有效的算符包括+-*/每个运算对象可以是整数也可以是另一个逆波兰表达式
* 注意两个整数之间的除法只保留整数部分
* 可以保证给定的逆波兰表达式总是有效的换句话说表达式总会得出有效数值且不存在除数为 0 的情况
* @Version: 1.0
*/
public class EvalRPN {
@Test
public void test(){
String[] tokens = {"2", "1", "+", "3", "*"};
System.out.println(evalRPN(tokens));
}
@Test
public void test1(){
String[] tokens = {"4","13","5","/","+"};
System.out.println(evalRPN(tokens));
}
@Test
public void test2(){
String[] tokens = {"10","6","9","3","+","-11","*","/","*","17","+","5","+"};
System.out.println(evalRPN(tokens));
}
@Test
public void test3(){
String[] tokens = {"2", "1", "+", "3", "*"};
System.out.println(evalRPN(tokens));
}
/**
* 由于没有括号等操作考虑使用一个stack解决遇上运算符就在往前消两栈计算出的结果再入栈
* 由于保证了给定的逆波兰表达式总是有效的所以不需要额外的判断
* 速度击败55%内存击败47%
* @param tokens
* @return
*/
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String token : tokens) {
if(!token.equals("+")&&!token.equals("-")&&!token.equals("*")&&!token.equals("/")){
stack.push(Integer.valueOf(token));
}else {
int num2=stack.pop();
int num1=stack.pop();
if(token.equals("+")){
stack.push(num1+num2);
}else if(token.equals("-")){
stack.push(num1-num2);
}else if(token.equals("*")){
stack.push(num1*num2);
}else if(token.equals("/")){
stack.push(num1/num2);
}
}
}
return stack.pop();
}
/**
* 官方给的使用数组模拟栈代码
* @param tokens
* @return
*/
public int evalRPN1(String[] tokens) {
int n = tokens.length;
int[] stack = new int[(n + 1) / 2];
int index = -1;
for (int i = 0; i < n; i++) {
String token = tokens[i];
switch (token) {
case "+":
index--;
stack[index] += stack[index + 1];
break;
case "-":
index--;
stack[index] -= stack[index + 1];
break;
case "*":
index--;
stack[index] *= stack[index + 1];
break;
case "/":
index--;
stack[index] /= stack[index + 1];
break;
default:
index++;
stack[index] = Integer.parseInt(token);
}
}
return stack[index];
}
}

View File

@ -0,0 +1,87 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.Stack;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 11:17
* @Description: TODO 力扣20题 有效的括号:
* 给定一个只包括 '('')''{''}''['']'的字符串 s 判断字符串是否有效
* <p>
* 有效字符串需满足
* 左括号必须用相同类型的右括号闭合
* 左括号必须以正确的顺序闭合
* 每个右括号都有一个对应的相同类型的左括号
* @Version: 1.0
*/
public class IsValid {
@Test
public void test() {
String s = "{([([])])}";
System.out.println(isValid(s));
}
/**
* 速度击败98%内存击败35.56%
*
* @param s
* @return
*/
public boolean isValid(String s) {
if (s.length() % 2 != 0) {
return false;
}
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '{' || c == '(' || c == '[') {
stack.push(c);
}
if (c == ')') {
if (stack.size() == 0 || stack.pop() != '(') {
return false;
}
}
if (c == '}') {
if (stack.size() == 0 || stack.pop() != '{') {
return false;
}
}
if (c == ']') {
if (stack.size() == 0 || stack.pop() != '[') {
return false;
}
}
}
if (!stack.empty()) {
return false;
}
return true;
}
/*
某大佬巧妙思路不过就是效率不高
执行用时47 ms, 在所有 Java 提交中击败了5.62%的用户
内存消耗41.4 MB, 在所有 Java 提交中击败了5.01%的用户
*/
public boolean isValid1(String s) {
while (true) {
int l = s.length();
s = s.replace("()", "");
s = s.replace("{}", "");
s = s.replace("[]", "");
if (s.length() == l) {
return l == 0;
}
}
}
}

View File

@ -0,0 +1,331 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-14 09:31
* @Description: TODO 力扣239题 滑动窗口最大值:
* 给你一个整数数组 nums有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧你只可以看到在滑动窗口内的 k个数字滑动窗口每次只向右移动一位
* 返回 滑动窗口中的最大值
* @Version: 1.0
*/
public class MaxSlidingWindow {
@Test
public void test() {
int[] nums = {1, 3, -1, -3, -5, -7, -9, 5, 3, 6, 7};
int k = 3;
int[] ints = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(ints));//[3, 3, -1, -3, -5, 5, 5, 6, 7]
}
@Test
public void test1() {
int[] nums = {1, 3, -1, -3, -5, -3, -9, 5, 3, 6, 7};//[3, 3, -1, -3, -3, 5, 5, 6, 7]
int k = 3;
int[] ints = maxSlidingWindow3(nums, k);
System.out.println(Arrays.toString(ints));
}
@Test
public void test4() {
int[] nums = {1, 3, -1, -3, -3, -3, -9, 5, 3, 6, 7};//[3, 3, -1, -3, -3, 5, 5, 6, 7]
int k = 3;
int[] ints = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(ints));
}
@Test
public void test2() {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
int[] ints = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(ints));//[3, 3, 5, 5, 6, 7]
}
@Test
public void test3() {
int[] nums = {1};
int k = 1;
int[] ints = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(ints));//[1]
}
@Test
public void test5() {
int[] nums = {1, 3, 1, 2, 0, 5};
int k = 3;
int[] ints = maxSlidingWindow4(nums, k);
System.out.println(Arrays.toString(ints));//[3, 3, 2, 5]
}
@Test
public void test6() {
int[] nums = {7,2,4};
int k = 2;
int[] ints = maxSlidingWindow3(nums, k);
System.out.println(Arrays.toString(ints));//[3, 3, 2, 5]
}
/**
* 本人思路:滑动窗口看似需要知道全部的值实际上只需要知道连续增大的数及其他的索引即可
* 例如[1,3,-1,-3,5,3,6,7] ,事实上真正重要的信息是[1,3,5,6,7]以及他们的索引[0,1,4,6,7]
* 可以发现的是这个数是否被使用可以看其索引和左右的差值 (1-0)+(4-1-1)=3 (3+1)-3+1=2所以3使用2次;同理,(4-1-1)+(6-4-1)=3 (3+1)-3+1=2所以5使用2次
* 其中添加了太多逻辑判断导致逻辑混乱目前还有问题
*
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow(int[] nums, int k) {
//使用一个栈,用于存放进来的数据
Stack<Integer> stack = new Stack<>();
boolean flag = false;
int lastIndex = 0;
//记录stack栈顶的数的索引
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (stack.empty()) {
stack.push(nums[i]);
//被迫放的将flag改一下
flag = true;
index = i;
continue;
}
//一个数与栈顶元素的索引之差大于3,那么就把他固化下来,并且把他的下一个元素放进去,并设置flag
if (i - index >= k && flag) {
//将flag设置回来
// flag=true;
// index=i-k;
lastIndex = index;
stack.push(nums[i - k + 1]);
//再把flag设置回去
index = i - k + 1;
}
if (nums[i] >= stack.peek() && !flag) {
//前面的数是想要的数
lastIndex = index;
stack.push(nums[i]);
index = i;
continue;
} else if (nums[i] >= stack.peek() && flag) {
//前面的数不是想要的数
stack.pop();
if (stack.empty() || stack.peek() < nums[i]) {
lastIndex = index;
flag = false;
}
index = i;
stack.push(nums[i]);
// lastIndex=index;
continue;
}
if (nums[i] < stack.peek() && !flag) {
//栈顶元素想要再放一次
stack.push(stack.peek());
//这个元素可能要,所以放一下
lastIndex = index;
stack.push(nums[i]);
index = i;
flag = true;
continue;
} else if (nums[i] < stack.peek() && flag && index - lastIndex + (i - index) + 1 > k) {
//中间的数是大数但是没有上一次的大但是已经达到范围了,就将这个数设置成想要的
flag = false;
}
}
int[] result = new int[nums.length - k + 1];
for (int i = result.length - 1; i >= 0; i--) {
result[i] = stack.pop();
}
return result;
}
/**
* 官方使用优先队列完成的做法:时间复杂度O(nlogn)
* 对于最大值我们可以想到一种非常合适的数据结构那就是优先队列其中的大根堆可以帮助我们实时维护一系列元素中的最大值
* 对于本题而言初始时我们将数组 nums 的前 k 个元素放入优先队列中每当我们向右移动窗口时我们就可以把一个新的元素放入优先队列中此时堆顶的元素就是堆中所有元素的最大值然而这个最大值可能并不在滑动窗口中在这种情况下这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧因此当我们后续继续向右移动窗口时这个值就永远不可能出现在滑动窗口中了我们可以将其永久地从优先队列中移除
* 我们不断地移除堆顶的元素直到其确实出现在滑动窗口中此时堆顶元素就是滑动窗口中的最大值为了方便判断堆顶元素与滑动窗口的位置关系我们可以在优先队列中存储二元组(num,index)表示元素 num 在数组中的下标为 index
* <p>
* TODO 本质上就是维护一个可以比较大小的优先队列,队列中存放(,索引)
* 当遍历到索引等于这个队列中第一个数的索引时就把第一个数弹出去每次遍历都peek第一个数,则就是最大的数
* 遍历数组复杂度O(n),将元素放入优先队列O(logn),因此时间复杂度O(nlogn)
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow1(int[] nums, int k) {
int n = nums.length;
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
//先构建窗口大小的优先队列
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.offer(new int[]{nums[i], i});
//如果索引小于等于这个队列中第一个数的索引就弹出这个数因为这个数已经越界了
while (pq.peek()[1] <= i - k) {
pq.poll();
}
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
/**
* 官方使用单调队列的做法:时间复杂度O(n)
* 由于我们需要求出的是滑动窗口的最大值如果当前的滑动窗口中有两个下标 i j其中 i j 的左侧i < j并且 ii 对应的元素不大于 jj 对应的元素nums[i]nums[j]那么会发生什么呢
* 当滑动窗口向右移动时只要 i 还在窗口中那么 j 一定也还在窗口中这是 i j 的左侧所保证的因此由于 nums[j] 的存在nums[i] 一定不会是滑动窗口中的最大值了我们可以将 nums[i] 永久地移除
* 因此我们可以使用一个队列存储所有还没有被移除的下标在队列中这些下标按照从小到大的顺序被存储并且它们在数组 nums 中对应的值是严格单调递减的因为如果队列中有两个相邻的下标它们对应的值相等或者递增那么令前者为 ii后者为 jj就对应了上面所说的情况 nums[i] 会被移除这就产生了矛盾
* 当滑动窗口向右移动时我们需要把一个新的元素放入队列中为了保持队列的性质我们会不断地将新的元素与队尾的元素相比较如果前者大于等于后者那么队尾的元素就可以被永久地移除我们将其弹出队列我们需要不断地进行此项操作直到队列为空或者新的元素小于队尾的元素
* 由于队列中下标对应的元素是严格单调递减的因此此时队首下标对应的元素就是滑动窗口中的最大值但与方法一中相同的是此时的最大值可能在滑动窗口左边界的左侧并且随着窗口向右移动它永远不可能出现在滑动窗口中了因此我们还需要不断从队首弹出元素直到队首元素在窗口中为止
* 为了可以同时弹出队首和队尾的元素我们需要使用双端队列满足这种单调性的双端队列一般称作单调队列
* <p>
* TODO 实际理念就是:维护一个两端都可以操作的队列(这个队列是单调的)
* 1在一个窗口中,如果一个数组的左边元素比右边元素小那么他就永远不会被用到那么将它从队列的尾部移除
* 2如果在右边但是比他小,那么他可能被用到,则加在队列的尾部
* 3 那么根据1和2可以得到一个单调递减的队列了,队列最前面一定是最大的元素
* 4通过每次i++之后都会result数组都会赋值一次,保证进来的数一定是窗口里的数永远维护着窗口
*
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow2(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<Integer>();
//先构建第一个窗口
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()];
//由于i每+1ans都会赋值一个元素,因此进来的数永远在窗口里,不存在把队列里的数全挪完了窗口数也超过的情况
for (int i = k; i < n; ++i) {
//移除比他小的元素
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
//移除过期元素
while (deque.peekFirst() <= i - k) {
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
/**
* 自己尝试实现一次官方单调队列(双端队列)的做法
* 速度超过66%内存超过90%
*/
public int[] maxSlidingWindow3(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<>();
//创建第一个窗口
for (int i = 0; i < k; i++) {
while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
deque.removeLast();
}
deque.offerLast(i);
}
//创建结果
int[] result = new int[n - k + 1];
//将第一个窗口的结果赋值给result
result[0] = nums[deque.peekFirst()];
for (int i = k; i < n; i++) {
//如果新来的数比这个数大,就把他移除
while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
deque.removeLast();
}
deque.offerLast(i);
//移除过期元素
while (deque.peekFirst() <= i - k) {
deque.removeFirst();
}
//将最前面的数赋值给结果
result[i - k + 1] = nums[deque.peekFirst()];
}
return result;
}
/**
* 官方分块+预处理法:时间复杂度O(n)
*
* TODO 实际上就是将数据分头处理
* 1窗口两端的一定是可以被窗口长度整除的这些数单独跟窗口两端的进行比较
* 2不在窗口两端的则需要在窗口前缀和窗口后缀中找到最大值在取前缀和后缀中的最大值,就可以得到那个位置上的最大值
* 3)因此根据2)可以提前从前往后遍历和从后往前遍历分别获取到最大值
* @param nums
* @param k
* @return
*/
public int[] maxSlidingWindow4(int[] nums, int k) {
int n = nums.length;
int[] prefixMax = new int[n];
int[] suffixMax = new int[n];
for (int i = 0; i < n; ++i) {
if (i % k == 0) {
prefixMax[i] = nums[i];
}
else {
prefixMax[i] = Math.max(prefixMax[i - 1], nums[i]);
}
}
for (int i = n - 1; i >= 0; --i) {
if (i == n - 1 || (i + 1) % k == 0) {
suffixMax[i] = nums[i];
} else {
suffixMax[i] = Math.max(suffixMax[i + 1], nums[i]);
}
}
int[] ans = new int[n - k + 1];
for (int i = 0; i <= n - k; ++i) {
ans[i] = Math.max(suffixMax[i], prefixMax[i + k - 1]);
}
return ans;
}
}

View File

@ -0,0 +1,111 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.Stack;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 09:17
* @Description:
* TODO 力扣232题 用栈实现队列:
* 请你仅使用两个栈实现先入先出队列队列应当支持一般队列支持的所有操作pushpoppeekempty
*
* 实现 MyQueue
* void push(int x) 将元素 x 推到队列的末尾
* int pop() 从队列的开头移除并返回元素
* int peek() 返回队列开头的元素
* boolean empty() 如果队列为空返回 true 否则返回 false
*
* 说明
* 你只能使用标准的栈操作 也就是只有push to top,peek/pop from top,size, 和is empty操作是合法的
* 你所使用的语言也许不支持栈你可以使用 list 或者 deque双端队列来模拟一个栈只要是标准的栈操作即可
*
* @Version: 1.0
*/
public class MyQueue {
public Stack<Integer> stack1;
public Stack<Integer> stack2;
public MyQueue() {
stack1=new Stack<>();
stack2=new Stack<>();
}
/**
* 本人思路:
* stack1维护一个反向的stack,stack2就维护一个正向的stack
* push时只push进stack1,每调用一次pop就将stack1的值push进stack2
* 速度击败100%,内存击败50%
* 代码随想录与本思路类似但是他额peek复用了pop(),即pop()之后再添加回去另外也不需要每次再pop会stack1,具体见MyQueue1
* @param x
*/
public void push(int x) {
stack1.push(x);
}
public int pop() {
//放入stack2
while (!stack1.empty()){
stack2.push(stack1.pop());
}
//获取结果
int result=stack2.pop();
//放回stack1
while (!stack2.empty()){
stack1.push(stack2.pop());
}
//返回结果
return result;
}
public int peek() {
//放入stack2
while (!stack1.empty()){
stack2.push(stack1.pop());
}
//查看结果结果
int result=stack2.peek();
//放回stack1
while (!stack2.empty()){
stack1.push(stack2.pop());
}
return result;
}
public boolean empty() {
return stack1.empty();
}
@Test
public void test(){
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
System.out.println(myQueue.peek());// return 1
System.out.println(myQueue.pop());// return 1, queue is [2]
System.out.println(myQueue.empty());// return false
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/

View File

@ -0,0 +1,99 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.Stack;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 09:17
* @Description:
* TODO 力扣232题 用栈实现队列:
* 请你仅使用两个栈实现先入先出队列队列应当支持一般队列支持的所有操作pushpoppeekempty
*
* 实现 MyQueue
* void push(int x) 将元素 x 推到队列的末尾
* int pop() 从队列的开头移除并返回元素
* int peek() 返回队列开头的元素
* boolean empty() 如果队列为空返回 true 否则返回 false
*
* 说明
* 你只能使用标准的栈操作 也就是只有push to top,peek/pop from top,size, 和is empty操作是合法的
* 你所使用的语言也许不支持栈你可以使用 list 或者 deque双端队列来模拟一个栈只要是标准的栈操作即可
*
* @Version: 1.0
*/
public class MyQueue1 {
public Stack<Integer> stack1;
public Stack<Integer> stack2;
public MyQueue1() {
stack1=new Stack<>();
stack2=new Stack<>();
}
/**
* 官方思路:
* 但是他额peek复用了pop(),即pop()之后再添加回去另外也不需要每次再pop会stack1,具体见MyQueue1
* 由于充分利用了两个stack,内存利用率大大提高
* 速度击败100%内存击败96.07%
* @param x
*/
public void push(int x) {
stack1.push(x);
}
public int pop() {
dumpStack();
//返回结果
return stack2.pop();
}
public int peek() {
dumpStack();
return stack2.peek();
}
public boolean empty() {
return stack1.empty()&& stack2.empty();
}
private void dumpStack(){
if(stack2.empty()){
while (!stack1.empty()){
stack2.push(stack1.pop());
}
}
}
@Test
public void test(){
MyQueue1 myQueue = new MyQueue1();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
System.out.println(myQueue.peek());// return 1
System.out.println(myQueue.pop());// return 1, queue is [2]
System.out.println(myQueue.empty());// return false
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/

View File

@ -0,0 +1,120 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.PriorityBlockingQueue;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 09:59
* @Description: TODO 力扣225题 用队列实现栈:
* 请你仅使用两个队列实现一个后入先出LIFO的栈并支持普通栈的全部四种操作pushtoppop empty
* 实现 MyStack
* void push(int x) 将元素 x 压入栈顶
* int pop() 移除并返回栈顶元素
* int top() 返回栈顶元素
* boolean empty() 如果栈是空的返回 true 否则返回 false
* <p>
* <p>
* 注意
* 你只能使用队列的基本操作 也就是push to backpeek/pop from frontsize 和is empty这些操作
* 你所使用的语言也许不支持队列你可以使用 list 列表或者 deque双端队列来模拟一个队列, 只要是标准的队列操作即可
* @Version: 1.0
*/
public class MyStack {
public Queue<Integer> queue1;
public Queue<Integer> queue2;
public MyStack() {
queue1 = new ArrayDeque<>();
queue2 = new ArrayDeque<>();
}
/**
* 上述方式是使用两个队列实现栈事实上还可以使用一个队列实现栈,具体参考MyStack1
* 速度击败100%内存击败50%
* @param x
*/
public void push(int x) {
if(queue1.isEmpty()&&queue2.isEmpty()){
queue1.add(x);
return;
}
if(queue1.isEmpty()){
//queue1为空,那么queue2不为空
queue1.add(x);
while (!queue2.isEmpty()){
queue1.add(queue2.poll());
}
}else if(queue2.isEmpty()){
//queue1为空,那么queue2为空
queue2.add(x);
while (!queue1.isEmpty()){
queue2.add(queue1.poll());
}
}
}
public int pop() {
if(!queue1.isEmpty()){
return queue1.poll();
}else {
return queue2.poll();
}
}
public int top() {
if(!queue1.isEmpty()){
return queue1.peek();
}else {
return queue2.peek();
}
}
public boolean empty() {
return queue1.isEmpty()&&queue2.isEmpty();
}
// public void dumpStack(){
// if(queue2.isEmpty()){
// while (!queue1.isEmpty()){
// queue2.add(queue1.poll());
// }
// }
// }
@Test
public void test() {
MyStack obj = new MyStack();
obj.push(1);
obj.push(2);
obj.push(3);
int param_2 = obj.pop();
int param_3 = obj.top();
boolean param_4 = obj.empty();
System.out.println(param_2);
System.out.println(param_3);
System.out.println(param_4);
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/

View File

@ -0,0 +1,95 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Queue;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-13 09:59
* @Description: TODO 力扣225题 用队列实现栈:
* 请你仅使用两个队列实现一个后入先出LIFO的栈并支持普通栈的全部四种操作pushtoppop empty
* 实现 MyStack
* void push(int x) 将元素 x 压入栈顶
* int pop() 移除并返回栈顶元素
* int top() 返回栈顶元素
* boolean empty() 如果栈是空的返回 true 否则返回 false
* <p>
* <p>
* 注意
* 你只能使用队列的基本操作 也就是push to backpeek/pop from frontsize 和is empty这些操作
* 你所使用的语言也许不支持队列你可以使用 list 列表或者 deque双端队列来模拟一个队列, 只要是标准的队列操作即可
* @Version: 1.0
*/
public class MyStack1 {
public Queue<Integer> queue1;
public MyStack1() {
queue1 = new ArrayDeque<>();
}
/**
* 一个队列实现stack,即先把元素加到队列尾部然后再把头pop出在添加到尾部即可
* 速度击败100%内存击败17%
*
* @param x
*/
public void push(int x) {
int size = queue1.size();
queue1.offer(x);
while (size-- > 0) {
queue1.offer(queue1.poll());
}
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
// public void dumpStack(){
// if(queue2.isEmpty()){
// while (!queue1.isEmpty()){
// queue2.add(queue1.poll());
// }
// }
// }
@Test
public void test() {
MyStack1 obj = new MyStack1();
obj.push(1);
obj.push(2);
obj.push(3);
int param_2 = obj.pop();
int param_3 = obj.top();
boolean param_4 = obj.empty();
System.out.println(param_2);
System.out.println(param_3);
System.out.println(param_4);
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/

View File

@ -0,0 +1,129 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-16 09:24
* @Description: TODO 力扣347题 前k个高频元素
* 给你一个整数数组 nums 和一个整数 k 请你返回其中出现频率前 k 高的元素你可以按 任意顺序 返回答案
* @Version: 1.0
*/
public class TopKFrequent {
@Test
public void test() {
int[] nums = {1, 1, 1, 2, 2, 3};
int k = 2;
int[] result = topKFrequent1(nums, k);
System.out.println(Arrays.toString(result));
}
@Test
public void test1() {
int[] nums = {1};
int k = 1;
int[] result = topKFrequent(nums, k);
System.out.println(Arrays.toString(result));
}
/**
* 自己的思路一:先给nums排序那么相同的数一定在临近然后遍历一遍获取count, ->如果使用数组进行记录则数组大小跟nums里最大的数有关,这里使用优先队列进行记录
* 整体时间复杂度O(nlogn),因为排序O(nlogn)遍历一遍O(n),放入优先队列O(logn)
* 速度击败99.59%,内存击败13.35%
*
* @param nums
* @param k
* @return
*/
public int[] topKFrequent(int[] nums, int k) {
Arrays.sort(nums);
int lastNum = nums[0];
int count = 0;
//使用优先队列记录<num,count>
PriorityQueue<int[]> deque = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] nums1, int[] nums2) {
return nums1[1] != nums2[1] ? nums2[1] - nums1[1] : nums2[0] - nums1[0];
}
});
for (int i = 0; i < nums.length; i++) {
if (nums[i] == lastNum) {
count += 1;
}
if (nums[i] != lastNum) {
deque.offer(new int[]{lastNum, count});
count = 1;
lastNum = nums[i];
}
//如果是最后一个数了那就直接放
if (i == nums.length - 1) {
deque.offer(new int[]{nums[i], count});
}
}
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = deque.poll()[0];
}
return result;
}
/**
* 官方思路:不用先排序,直接用map装<num,count>,装完以后遍历map,用一个小顶堆维护一个大小为k的堆少于k直接装大于k就和堆顶元素比
* map装复杂度O(n),取map O(1),堆大小只有k,所以堆化操作复杂度O(logk),所以最后时间复杂度O(nlogk)
* 速度超过94.94%内存超过25.05%
*/
public int[] topKFrequent1(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
//创建一个小顶堆
PriorityQueue<int[]> deque = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] nums1, int[] nums2) {
return nums1[1] - nums2[1];
}
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
Integer num = entry.getKey();
Integer count = entry.getValue();
if(deque.size()<k){
deque.offer(new int[]{num,count});
}else {
if(count>deque.peek()[1]){
deque.poll();
deque.offer(new int[]{num,count});
}
}
}
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i]=deque.poll()[0];
}
return result;
}
}

View File

@ -0,0 +1,199 @@
package com.markilue.leecode.stackAndDeque;
import org.junit.Test;
import java.util.Deque;
import java.util.LinkedList;
import java.util.PriorityQueue;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.stackAndDeque
* @Author: dingjiawen
* @CreateTime: 2022-09-18 09:14
* @Description: TODO 力扣42题 接雨水:
* 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图计算按此排列的柱子下雨之后能接多少雨水
* @Version: 1.0
*/
public class Trap {
@Test
public void test() {
int[] height = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
System.out.println(trap3(height));
}
@Test
public void test1() {
int[] height = {4, 2, 0, 3, 2, 5};
System.out.println(trap2(height));
}
@Test
public void test2() {
int[] height = {5, 4, 1, 2};
System.out.println(trap2(height));
}
@Test
public void test3() {
int[] height = {5, 5, 1, 3};
System.out.println(trap3(height));
}
@Test
public void test4() {
int[] height = {2, 0, 1, 1, 0, 1, 3, 2, 1, 2, 1};
System.out.println(trap3(height));
}
/**
* 自己的思路:这题和滑动窗口的题非常相似都是需要记录在右边比他小的值如果右边的比他大则出栈
* 因此仍然使用一个双端队列(单调队列)进行遍历遇上下一个比他大的就出栈则接雨水数就等于索引之差
* 速度击败30.41%内存击败71.84%
*
* @param height
* @return
*/
public int trap(int[] height) {
//int[元素,索引]
Deque<int[]> deque = new LinkedList<>();
//记录上一次出栈的高度
int lastHeight = 0;
int result = 0;
boolean flag = false;
for (int i = 0; i < height.length; i++) {
while (deque.size() != 0 && deque.peek()[0] <= height[i]) {
int[] poll = deque.poll();
//高度乘以宽度=蓄水量 =>(i-poll[1]-1)索引之差即宽度;这次出栈的高度-lastHeight即高度
result += (i - poll[1] - 1) * (poll[0] - lastHeight);
lastHeight = poll[0];
flag = true;
}
//防止在内部时不出站计算不了对应的雨水量的情况
//这里使用flag记录是否进入过上面的接雨水环节如果接过雨水还没出栈完,那么计算那部分的雨水量
//这里使用if即可因为flag出来以后一定遇上的是比他大的第一个数
if (deque.size() != 0 && flag && height[i] > lastHeight) {
int[] peek = deque.peek();
//高度乘以宽度=蓄水量 =>(i-poll[1]-1)索引之差即宽度;这次这次高度-lastHeight即高度
result += (i - peek[1] - 1) * (height[i] - lastHeight);
}
flag = false;
deque.push(new int[]{height[i], i});
}
return result;
}
/**
* 官方动态规划解法:只要记录左边柱子的最高高度和右边柱子的最高高度,就可以计算当前位子的雨水面积
* TODO 当前位置的雨水面积: [min(左边柱子的最高高度,右边柱子的最高高度)-当前柱子高度]*单位宽度
* 因此可以分别将左右两边柱子的最高高度分别记录在两个数组maxLeft和maxRight中这时就用到了动态规划
* 精髓在于当前位置的雨水面积的计算方式
* 时间复杂度O(n),空间复杂度O(n)
* 速度超过76.2%内存超过79.9%
*
* @param height
* @return
*/
public int trap2(int[] height) {
int n = height.length;
int[] maxLeft = new int[n];
maxLeft[0] = height[0];
for (int i = 1; i < n; i++) {
maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
}
int[] maxRight = new int[n];
maxRight[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
maxRight[i] = Math.max(height[i], maxRight[i + 1]);
}
int result = 0;
for (int i = 0; i < n; i++) {
result += Math.min(maxLeft[i], maxRight[i]) - height[i];
}
return result;
}
/**
* 官方单调栈解法:使用一个单调栈维护一个单调递减的栈栈顶元素小于栈低元素栈中存放索引
* 栈中必须要有两个元素才会有凹槽才能接雨水
* 速度击败39.81%,内存击败86.4%
* @param height
* @return
*/
public int trap3(int[] height) {
int n = height.length;
Deque<Integer> deque = new LinkedList<>();
int result=0;
for (int i = 0; i < n; i++) {
while (!deque.isEmpty() && height[i] > height[deque.peek()]) {
int top=deque.pop();
if(deque.isEmpty()){
//栈中必须有两个元素才会有凹槽
break;
}
int left=deque.peek();
int currWidth = i - left - 1;
int currHeight = Math.min(height[left], height[i]) - height[top];
result += currWidth * currHeight;
}
deque.push(i);
}
return result;
}
/**
* 官方进阶双指针法:思路是动态规划法为基础,同时将空间复杂度降到了O(1)
* 注意到下标 i 处能接的雨水量由 leftMax[i] rightMax[i] 中的最小值决定
* 由于数组 leftMax 是从左往右计算数组 rightMax 是从右往左计算因此可以使用双指针和两个变量代替两个数组
*
* TODO 精髓在于:如果左边比右边的小那么就算左边的高度,因为右边比左边大了那么左边的水一定可以蓄起来反之亦然
* 速度超过100%内存超过18.38%
*/
public int trap4(int[] height) {
int ans = 0;
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
//如果左边的小那么左边的水一定可以蓄起来那么就计算左边的水
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
}
}

View File

@ -0,0 +1,63 @@
package com.markilue.leecode.string;
import org.junit.Test;
import java.util.Arrays;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.string
* @Author: dingjiawen
* @CreateTime: 2022-09-12 12:15
* @Description: TODO 力扣459题:重复的子字符串:
* 给定一个非空的字符串 s 检查是否可以通过由它的一个子串重复多次构成
* @Version: 1.0
*/
public class RepeatedSubstringPattern {
@Test
public void test() {
String s = "abcabcabc";
System.out.println(repeatedSubstringPattern(s));
// int[] next1 = getNext1(s);
// System.out.println(Arrays.toString(next1));
}
/**
* 这一题也可以用经典的KMP算法进行解决,又KMP算法可知:如果string由重复子字符串组成其next数组一定是前面全是-1,后面全是升序排列
* "abcabcabc",其next数组为[-1, -1, -1, 0, 1, 2, 3, 4, 5],所以规律是最后一个数+1可以整除是-1的数
* 速度击败74.63%内存击败10.85%
* @param s
* @return
*/
public boolean repeatedSubstringPattern(String s) {
int[] next1 = getNext1(s);
int length = next1.length;
if (next1[length - 1] != -1 && length % (length - next1[length - 1] - 1) == 0) {
return true;
}
return false;
}
public int[] getNext1(String needle) {
int[] next = new int[needle.length()];
next[0] = -1;
int k = -1;
for (int i = 1; i < needle.length(); ++i) {
while (k != -1 && needle.charAt(k + 1) != needle.charAt(i)) {
k = next[k];
}
if (needle.charAt(k + 1) == needle.charAt(i)) {
++k;
}
next[i] = k;
}
return next;
}
}

View File

@ -0,0 +1,147 @@
package com.markilue.leecode.string;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.string
* @Author: dingjiawen
* @CreateTime: 2022-09-09 10:16
* @Description: TODO 力扣541题 反转字符串II:
* 给定一个字符串 s 和一个整数 k从字符串开头算起每计数至 2k 个字符就反转这 2k 字符中的前 k 个字符
* 1)如果剩余字符少于 k 则将剩余字符全部反转
* 2)如果剩余字符小于 2k 但大于或等于 k 则反转前 k 个字符其余字符保持原样
* @Version: 1.0
*/
public class ReverseStr {
@Test
public void test() {
String s = "abcdefghij";
int k = 3;
String s1 = reverseStr(s, k);
System.out.println(s1);
}
@Test
public void test1() {
String s = "abcd";
int k = 2;
String s1 = reverseStr(s, k);
System.out.println(s1);
}
@Test
public void test2() {
String s = "hyzqyljrnigxvdtneasepfahmtyhlohwxmkqcdfehybknvdmfrfvtbsovjbdhevlfxpdaovjgunjqlimjkfnqcqnajmebeddqsgl";
int k = 39;
String s1 = reverseStr(s, k);
System.out.println(s1);
}
@Test
public void test3() {
String s = "abcdefg";
int k = 39;
String s1 = reverseStr(s, k);
System.out.println(s1);
}
/**
* 本人的思路:fori一下i步长为2k,2k内部就自己进行交换for完之后判断i==length还是i<length-k还是i<length-2k做最后的处理
* 速度击败5.11%内存击败8.84%
*
* @param s
* @param k
* @return
*/
public String reverseStr(String s, int k) {
char[] chars = s.toCharArray();
int i = 0;
if (chars.length > 2 * k) {
//留下最后2k以内的范围单独处理,因为要分情况单独处理
for (; i < chars.length - 2 * k; i += 2 * k) {
int z = i;
//前k个字母反转:i和i+k-1交换
while (z < i + k / 2) {
exchange(chars, z, i + k - 1 - z + i);
z++;
}
}
}
//出来以后判断是从什么条件出来的
if (i >= chars.length - k) {
//剩下的长度<k
//有多少交换多少
int z = i;
while (z < i + (chars.length - i) / 2) {
exchange(chars, z, chars.length - 1 - z + i);
z++;
}
} else if (i >= chars.length - 2 * k) {
//剩下的长度<2k
//交换前k个
int z = i;
while (z < i + k / 2) {
exchange(chars, z, i + k - 1 - z + i);
z++;
}
}
String result = "";
for (char aChar : chars) {
result += aChar;
}
return result;
}
/**
* 官方的思路:与本人的类似但是不用留下剩下的做判断,都把他视作交换全部的一部分只不过是左右范围不同
* 速度击败100%内存击败35%
*
* @param s
* @param k
* @return
*/
public String reverseStr1(String s, int k) {
char[] chars = s.toCharArray();
//均是交换只不过是交换的范围不同这样就可以不用分头处理了
for (int i = 0; i < chars.length; i += 2 * k) {
reverse(chars,i,Math.min(i+k,chars.length)-1);
}
return new String(chars);
}
public void reverse(char[] chars,int left,int right){
while (left<right){
char temp =chars[left];
chars[left]=chars[right];
chars[right]=temp;
left++;
right--;
}
}
//交换数组中的两个元素
public void exchange(char[] s, int i, int j) {
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}

View File

@ -0,0 +1,63 @@
package com.markilue.leecode.string;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.string
* @Author: dingjiawen
* @CreateTime: 2022-09-09 09:30
* @Description:
* TODO 力扣344题 反转字符串:
* 编写一个函数其作用是将输入的字符串反转过来输入字符串以字符数组 s 的形式给出
* 不要给另外的数组分配额外的空间你必须原地修改输入数组使用 O(1) 的额外空间解决这一问题
*
* @Version: 1.0
*/
public class ReverseString {
@Test
public void test(){
char[] s = {'h', 'e', 'l', 'l', 'o'};
reverseString(s);
char[] c = {'H','a','b','n','a','h'};
reverseString(c);
}
@Test
public void test1(){
char[] s = {'A',' ','m','a','n',',',' ','a',' ','p','l','a','n',',',' ','a',' ','c','a','n','a','l',':',' ','P','a','n','a','m','a'};
reverseString(s);
}
/**
* 自己的思路:由于不让给数组分配另外的空间因此考虑使用前后交换法找到数组的中间然后前后交换
* 速度击败100% 内存击败24.24%
* @param s
*/
public void reverseString(char[] s) {
if (s.length<=1) {
return;
}
for (int i = 0; i < s.length / 2; i++) {
exchange(s,i, s.length-1-i);
}
// System.out.println(s.toString());
System.out.println(s);
}
//交换数组中的两个元素
public void exchange(char[] s,int i,int j){
char temp=s[i];
s[i]=s[j];
s[j]=temp;
}
}

View File

@ -0,0 +1,104 @@
package com.markilue.leecode.string;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.string
* @Author: dingjiawen
* @CreateTime: 2022-09-09 12:12
* @Description: TODO 力扣151题反转字符串中的单词:
* 给你一个字符串 s 请你反转字符串中 单词 的顺序
* 单词 是由非空格字符组成的字符串s 中使用至少一个空格将字符串中的 单词 分隔开
* 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串
* 注意输入字符串 s中可能会存在前导空格尾随空格或者单词间的多个空格返回的结果字符串中单词间应当仅用单个空格分隔且不包含任何额外的空格
* @Version: 1.0
*/
public class ReverseWords {
@Test
public void test() {
String s = " hello world ";
String s1 = reverseWords(s);
System.out.println(s1);
}
@Test
public void test1() {
String s = " the sky is blue ";
String s1 = reverseWords(s);
System.out.println(s1);
}
/**
* 速度超过12%内存超过5%
* @param s
* @return
*/
public String reverseWords(String s) {
String[] s1 = s.split(" ");
String result = "";
for (int i = s1.length - 1; i >= 0; i--) {
if (!"".equals(s1[i]) ) {
result += s1[i] + " ";
}
}
return result.trim();
}
/**
* 速度超过12%内存超过5%
* @param s
* @return
*/
public String reverseWords1(String s) {
String[] s1 = s.trim().split(" ");
String result = "";
for (int i = s1.length - 1; i >= 0; i--) {
if (!"".equals(s1[i]) ) {
if(i==0){
result += s1[i];
}else {
result += s1[i] + " ";
}
}
}
return result;
}
/**
* 官方使用API的代码
* 速度超过12%内存超过5%
* @param s
* @return
*/
public String reverseWords2(String s) {
// 除去开头和末尾的空白字符
s = s.trim();
// 正则匹配连续的空白字符作为分隔符分割
List<String> wordList = Arrays.asList(s.split("\\s+"));
Collections.reverse(wordList);
return String.join(" ", wordList);
}
}

View File

@ -0,0 +1,167 @@
package com.markilue.leecode.string;
import org.junit.Test;
import java.util.Arrays;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.string
* @Author: dingjiawen
* @CreateTime: 2022-09-12 09:01
* @Description: TODO 力扣28题:实现strStr()
* 实现strStr()函数
* 给你两个字符串haystack needle 请你在 haystack 字符串中找出 needle 字符串出现的第一个位置下标从 0 开始如果不存在则返回 -1
* <p>
* 说明
* 当needle是空字符串时我们应当返回什么值呢这是一个在面试中很好的问题
* 对于本题而言当needle是空字符串时我们应当返回 0 这与 C 语言的strstr()以及 Java 的indexOf()定义相符
* @Version: 1.0
*/
public class StrStr {
@Test
public void test() {
String haystack = "hello";
String needle = "ll";
int i = strStr(haystack, needle);
System.out.println(i);
}
@Test
public void test1() {
String haystack = "aaaaa";
String needle = "bba";
int i = strStr(haystack, needle);
System.out.println(i);
}
@Test
public void test2() {
String haystack = "baabbbbababbbabab";
String needle = "abbab";
int i = strStr(haystack, needle);
System.out.println(i);
}
@Test
public void test3() {
String haystack = "ababaabbbbababbaabaaabaabbaaaabbabaabbbbbbabbaabbabbbabbbbbaaabaababbbaabbbabbbaabbbbaaabbababbabbbabaaabbaabbabababbbaaaaaaababbabaababaabbbbaaabbbabb";
String needle = "abbabbbabaa";
int i = strStr(haystack, needle);
System.out.println(i);
}
@Test
public void test4() {
String haystack = "ababac";
// String needle = "abbabbbabaa";
int[] next = getNext1(haystack);
System.out.println(Arrays.toString(next));
}
/**
* 尝试使用KMP算法:维护一个next数组,存储needle的最小前缀和最小后缀匹配的长度,从而减少从头开始匹配的次数,从而将时间复杂度从O(n*m)降到O(n+m)
* 具体内容参考代码随想录和数据结构与算法之美的讲解内容
* 速度击败39.11%内存击败91.79%
*
* @param haystack
* @param needle
* @return
*/
public int strStr(String haystack, String needle) {
int[] next = getNext(needle);
int j = 0;
for (int i = 0; i < haystack.length(); ++i) {
while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
j = next[j - 1] + 1;
}
if (haystack.charAt(i) == needle.charAt(j)) {
++j;
}
if (j == needle.length()) {
return i - j + 1;
}
}
return -1;
}
public int[] getNext(String needle) {
int[] result = new int[needle.length()];
for (int i = 0; i < needle.length(); i++) {
result[i] = -1;
if (i == 0) {
continue;
}
int right = i;
for (int left = i - 1; left >= 0; left--) {
boolean flag = false;
if (needle.charAt(left) == needle.charAt(right)) {
flag = true;
for (int z = left - 1, k = right - 1; z >= 0; z--, k--) {
if (needle.charAt(z) != needle.charAt(k)) {
flag = false;
break;
}
}
}
if (flag) {
result[i] = left;
break;
}
}
}
return result;
}
/**
* 官方获取next数组的方法精髓在于这次的next[i]可以通过上一次的next[i-1]来获取分为两种情况
* 1如果next[i-1]的下一个数和next[i]的下一个数相等那么最大子串一定是在上一个的基础上+1,对应下面的if的内容
* 2如果next[i-1]的下一个数和next[i]的下一个数不相等那么寻找上一个最大子串看看上一次最大子串的下一个数和next[i]的下一个数相等如果不相等就寻找在上一次的对应于下面的while内容
* 下所述的看就是用来记录上一次最大子串的索引
*
* 速度击败39.11%内存击败73.88%
* 但是只用了1ms
* @param needle
* @return
*/
public int[] getNext1(String needle) {
int[] next = new int[needle.length()];
next[0] = -1;
int k = -1;
for (int i = 1; i < needle.length(); ++i) {
while (k != -1 && needle.charAt(k + 1) != needle.charAt(i)) {
k = next[k];
}
if (needle.charAt(k + 1) == needle.charAt(i)) {
++k;
}
next[i] = k;
}
return next;
}
}

View File

@ -0,0 +1,38 @@
package com.markilue.leecode.test;
import java.text.SimpleDateFormat;
public class Fibonaqi {
/**
* 测试使用时间复杂度为n的斐波那契数列递归法
*
*/
// @Test
// public static void testFibonaqi(){
//
// }
public static void main(String[] args) {
int n=5;
System.out.println(fibonacci(1,1,10));
}
public static int fibonacci(int first,int second,int n){
if(n<=0){
return 0;
}
if(n <3){
return 1;
}else if(n==3){
return first+second;
}
else {
return fibonacci(second,first+second,n-1);
}
}
}

View File

@ -0,0 +1,46 @@
package com.markilue.leecode.test;
import com.markilue.leecode.tree.TreeNode;
import org.junit.Test;
import java.util.ArrayList;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.test
* @Author: dingjiawen
* @CreateTime: 2022-09-21 11:32
* @Description: TODO
* @Version: 1.0
*/
public class test {
//测试ArrayList的index是否发生变化
@Test
public void test(){
// ArrayList<Integer> treeNodes1 = new ArrayList<>();
// for (int i = 0; i < 4; i++) {
// treeNodes1.add(i);
// }
// for (int i = 0; i < 4; i++) {
// System.out.println(treeNodes1.remove(i));
//
// System.out.println(treeNodes1.remove(4 - i));//java.lang.IndexOutOfBoundsException: Index: 4, Size: 3
// System.out.println("=======================");
// }
}
//测试null和null是否相等
@Test
public void test1(){
String s1="1";
String s2="2";
s1=s1+2;
System.out.println(s1);
}
}

View File

@ -0,0 +1,174 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-23 11:48
* @Description: TODO 力扣257题 二叉树的所有路径:
* 给你一个二叉树的根节点 root 任意顺序 返回所有从根节点到叶子节点的路径
* 叶子节点 是指没有子节点的节点
* @Version: 1.0
*/
public class BinaryTreePaths {
@Test
public void test() {
TreeNode root = new TreeNode(3);
TreeNode TreeNode2 = new TreeNode(9);
TreeNode TreeNode3 = new TreeNode(20);
// TreeNode TreeNode4 = new TreeNode(4);
// TreeNode TreeNode5 = new TreeNode(5);
TreeNode TreeNode6 = new TreeNode(15);
TreeNode TreeNode7 = new TreeNode(7);
root.setRight(TreeNode3);
root.setLeft(TreeNode2);
// TreeNode2.setLeft(TreeNode4);
// TreeNode2.setRight(TreeNode5);
TreeNode3.setRight(TreeNode7);
TreeNode3.setLeft(TreeNode6);
System.out.println(binaryTreePaths1(root));
}
/**
* 本人递归法: 本质上算是广度优先算法
* 可以发现官方代码之所以简洁是因为他把当时path记录了下来就不需要一直通过遍历去加上对应的节点
* 速度超过49.92%内存超过34%
* @param root
* @return
*/
public List<String> binaryTreePaths(TreeNode root) {
List<StringBuilder> result1 = new ArrayList<>();
if (root == null) {
return new ArrayList<>();
}
result1.add(new StringBuilder().append(root.val).append("->"));
path(root, result1);
List<String> result = new ArrayList<>();
for (int i = 0; i < result1.size(); i++) {
String s = result1.get(i).toString();
result.add(s.substring(0,s.length()-2));
}
return result;
}
//这里使用StringBuilder,因为他快是因为他适用于同一个string不可变字符串的时候这里每一个只添加一次
//这里处理node的左右节点而不是处理他本身因为涉及到要不要新new上一个List
public void path(TreeNode node, List<StringBuilder> result) {
if (node.left == null && node.right == null) {
return;
} else if (node.left != null && node.right != null) {
List<StringBuilder> temp=new ArrayList<>();
for (StringBuilder s : result) {
temp.add(new StringBuilder().append(s));
s .append(node.left.val) .append("->");
}
path(node.left, result);
for (StringBuilder s : temp) {
s.append(node.right.val) .append("->");
}
path(node.right, temp);
result.addAll(temp);
} else if (node.left != null) {
for (StringBuilder s : result) {
s .append(node.left.val) .append("->");
}
path(node.left, result);
} else {
for (StringBuilder s : result) {
s .append(node.right.val) .append("->");
}
path(node.right, result);
}
}
/**
* 官方递归法: 本质上是深度优先算法 ->前序遍历
* 速度超过99.98%内存超过58.81%
* @param root
* @return
*/
public List<String> binaryTreePaths1(TreeNode root) {
List<String> result = new ArrayList<>();
constructPaths(root,"",result);
return result;
}
//这里使用StringBuilder,因为他快是因为他适用于同一个string不可变字符串的时候这里每一个只添加一次
//这里处理node的左右节点而不是处理他本身因为涉及到要不要新new上一个List
public void constructPaths(TreeNode node,String path, List<String> result) {
if(node!=null){
StringBuilder pathDS = new StringBuilder(path);
pathDS.append(node.val);
//遍历到了叶子结点,可以把结果加入
if(node.left==null&&node.right==null){
result.add(pathDS.toString());
}else{
//不是叶子节点,需要继续遍历
pathDS.append("->");
constructPaths(node.left,pathDS.toString(),result);
constructPaths(node.right,pathDS.toString(),result);
}
}
}
/**
* 官方广度优先算法
* @param root
* @return
*/
public List<String> binaryTreePaths2(TreeNode root) {
List<String> paths = new ArrayList<String>();
if (root == null) {
return paths;
}
Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
Queue<String> pathQueue = new LinkedList<String>();
nodeQueue.offer(root);
pathQueue.offer(Integer.toString(root.val));
//node和path同步offer和poll保证两边一定是相对应的
while (!nodeQueue.isEmpty()) {
TreeNode node = nodeQueue.poll();
String path = pathQueue.poll();
if (node.left == null && node.right == null) {
paths.add(path);
} else {
if (node.left != null) {
nodeQueue.offer(node.left);
pathQueue.offer(new StringBuffer(path).append("->").append(node.left.val).toString());
}
if (node.right != null) {
nodeQueue.offer(node.right);
pathQueue.offer(new StringBuffer(path).append("->").append(node.right.val).toString());
}
}
}
return paths;
}
}

View File

@ -0,0 +1,276 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-25 09:58
* @Description: TODO 力扣106题 从中序与后序遍历序列构造二叉树
* 给定两个整数数组 inorder postorder 其中 inorder 是二叉树的中序遍历 postorder 是同一棵树的后序遍历请你构造并返回这颗二叉树
* @Version: 1.0
*/
public class BuildTree {
@Test
public void test(){
int[] inorder = {9, 3, 15, 20, 7}, postorder = {9, 15, 7, 20, 3};
TreeNode root = buildTree(inorder, postorder);
System.out.println(levelOrder(root));
}
@Test
public void test1(){
int[] inorder = {2,1}, postorder = {2,1};
TreeNode root = buildTree(inorder, postorder);
System.out.println(levelOrder(root));
}
@Test
public void test2(){
int[] inorder = {15,9,10,3,20,5,7,8,4}, postorder = {15,10,9,5,4,8,7,20,3};
TreeNode root = buildTree2(inorder, postorder);
System.out.println(levelOrder(root));
}
/**
* 对于这题本人没有任何思路代码随想录思路:本质上就是寻找中间节点来划分左右树而后序遍历的最后一个节点一定是中间节点:
* TODO 以后序数组的最后一个元素作为切割点先切割中序数组然后根据中序数组反过来切割后序数组一层一层切下去每次后序数组额最后一个元素就是节点元素
* 递归法:
* 速度超过5.22%内存超过5.01%
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree(int[] inorder, int[] postorder) {
TreeNode root = new TreeNode();
if (inorder.length == 0) {
return root;
}
build1(inorder, postorder, root);
return root;
}
public void build(int[] inorder, int[] postorder, TreeNode node) {
node.val = postorder[postorder.length - 1];
int length=postorder.length;
if (length == 1) {
return;
}
//分别分割左右节点的inorder和postorder
int[] newIn1 = new int[length];
int[] newPost1 = new int[length];
int[] newIn2 = new int[length];
int[] newPost2 = new int[length];
int size = 0;
boolean flag = true;
for (int i = 0,j=0; i < length; i++) {
if(node.val==inorder[i]){
flag=false;
continue;
}
if (flag) {
newIn1[i] = inorder[i];
newPost1[i]=postorder[i];
size+=1;
}else {
//这里-1因为root节点需要被跳过但是post不用
newIn2[j] = inorder[i];
newPost2[j]=postorder[i-1];
j++;
}
}
//这里需要判断是否大于0再做递归
if(size>0){
newIn1 = Arrays.copyOf(newIn1, size);
newPost1=Arrays.copyOf(newPost1,size);
node.left=new TreeNode();
build(newIn1,newPost1,node.left);
}
if(length-size-1>0){
newIn2=Arrays.copyOf(newIn2,length-size-1);
newPost2=Arrays.copyOf(newPost2,length-size-1);
node.right=new TreeNode();
build(newIn2,newPost2,node.right);
}
}
//可以改进只需要找到索引避免一直复制速度击败9.2%内存击败5.01%
public void build1(int[] inorder, int[] postorder, TreeNode node) {
node.val = postorder[postorder.length - 1];
int length=postorder.length;
if (length == 1) {
return;
}
//分别分割左右节点的inorder和postorder
int size = 0;
for (; size < length; size++) {
if(inorder[size]==node.val){
break;
}
}
//这里需要判断是否大于0再做递归
if(size>0){
node.left=new TreeNode();
//copyOfRange是左闭右开
build(Arrays.copyOfRange(inorder,0, size),Arrays.copyOfRange(postorder,0,size),node.left);
}
if(length-size-1>0){
node.right=new TreeNode();
build(Arrays.copyOfRange(inorder,size+1,length),Arrays.copyOfRange(postorder,size,length-1),node.right);
}
}
int post_idx;
int[] postorder;
int[] inorder;
Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
/**
* 官方的递归法,只用传索引不传一个数组避免了数组的一直赋值操作加快进度
* 速度超过99.46%内存超过34.84%
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree1(int[] inorder, int[] postorder) {
this.postorder = postorder;
this.inorder = inorder;
// 从后序遍历的最后一个元素开始
post_idx = postorder.length - 1;
// 建立元素下标键值对的哈希表
int idx = 0;
for (Integer val : inorder) {
idx_map.put(val, idx++);
}
return helper(0, inorder.length - 1);
}
public TreeNode helper(int in_left, int in_right) {
// 如果这里没有节点构造二叉树了就结束
if (in_left > in_right) {
return null;
}
// 选择 post_idx 位置的元素作为当前子树根节点
int root_val = postorder[post_idx];
TreeNode root = new TreeNode(root_val);
// 根据 root 所在位置分成左右两棵子树
int index = idx_map.get(root_val);
// 下标减一
post_idx--;
// 构造右子树
root.right = helper(index + 1, in_right);
// 构造左子树
root.left = helper(in_left, index - 1);
return root;
}
/**
* 官方迭代法:
* 1)我们用一个栈和一个指针辅助进行二叉树的构造初始时栈中存放了根节点后序遍历的最后一个节点指针指向中序遍历的最后一个节点
* 2)我们依次枚举后序遍历中除了第一个节点以外的每个节点如果 index 恰好指向栈顶节点
* 那么我们不断地弹出栈顶节点并向左移动 index并将当前节点作为最后一个弹出的节点的左儿子
* 如果 index 和栈顶节点不同我们将当前节点作为栈顶节点的右儿子
* 3)无论是哪一种情况我们最后都将当前的节点入栈
*
* 最后得到的二叉树即为答案
*
* @param inorder
* @param postorder
* @return
*/
public TreeNode buildTree2(int[] inorder, int[] postorder) {
if (postorder == null || postorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(postorder[postorder.length - 1]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = inorder.length - 1;
for (int i = postorder.length - 2; i >= 0; i--) {
int postorderVal = postorder[i];
TreeNode node = stack.peek();
//前面的如果不等,那么就是其右节点
if (node.val != inorder[inorderIndex]) {
node.right = new TreeNode(postorderVal);
stack.push(node.right);
} else {
//右节点处理完毕处理其左节点;如果出栈就证明遇上了需要处理的左节点
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
//同步处理stack和inorder
node = stack.pop();
inorderIndex--;
}
node.left = new TreeNode(postorderVal);
stack.push(node.left);
}
}
return root;
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//使用队列完成
Deque<TreeNode> deque = new LinkedList<TreeNode>();
if (root != null) {
deque.addLast(root);
}
while (deque.size() > 0) {
List<Integer> mid = new ArrayList<>();
int size = deque.size();
//注意这里必须使用size,而不是deque.size因为deque的size一直在发生变化
for (int i = 0; i < size; i++) {
TreeNode poll = deque.poll();
mid.add(poll.val);
if (poll.left != null) deque.offer(poll.left);
if (poll.right != null) deque.offer(poll.right);
}
result.add(mid);
}
return result;
}
}

View File

@ -0,0 +1,132 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-25 12:45
* @Description: TODO 力扣105题 从前序与中序遍历序列构造二叉树:
* 给定两个整数数组preorder inorder其中preorder 是二叉树的先序遍历 inorder是同一棵树的中序遍历请构造二叉树并返回其根节点
* @Version: 1.0
*/
public class BuildTreeII {
@Test
public void test() {
int[] preorder = {3, 9, 20, 15, 7}, inorder = {9, 3, 15, 20, 7};
TreeNode node = buildTree(preorder, inorder);
System.out.println(levelOrder(node));
}
/**
* 核心其实一致即中序遍历可以确定左右节点前后序遍历可以确定根节点;因此根据前后序一直找根节点即可
* 这里运用递归法:
* 速度超过99.46%内存超过60.36%
*
* @param preorder
* @param inorder
* @return
*/
int[] preorder;
//<val,index>
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int wanted=0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return build(0, inorder.length-1);
}
public TreeNode build(int leftIndex, int rightIndex) {
if(leftIndex>rightIndex){
return null;
}
TreeNode node = new TreeNode(preorder[wanted]);
Integer index = map.get(preorder[wanted++]);
node.left=build(leftIndex,index-1);
node.right=build(index+1,rightIndex);
return node;
}
/**
* 官方迭代法:
* 1)我们用一个栈和一个指针辅助进行二叉树的构造初始时栈中存放了根节点前序遍历的第一个节点指针指向中序遍历的第一个节点
* 2)我们依次枚举前序遍历中除了第一个节点以外的每个节点
* 如果 index 恰好指向栈顶节点那么我们不断地弹出栈顶节点并向右移动 index并将当前节点作为最后一个弹出的节点的右儿子
* 如果 index 和栈顶节点不同我们将当前节点作为栈顶节点的左儿子
* 3)无论是哪一种情况我们最后都将当前的节点入栈
*
* @param preorder
* @param inorder
* @return
*/
public TreeNode buildTree1(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < preorder.length; i++) {
int preorderVal = preorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.left = new TreeNode(preorderVal);
stack.push(node.left);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//使用队列完成
Deque<TreeNode> deque = new LinkedList<TreeNode>();
if (root != null) {
deque.addLast(root);
}
while (deque.size() > 0) {
List<Integer> mid = new ArrayList<>();
int size = deque.size();
//注意这里必须使用size,而不是deque.size因为deque的size一直在发生变化
for (int i = 0; i < size; i++) {
TreeNode poll = deque.poll();
mid.add(poll.val);
if (poll.left != null) deque.offer(poll.left);
if (poll.right != null) deque.offer(poll.right);
}
result.add(mid);
}
return result;
}
}

View File

@ -1,11 +1,10 @@
package tree; package com.markilue.leecode.tree;
import lombok.Data;
import lombok.ToString;
import java.util.List; import java.util.List;
import static tree.InorderTraversal.inorderTraversal1; import static com.markilue.leecode.tree.InorderTraversal.inorderTraversal1;
/** /**
* 删除二叉排序树的某个节点 * 删除二叉排序树的某个节点

Some files were not shown because too many files have changed in this diff Show More