Merge branch 'master' of https://gitee.com/dingjiawen/self_example
This commit is contained in:
commit
f55bd38ecd
|
|
@ -125,6 +125,21 @@
|
|||
</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>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
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.util.Collector
|
||||
|
||||
object Example_scala1 {
|
||||
|
||||
|
|
@ -11,28 +14,49 @@ object Example_scala1 {
|
|||
env.setParallelism(1)
|
||||
|
||||
//TODO 读取数据源
|
||||
val stream = env.socketTextStream("localhost", 9999)
|
||||
|
||||
// stream.flatMap(
|
||||
// words =>{
|
||||
// val word = words.split(" ")
|
||||
// val stream = env.socketTextStream("localhost", 9999)
|
||||
val stream = env.fromElements("hello world", "hello world")
|
||||
//
|
||||
// word.map(
|
||||
// word =>{
|
||||
// WordWithCount(word,1L)
|
||||
// }
|
||||
// )
|
||||
// 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))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
|
||||
|
||||
// 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 => {
|
||||
// val word = words.split(" ")
|
||||
// word.map(
|
||||
// word => {
|
||||
// WordWithCount(word, 1L)
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
case class WordWithCount(var word:String,var count:Long)
|
||||
|
||||
case class WordWithCount(var word: String, var count: Long)
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public class Example5 {
|
|||
|
||||
//声明一个状态变量作为累加器
|
||||
//状态变量的可见范围(作用域)的当前key
|
||||
//状态变量是单例,只能被实例化一次
|
||||
//状态变量是单例,只能被实例化一次,
|
||||
private ValueState<Tuple2<Integer,Integer>> valueState;
|
||||
//保存定时器的时间戳
|
||||
private ValueState<Long> timerTs;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ public class Example2 {
|
|||
//迭代器参数中只包含了一个元素,就是增量聚合函数发送过来的聚合结果
|
||||
long windowStart =context.window().getStart();
|
||||
long windowEnd = context.window().getEnd();
|
||||
long count = iterable.iterator().next(); //取出增量聚合函数的哪一个元素
|
||||
long count = iterable.iterator().next(); //取出增量聚合函数的那一个元素
|
||||
collector.collect("用户:"+key+"在窗口"
|
||||
+""+new Timestamp(windowStart)+"~"+new Timestamp(windowEnd)
|
||||
+""+"中的pv次数是:"+count);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class Example7 {
|
|||
env.setParallelism(1);
|
||||
|
||||
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>() {
|
||||
@Override
|
||||
public UserBehavior map(String value) throws Exception {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class Example4 {
|
|||
orderStream.keyBy(r ->r.userId)
|
||||
.intervalJoin(pvStream.keyBy(r -> r.userId))
|
||||
//第一条流和第二条流的哪一段join
|
||||
//最近10min和未来10min以内的
|
||||
//最近10min和未来5min以内的
|
||||
.between(Time.minutes(-10),Time.minutes(5))
|
||||
.process(new ProcessJoinFunction<Event, Event, String>() {
|
||||
@Override
|
||||
|
|
@ -67,7 +67,7 @@ public class Example4 {
|
|||
pvStream.keyBy(r ->r.userId)
|
||||
.intervalJoin(orderStream.keyBy(r -> r.userId))
|
||||
//第一条流和第二条流的哪一段join
|
||||
//最近10min和未来10min以内的
|
||||
//最近5min和未来10min以内的
|
||||
.between(Time.minutes(-5),Time.minutes(10))
|
||||
.process(new ProcessJoinFunction<Event, Event, String>() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public class Example10 {
|
|||
env.setParallelism(1);
|
||||
|
||||
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(
|
||||
new MapFunction<String, UserBehavior>() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectIn
|
|||
* 1.实现接口或者继承类
|
||||
* 2.重写相应的方法
|
||||
* 3.打包
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* <p>
|
||||
* 自定义UDF函数类
|
||||
* 继承Hive提供的GenericUDF类
|
||||
*/
|
||||
|
|
@ -22,6 +22,7 @@ public class CalStringLengthUDF extends GenericUDF {
|
|||
|
||||
/**
|
||||
* 初始化方法
|
||||
*
|
||||
* @param objectInspectors 传入到函数中的参数对应的类型的鉴别器对象
|
||||
* @return 指定函数的返回值类型对象的鉴别器对象
|
||||
* @throws UDFArgumentException
|
||||
|
|
@ -29,12 +30,12 @@ public class CalStringLengthUDF extends GenericUDF {
|
|||
@Override
|
||||
public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {
|
||||
//1.校验函数的参数个数
|
||||
if(objectInspectors==null||objectInspectors.length!=1){
|
||||
if (objectInspectors == null || objectInspectors.length != 1) {
|
||||
throw new UDFArgumentLengthException("函数的参数个数不正确");
|
||||
}
|
||||
//2.校验函数的参数类型,getCategory()返回的是传入的类型,PRIMITIVE表示基本类型
|
||||
if(!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
|
||||
throw new UDFArgumentTypeException(0,"磺酸钠会参数类型不正确");
|
||||
if (!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {
|
||||
throw new UDFArgumentTypeException(0, "函数的参数类型不正确");
|
||||
}
|
||||
//3.返回函数的返回值类型对应的鉴别器类型
|
||||
return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
|
||||
|
|
@ -42,6 +43,7 @@ public class CalStringLengthUDF extends GenericUDF {
|
|||
|
||||
/**
|
||||
* 函数核心处理方法
|
||||
*
|
||||
* @param deferredObjects 传入到函数的参数
|
||||
* @return 函数的返回值
|
||||
* @throws HiveException
|
||||
|
|
@ -50,7 +52,7 @@ public class CalStringLengthUDF extends GenericUDF {
|
|||
|
||||
//1.获取参数
|
||||
Object argument = deferredObjects[0].get();
|
||||
if(argument==null){
|
||||
if (argument == null) {
|
||||
return 0;
|
||||
}
|
||||
return argument.toString().length();
|
||||
|
|
@ -58,6 +60,7 @@ public class CalStringLengthUDF extends GenericUDF {
|
|||
|
||||
/**
|
||||
* 用于以后sql函数需要显示哪些内容
|
||||
*
|
||||
* @param strings
|
||||
* @return
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ object Spark010_RDD_Operator_Transform {
|
|||
//(null.null) =>null null
|
||||
//(1,null) =>1
|
||||
val rdd1: RDD[Int] = rdd.distinct()
|
||||
val rdd2=rdd.map(x=>(x,null)).reduceByKey((x, _) => x, 7)
|
||||
rdd1.collect().foreach(println(_))
|
||||
println("==========================")
|
||||
rdd2.collect().foreach(println(_))
|
||||
|
||||
sc.stop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,27 @@ object Spark01_RDD_Operator_Transform_Par {
|
|||
}
|
||||
)
|
||||
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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ object Spark01_RDD_Serial {
|
|||
|
||||
//不加Serializable特质之前
|
||||
// NotSerializableException: com.atguigu.spark.core.rdd.serial.Spark01_RDD_Serial$Search
|
||||
//search.getMatch1(rdd).collect().foreach(println(_))
|
||||
// search.getMatch1(rdd).collect().foreach(println(_))
|
||||
search.getMatch2(rdd).collect().foreach(println(_))
|
||||
|
||||
sc.stop()
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ object SparkStreaming11_Req1_blackList1 {
|
|||
println(s"${day} ${user} ${ad} ${sum}")
|
||||
if(sum >= 30){
|
||||
//拉入黑名单
|
||||
val conn = JDBCUtil.getConnection
|
||||
// val conn = JDBCUtil.getConnection
|
||||
val sql =
|
||||
"""
|
||||
|insert into black_list (userid) values (?)
|
||||
|
|
@ -126,7 +126,7 @@ object SparkStreaming11_Req1_blackList1 {
|
|||
//将当天的广告点击数量进行更新
|
||||
//查询统计表数据
|
||||
|
||||
val conn = JDBCUtil.getConnection
|
||||
// val conn = JDBCUtil.getConnection
|
||||
val sql =
|
||||
"""
|
||||
|select *
|
||||
|
|
|
|||
|
|
@ -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>-->
|
||||
<!--<!– <scope>compile</scope>–>-->
|
||||
<!-- </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>
|
||||
|
|
@ -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;
|
||||
}*/
|
||||
}
|
||||
|
|
@ -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方法:
|
||||
* 1)addAll(另一个集合)方法会将单个元素都加进去
|
||||
* 2)add(另一个集合)方法会将其作为一个整体加入
|
||||
* 3)retainAll(另一个集合)方法会将两个集合的交集保留下来,其他的丢弃 =>注意:不是返回一个新集合,而是改变原来的集合
|
||||
* @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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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接口。为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
|
||||
* 3.Stack与LinkedList都能作为栈结构,对外表现的功能效果是一样,但是它们的物理结构不同,Stack的物理结构是顺序结构的数组,而LinkedList的物理结构是链式结构的双向链表。我们推荐使用LinkedList。
|
||||
* 4.每种方法都存在两种形式:一种形式在操作失败时抛出异常(add,remove,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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及其实现类:HashMap、HashTable、LinkedHashMap、TreeMap、Properties
|
||||
* 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的内部实现是一个HashMap,TreeSet的内部实现是一个TreeMap,LinkedHashSet的内部实现是一个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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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++];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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的三个实现类 HashSet、LinkedHashSet、TreeSet:
|
||||
* 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 + "]";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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("刮风");
|
||||
}
|
||||
}
|
||||
|
|
@ -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)和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.Comparable、java.util.Comparator、java.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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)初始化顺序:
|
||||
* (1)super()或super(实参列表) 这里选择哪个,看原来构造器首行是哪句,没写,默认就是super()
|
||||
* (2)非静态实例变量的显示赋值语句
|
||||
* (3)非静态代码块
|
||||
* (4)对应构造器中的代码
|
||||
* 特别说明:其中(2)和(3)是按顺序合并的,(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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()无参构造");
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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)等
|
||||
* 2)String由于其不是基本类型,因此使用构造方法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() {
|
||||
//参考2)、3)
|
||||
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字符串内容也helloworld,s1是常量,"world"常量,常量+ 常量 结果在常量池中
|
||||
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和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字符串内容也helloworld,s1是变量,"world"常量,变量 + 常量的结果在堆中
|
||||
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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("再给钱");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,以此保证注意1、2、3
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
是否结束?(Y、N)
|
||||
y
|
||||
main:6
|
||||
main:7
|
||||
main:8
|
||||
main:9
|
||||
|
||||
|
||||
===========================================
|
||||
第二次结果:
|
||||
main:1
|
||||
main:2
|
||||
是否结束?(Y、N)
|
||||
main:3
|
||||
main:4
|
||||
main:5
|
||||
n
|
||||
是否结束?(Y、N)
|
||||
n
|
||||
是否结束?(Y、N)
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()的区别
|
||||
* (1)sleep()不释放锁,wait()释放锁
|
||||
* (2)sleep()指定休眠的时间,wait()可以指定时间也可以无限等待直到notify或notifyAll
|
||||
* (3)sleep()在Thread类中声明的静态方法,wait方法在Object类中声明
|
||||
* 因为我们调用wait()方法是由锁对象调用,而锁对象的类型是任意类型的对象。那么希望任意类型的对象都要有的方法,只能声明在Object类中。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class sleepAndWait {
|
||||
}
|
||||
|
|
@ -17,6 +17,15 @@ object Scala04_Function_Normal_1 {
|
|||
//调用时,如果不想使用默认值,直接传值即可
|
||||
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")
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ object Scala05_Object_Field {
|
|||
//而在scala中给属性提供的set,set方法不遵循bean规范,为了统一用于则有了下述写法
|
||||
test.setEmail("xxx")
|
||||
test.getEmail()
|
||||
println(test.email)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ object Scala12_Object_Trait_4 {
|
|||
trait Log extends Operator{
|
||||
override def operDate(): Unit ={
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.atguigu.scala.chapter07
|
||||
|
||||
import scala.+:
|
||||
|
||||
object Scala01_Collection_1 {
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
|
|
@ -27,9 +29,13 @@ object Scala01_Collection_1 {
|
|||
|
||||
//val ints1: Array[Int] = array1.:+(5)
|
||||
val ints1 = array1 :+ 5
|
||||
val ints3 =5 +: array1
|
||||
|
||||
val ints2 = array1 ++ array3
|
||||
//val ints3 = array1 ++: array3
|
||||
// println(ints)
|
||||
// println(ints1)
|
||||
// println(ints2)
|
||||
|
||||
println(array1 eq ints) //false
|
||||
println(array1 eq ints1) //false
|
||||
|
|
@ -38,9 +44,9 @@ object Scala01_Collection_1 {
|
|||
|
||||
// 遍历
|
||||
//把集合中的元素用逗号分割形成一个字符串
|
||||
// println(ints.mkString(",")) //5,1,2,3,4
|
||||
// println(ints1.mkString(",")) //1,2,3,4,5
|
||||
// println(ints2.mkString(",")) //1,2,3,4,5,6,7,8
|
||||
println(ints.mkString(",")) //5,1,2,3,4
|
||||
println(ints1.mkString(",")) //1,2,3,4,5
|
||||
println(ints2.mkString(",")) //1,2,3,4,5,6,7,8
|
||||
|
||||
//foreach方法是一个循环的方法,需要穿第一个函数,这个传参数的类型是函数类型
|
||||
// 函数类型 : Int => U
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ object Scala06_Collection_Seq {
|
|||
//TODO - 集合 - Seq
|
||||
|
||||
//一般会采用List
|
||||
val seq = Seq(1,2,3,4)
|
||||
val list = List(1,2,3,4)
|
||||
val list1 = List(5,6,7,8)
|
||||
val seq = Seq(1, 2, 3, 4)
|
||||
val list = List(1, 2, 3, 4)
|
||||
val list1 = List(5, 6, 7, 8)
|
||||
|
||||
println(seq) //List(1, 2, 3, 4)
|
||||
println(list) //List(1, 2, 3, 4)
|
||||
|
|
@ -21,11 +21,11 @@ object Scala06_Collection_Seq {
|
|||
val ints1: List[Int] = 5 +: list
|
||||
|
||||
//Nil 在集合中表示空集合,增加集合的方式
|
||||
val ints2 = 1 :: 2 ::3 :: Nil
|
||||
val ints2 = 1 :: 2 :: 3 :: Nil
|
||||
//准确写法是:
|
||||
//Nil.::(3).::(2).::(1)
|
||||
val ints3 = 1 :: 2 ::3 ::list1 :: Nil
|
||||
val ints4 = 1 :: 2 ::3 ::list1 ::: Nil
|
||||
val ints3 = 1 :: 2 :: 3 :: list1 :: Nil
|
||||
val ints4 = 1 :: 2 :: 3 :: list1 ::: Nil
|
||||
|
||||
|
||||
println(list eq ints) //false
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ object Scala06_Collection_Seq_2 {
|
|||
// println(list) //ListBuffer(5, 3, 4, 2, 1)
|
||||
//
|
||||
// list.remove(1)
|
||||
// list.remove(1,2)
|
||||
println(list)
|
||||
println(list.remove(1,2))
|
||||
//
|
||||
// list.mkString()
|
||||
// list.iterator
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ object Scala01_Transform {
|
|||
}
|
||||
|
||||
val age :Int = thirdPart() //Double ? Int?
|
||||
println(age)
|
||||
println(age.isInstanceOf[Double])
|
||||
|
||||
}
|
||||
def thirdPart(): Double ={
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ package com.atguigu.scala.test;
|
|||
public class TestAdd {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//int i=0;
|
||||
//int j=i++;
|
||||
int i=0;
|
||||
// int j=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("j="+j); // 0
|
||||
System.out.println("i="+i); // 1
|
||||
// System.out.println("j="+j); // 0
|
||||
|
||||
//TODO 阶乘:一个大于1的数的阶乘等于这个数乘以他减一的数的阶乘
|
||||
//5!=5*4!=5*4*3!=....=5*4*3*2*1
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.atguigu.scala.test;
|
|||
|
||||
//import com.atguigu.scala.chapter09.Scala02_Exception;
|
||||
|
||||
import com.atguigu.scala.chapter09.Scala02_Exception;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
|
|
@ -38,7 +40,7 @@ public class TestException {
|
|||
// }
|
||||
// System.out.println("xxxxxx");
|
||||
|
||||
// Scala02_Exception.test();
|
||||
Scala02_Exception.test();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ object ToBitmapApp {
|
|||
def main(args: Array[String]): Unit = {
|
||||
|
||||
//声明环境
|
||||
val sparkConf: SparkConf = new SparkConf().setAppName("bitmap_app")//.setMaster("local[*]")
|
||||
val sparkConf: SparkConf = new SparkConf().setAppName("bitmap_app") //.setMaster("local[*]")
|
||||
val sparkSession: SparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
|
||||
|
||||
val taskId: String = args(0);
|
||||
|
|
@ -83,13 +83,12 @@ object ToBitmapApp {
|
|||
//TODO 需要做幂等性处理,每次插入数据前,需要做分区清理
|
||||
//alter table ${bitmapTableName} delete where dt='$taskDate'
|
||||
|
||||
val clearSQL=s"alter table ${bitmapTableName} delete where dt='$taskDate'"
|
||||
val clearSQL = s"alter table ${bitmapTableName} delete where dt='$taskDate'"
|
||||
println(clearSQL)
|
||||
ClickhouseUtils.executeSql(clearSQL)
|
||||
|
||||
|
||||
|
||||
if(tagList.size >0){
|
||||
if (tagList.size > 0) {
|
||||
//('gender',gender),('agegroup',agegroup),('favor',favor)
|
||||
val tagCodeSQL: String = tagList.map(
|
||||
tagInfo => s"('${tagInfo.tagCode}',${tagInfo.tagCode.toLowerCase()})"
|
||||
|
|
@ -113,7 +112,6 @@ object ToBitmapApp {
|
|||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class ZooKeeperTest {
|
|||
* @throws KeeperException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
@Test
|
||||
public void deleteAll(String path,ZooKeeper zk) throws KeeperException, InterruptedException {
|
||||
|
||||
//判断当前节点是否存在,获取stat
|
||||
|
|
|
|||
|
|
@ -7,7 +7,47 @@
|
|||
<groupId>org.example</groupId>
|
||||
<artifactId>Leecode</artifactId>
|
||||
<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>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.projectlombok</groupId>-->
|
||||
<!-- <artifactId>lombok</artifactId>-->
|
||||
<!-- <version>RELEASE</version>-->
|
||||
<!--<!– <scope>compile</scope>–>-->
|
||||
<!-- </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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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]](若两个四元组元素一一对应,则认为两个四元组重复):
|
||||
* 1)0 <= a, b, c, d< n
|
||||
* 2)a、b、c 和 d 互不相同
|
||||
* 3)nums[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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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:
|
||||
* 给你四个整数数组 nums1、nums2、nums3 和 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 != j、i != 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
package com.markilue.leecode.listnode;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 设计链表:
|
||||
* 设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val和next。val是当前节点的值,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
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
package com.markilue.leecode.listnode;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 设计链表:
|
||||
* 设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val和next。val是当前节点的值,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode;
|
||||
package com.markilue.leecode.listnode;
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
* public class ListNode {
|
||||
|
|
@ -10,8 +10,6 @@ package listnode;
|
|||
* }
|
||||
*/
|
||||
|
||||
import com.sun.corba.se.impl.resolver.SplitLocalResolverImpl;
|
||||
|
||||
/**
|
||||
* 2.两数相加
|
||||
*/
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode;
|
||||
package com.markilue.leecode.listnode;
|
||||
|
||||
public class mergeKLists {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode;
|
||||
package com.markilue.leecode.listnode;
|
||||
|
||||
public class mergeTwoLists {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode;
|
||||
package com.markilue.leecode.listnode;
|
||||
|
||||
/**
|
||||
* 删除链表的倒数第N个节点
|
||||
|
|
@ -8,22 +8,34 @@ public class removeNthFromEnd {
|
|||
public static void main(String[] args) {
|
||||
|
||||
ListNode l2 = new ListNode(1);
|
||||
// l2.next = new ListNode(2);
|
||||
// l2.next.next = new ListNode(3);
|
||||
// l2.next.next.next =new ListNode(4);
|
||||
// l2.next.next.next.next =new ListNode(5);
|
||||
l2.next = new ListNode(2);
|
||||
l2.next.next = new ListNode(3);
|
||||
l2.next.next.next =new ListNode(4);
|
||||
l2.next.next.next.next =new ListNode(5);
|
||||
// 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.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);
|
||||
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的位置删除
|
||||
*
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
package listnode.selftry;
|
||||
|
||||
import listnode.mergeTwoLists;
|
||||
package com.markilue.leecode.listnode.selftry;
|
||||
|
||||
public class mergeKLists {
|
||||
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
package listnode.selftry;
|
||||
|
||||
import listnode.addTwoNumbers;
|
||||
import listnode.removeNthFromEnd;
|
||||
package com.markilue.leecode.listnode.selftry;
|
||||
|
||||
public class mergeTwoLists {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode.selftry;
|
||||
package com.markilue.leecode.listnode.selftry;
|
||||
|
||||
/**
|
||||
* 删除链表的倒数第N个节点
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode.selftry;
|
||||
package com.markilue.leecode.listnode.selftry;
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package listnode;
|
||||
package com.markilue.leecode.listnode;
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
package sort;
|
||||
|
||||
import com.sun.corba.se.impl.ior.FreezableList;
|
||||
package com.markilue.leecode.sort;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -34,7 +32,7 @@ public class ThreeSum {
|
|||
|
||||
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) {
|
||||
return parentList;
|
||||
|
|
@ -78,7 +76,7 @@ public class ThreeSum {
|
|||
}
|
||||
//如果是第二个条件跳出的循环
|
||||
if (nums[j] + nums[k] == need) {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
List<Integer> list = new ArrayList<Integer>();
|
||||
list.add(nums[i]);
|
||||
list.add(nums[j]);
|
||||
list.add(nums[k]);
|
||||
|
|
@ -99,7 +97,7 @@ public class ThreeSum {
|
|||
//更简洁一点的思路
|
||||
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) {
|
||||
return parentList;
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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每+1,ans都会赋值一个元素,因此进来的数永远在窗口里,不存在把队列里的数全挪完了,窗口数也超过的情况
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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题 用栈实现队列:
|
||||
* 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
|
||||
*
|
||||
* 实现 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();
|
||||
*/
|
||||
|
|
@ -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题 用栈实现队列:
|
||||
* 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
|
||||
*
|
||||
* 实现 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();
|
||||
*/
|
||||
|
|
@ -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)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
|
||||
* 实现 MyStack 类:
|
||||
* void push(int x) 将元素 x 压入栈顶。
|
||||
* int pop() 移除并返回栈顶元素。
|
||||
* int top() 返回栈顶元素。
|
||||
* boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
|
||||
* <p>
|
||||
* <p>
|
||||
* 注意:
|
||||
* 你只能使用队列的基本操作 —— 也就是push to back、peek/pop from front、size 和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();
|
||||
*/
|
||||
|
|
@ -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)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
|
||||
* 实现 MyStack 类:
|
||||
* void push(int x) 将元素 x 压入栈顶。
|
||||
* int pop() 移除并返回栈顶元素。
|
||||
* int top() 返回栈顶元素。
|
||||
* boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
|
||||
* <p>
|
||||
* <p>
|
||||
* 注意:
|
||||
* 你只能使用队列的基本操作 —— 也就是push to back、peek/pop from front、size 和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();
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue