题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:

输入:head = [1], n = 1
输出:[]
示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

#解题
自己没解出来,看了提示,自己解的总是边界做不好
虚拟头,快慢指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode pre = new ListNode(0,head);
ListNode slow = pre;
ListNode fast = pre;
while( n+1 != 0){
fast = fast.next;
n--;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;

return pre.next;
}
}

解释器模式(interpreter)
对象行为模式
给定一种语言,定义一种文法,定义一个解释器,解析语句。
复杂的语法容易不好维护。

状态模式(status)
对象行为模式
改变对象的状态就会改变它的行为。
对象的状态影响它的行为,多个分支条件判断

观察者模式(Observer)
对象行为模式
当多个对象依赖一个对象,被依赖对象更新,会通知所有依赖对象。
被观察者更新,通过所有观察者。
会有推、拉模型,推可能观察者的状态失效,拉可能调用过多
解耦被观察者,和观察者。

解释器模式不太常用,观察者很常见,但实现起来不简单。状态也很常见,实现也不易。

抽象公共的步骤、算法到公共类,子类可以复用公共的算法,再自己实现需要修改的部分。每个子类可以自定义具体实现,但公共部分应为抽象合理,所以直接复用,拆分整体算法,到具体步骤中,只实现规定步骤。

这种是用在,算法明显相同的情景,不用每次都关心流程,只关心自己特殊步骤的实现即可,延迟实现到子类。

基于类继承关系实现,面向对象思想。

很多时候设计模式记不住,我理解是想反了,我们从设计模式特点反看设计模式,这个设计模式有这些这些特点,我们可以对比,熟记。但我们用的时候,却是从正面去思考,什么场景,什么特点的情景,用这个方式抽象。
从不同维度看同一件事情也很重要,是能更多角度了解同一件事情。

模版模式跟策略模式,有些像。

题目

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

解题

思路1 通过虚拟头节点+头插法,便利链表,头插入就是倒序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
return head;
}
ListNode sentry = new ListNode(0);
while(head != null){
ListNode oneNode = new ListNode(head.val, sentry.next);
sentry.next = oneNode;
head = head.next;
}
return sentry.next;
}
}

思路2
看解题思路,双指针反插法,更节省空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while(cur!= null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}

JUnit Jupiter 允许 @Test,@RepeatedTest, @ParameterizedTest,@TestTemplate,@TestFactory,@BeforeEach,@AfterEach 在接口上定义default方法。@BeforeAll and @AfterAll也可以定义静态方法在测试接口或接口的默认方法,如果测试接口或测试类被@TestInstance(Lifecycle.PER_CLASS) 注解。

@ExtendWith 和@Tag 也可以定义在测试接口上,实现类自定继承标签和扩展。

在之前版本的Junit,测试构造器和方法不被允许有参数(至少标准的Runner实现没有),Junit Jupiter主要的改变,测试构造器和方法都允许有参数。这样扩展性更强,可以依赖注入构造器和方法。
ParameterResolver定义了API,期望在测试用例执行时,运行期动态解决参数。如果一个测试类构造器,测试方法或生命周期方法,接受一个参数,参数必须运行期注册为ParameterResolver。

有几个内建解析器会自动注册。
TestInfoParameterResolver,如果一个构造器或方法参数类型是TestInfo。TestInfoParameterResolver会供给TestInfo实例,相当于当前容器或测试值当作参数。TestInfo用于从当前容器或测试检索信息,例如显示姓名,测试类,测试方法,有关联的标签等。显示的姓名是一个科技方法,例如测试类的名字或方法,或通过@DisplayName自定义的。

TestInfo是替换junit4的TestName规则。下面例子的目的是测试构造器注入testInfo,@BeforeEach和测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("TestInfo Demo")
class TestInfoDemo {

TestInfoDemo(TestInfo testInfo) {
assertEquals("TestInfo Demo", testInfo.getDisplayName());
}

@BeforeEach
void init(TestInfo testInfo) {
String displayName = testInfo.getDisplayName();
assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
}

@Test
@DisplayName("TEST 1")
@Tag("my-tag")
void test1(TestInfo testInfo) {
assertEquals("TEST 1", testInfo.getDisplayName());
assertTrue(testInfo.getTags().contains("my-tag"));
}

@Test
void test2() {
}

}

RepetitionInfoParameterResolver
如果一个方法参数在@RepeatedTest,@BeforeEach,@AfterEach是RepetitionInfo类型,RepetitionInfoParameterResolver将会接受一个RepetitionInfo实例。RepetitionInfo可以用来检索信息,包括当前重复信息和总的循环次数从@RepeatedTest。注意,RepetitionInfoParameterResolver在@RepeatedTest上下文外不会被注册。
TestReporterParameterResolver
如果一个构造器或方法参数的类型是TestReporter,TestReporterParameterResolver将会接受一个TestReporter实例。TestReporter可以发布当前测试运行的额外信息。在TestExecutionListener的reportingEntryPublished()来消耗数据,允许IDEs或报告查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class TestReporterDemo {

@Test
void reportSingleValue(TestReporter testReporter) {
testReporter.publishEntry("a status message");
}

@Test
void reportKeyValuePair(TestReporter testReporter) {
testReporter.publishEntry("a key", "a value");
}

@Test
void reportMultipleKeyValuePairs(TestReporter testReporter) {
Map<String, String> values = new HashMap<>();
values.put("user name", "dk38");
values.put("award year", "1974");

testReporter.publishEntry(values);
}

}

花了几个小时的时间,搜索了互联网的信息,中文的内容不太多,而且大多不成体系,看到一个最近的报告,其中这3种能力是独立开发者最缺少的。
Ui设计,产品设计,流量运营
可能大部分独立开发者,都是开发出身,这样才有独立开发的基础,但其他几项能力的缺少,让开发出来的东西,在市场上没有竞争力。

而且现在看起来,这一行业在中国还不成气候,道路还不可复制。所以可能比较难,但机会还有有的,而且很少人能做。
首先是市场需求的分析能力,能找到受众,客户的需求,这个是产品经理的能力。
第二个是Ui设计能力,这个需要审美。
第三个是产品设计能力
第四个是开发能力
第五个是,运营能力,开发后,如何跟客户互动,赚钱。
没有成熟的路线,就要自己的分析,自己的脑子了,这个也是以后赚钱的根本,不能偷懒,一点点做。

题目

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性: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 个节点。

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3

提示:

所有val值都在 [1, 1000] 之内。
操作次数将在 [1, 1000] 之内。
请不要使用内置的 LinkedList 库。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/design-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

自己没写对,参考提示,用虚拟头节点,单链书写,通过size状态,有效控制,前后的结束的判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Node{
Node next;
int val;
public Node(int value){
this.val = value;
}
}
class MyLinkedList {
int size;
Node head;

public MyLinkedList() {
this.size = 0;
head = new Node(0);
}

public int get(int index) {
if(index < 0 || index >= size){
return -1;
}
Node current = head;
for(int i = 0 ; i < index + 1 ; i++){
current = current.next;
}
return current.val;
}

public void addAtHead(int val) {
addAtIndex(0, val);
}

public void addAtTail(int val) {
addAtIndex(size, val);
}

public void addAtIndex(int index, int val) {
if(index < 0){
index = 0;
}
if(index > size){
return;
}
size++;
Node pre = head;
for(int i = 0 ; i< index ; i++){
pre = pre.next;
}
Node insertNode = new Node(val);
insertNode.next = pre.next;
pre.next = insertNode;
}

public void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
size--;
Node pre = head;
for(int i = 0 ; i< index ; i++){
pre = pre.next;
}
pre.next = pre.next.next;
}
}

/**
* 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);
*/

最近有个需求,就是我们列表是 从某个系统查询主数据源,分页20条,再从本地db,其他系统获取补充数据源,然后拼接这些副数据源到主数据源,再进行业务处理,返回给页面列表展示。
同时另外一个场景,是mq推送单条主数据源的变更数据,我们自己再拼接其他数据源,业务处理,同时推送这条变化到列表,告诉页面这条有变化。
两个场景除了数据源的获取方式有差异,业务处理最后要保持一致。但最开始,这两块是分开的。现在想共用数据处理逻辑,数据源本来就不一样。
我们后面使用模版方法,重构上面处理,分为,数据源+副作用处理+返参映射,这样列表给出的实现,只要推送的集成,重写数据源部分就行了,副作用和返参部分直接继承,修改也同时修改,一处改动就好了。
因为模版方法是基于继承的,所以想实现继承的就很方便,如果同样的需求,要用责任链,就要重新构建责任链,它是不能继承的。

测试执行顺序

默认,测试类或方法执行顺序由算法决定,但是可能不那么显而易见。这章确保随后执行的测试集合,测试类和测试方法以相同的顺序执行,所以允许重复编译。

方法顺序

即使真正的单元测试不应该依赖执行顺序,但还是有时必须要强制执行顺序——例如书写集成测试或函数式测试,执行顺序很重要
@TestInstance(Lifecycle.PER_CLASS)
为了控制测试方法的执行顺序,注解你的类或测试接口使用@TestMethodOrder或者重新定义需要的TestOrder实现。你可以实现你自己的TestOrder或使用内建的实现

MethodOrderer.DisplayName 基于显示名称的字母数字顺序的排序。
MethodOrderer.MethodName 基于名字或参数列表
MethodOrderer.OrderAnnotation 通过@Order
MethodOrderer.Random 基于伪随机,支持自定义种子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo {

@Test
@Order(1)
void nullValues() {
// perform assertions against null values
}

@Test
@Order(2)
void emptyValues() {
// perform assertions against empty values
}

@Test
@Order(3)
void validValues() {
// perform assertions against valid values
}

}

类的顺序

即使测试类典型不应该依赖执行的顺序,还有些场景需要强制执行特定的类的顺序。你可能希望随机执行确保没有意外依赖,你可能希望执行在这些场景:
快速失败模式
并行场景,执行更长先执行,最短的测试执行。跟方法一样的自定义ClassOrder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestClassOrder;

@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class OrderedNestedTestClassesDemo {

@Nested
@Order(1)
class PrimaryTests {

@Test
void test1() {
}
}

@Nested
@Order(2)
class SecondaryTests {

@Test
void test2() {
}
}
}