61b 一拖再拖,课内学得稀烂。把 61b 的仓库 public 了,搬了一堆屎到博客上,希望得到存在或不存在的人的监督🙏✊
又听到 Josh Hug 的声音,感觉好幸福。

Lists in Java

date: 2025-8-20 23:35:07

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
public class SLList {
private static class Int Node {
public int item;
public IntNode next;

public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}

private IntNode first;

public SLList(int x) {
first = new IntNode(x, null);
}

public void addFirst(int x) {
first = new IntNode(x, first);
}
public int getFirst() {
return first.item;
}

// Why is it `static`?
private static int size(IntNode p) {
if (p.next == null) {
return 1;
}
return 1 + size(p.netx);
}
public int size() {
return size(first);
}
}

更强的封装性,更面向对象。留意 .size() 的实现。


The meaning of a sentinel

统一的逻辑,严谨的结构(空表)


addLast is slow

需要将倒数第二个节点的 next 置空,要遍历链表。


If we just maintain a lastNode, the moveLast would be slow

多维护一个 lastNode 也没有用(思考)。


So the solution is… Doubly Linked List!

因此想到双向链表。


Two sentinel? Can be more perfect. One Sentinel!

image-20250820151743741

image-20250820231056714

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
public class DLList<T> {
private class Node {
public T item;
public Node next;
public Node prev;

public Node(T i, Node n, Node p) {
item = i;
next = n;
prev = p;
}
}

private Node sentinel;
private int size;

public DLList() {
size = 0;
sentinel = new Node(null, null, null);\
sentinel.next = sentinel;
sentinel.prev = sentinel;
}

public void addFirst(T x) {
Node newNode = new Node(x, sentinel.next, sentinel);
sentinel.next.prev = newNode;
sentinel.next = newNode;
++size;
}

public void removeFirst() {
if (size == 0) {
throw new IllegalStateException("Cannot remove from an empty list");
}
sentinel.next.next.prev = sentinel;
sentinel.next = sentinel.next.next;
--size;
}

public T getFirst() {
if (size == 0) {
throw new IllegalStateException("Cannot get first element of an empty list");
}
return sentinel.next.item;
}

public void addLast(T x) {
Node newNode = new Node(x, sentinel, sentinel.prev);
sentinel.prev.next = newNode;
sentinel.prev = newNode;
++size;
}

public void removeLast() {
if (size == 0) {
throw new IllegalStateException("Cannot remove from an empty list");
}
sentinel.prev.prev.next = sentinel;
sentinel.prev = sentinel.prev.prev;
--size;
}

public T getLast() {
if (size == 0) {
throw new IllegalStateException("Cannot get last element of an empty list");
}
return sentinel.prev.item;
}

public int size() { return size; }
}

Lecture8-10

date: 2025-9-26 9:40:27

接口继承和实现继承

Java 中, 继承分为接口继承 implements 和实现继承 entends.

前者提供一个接口 interface , 包含所有子类应实现的功能, 但没有具体实现, 子类用 implements 关键字声明.

1
2
3
4
5
6
7
8
9
10
public interface List61B<Item> {
public void addFirst(Item x);
public void add Last(Item y);
public Item getFirst();
public Item getLast();
public Item removeLast();
public Item get(int i);
public void insert(Item x, int position);
public int size();
}
1
2
3
4
5
6
public class AList<Item> implements List61B<Item> {
@Override
public void addFirst(Item x) { ... }

...
}

后者的使用情景为子类需要重写父类的某些方法, 对于这些方法, 加入 default 关键字声明, 子类用 extends 关键字声明.

1
2
3
4
5
6
default public void print() { // 需要重写的方法
for (int i = 0; i < size(); ++i) {
System.out.print(get(i) + " ");
}
System.out.println();
}
1
2
3
4
5
6
@Override
public void print() {
for (Node p = sentinel.next; p != null; p = p.next) {
System.out.print(p.item + " ");
}
}

使用 extends 关键字, 子类继承父类构造和析构函数外的所有成员(虽然不继承, 但在子类对象构造时也会被显示/隐式调用), 并且 private variables 不能被子类直接访问.


静态类型和动态类型

省流: 编译检查看静态类型, 为了通过编译检查会用到强制类型转换; 虚表查找(…就是那个意思)看动态类型


高阶函数和子类多态性

Java 中高阶函数的实现.

1
2
3
public interface IntUnaryFunction {
int apply(int x);
}
1
2
3
4
5
6
7
public TenX implements IntUnaryFunction {
/* Returns ten times the argument. */
@Override
public int apply(int x) {
return 10 * x;
}
}
1
2
3
public static int do_twice(IntUnaryFunction f, int x) {
return f.apply(f.apply(x));
}

比较器

1
2
3
4
5
6
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
}

上述代码通过 Comparable 实现了运算符重载.

需要实现多种不同的比大小方法时, 用到另一个类 Comparator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.Comparator;

public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}

private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}

public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}

Eceptions and Literators

date: 2025-9-26 11:07:16

本节实现数据结构 ArraySet

Exceptions

1
throw new ExceptionObject(parameter1, ...)
1
2
3
4
5
6
7
8
9
10
11
12
/* Associates pthe specified value with the specified key in this map.
Throws an IllegalArgumentException if the key is null. */
public void add(T x) {
if (x == null) {
throw new IllegalArgumentException("can't add null");
}
if (contains(x)) {
return;
}
items[size] = x;
size += 1;
}

Iterators and Iterables

1
2
3
public interface Iterable<T> {
Iterator<T> iterator();
}
1
2
3
4
public interface Iterator<T> {
boolean hasNext();
T next();
}
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
import java.util.Iterator:

public class ArraySet<T> implements Iterable<T> {
private T[] items;
private int size; // the next item to be added will be at position `size`

public ArraySet() {
items = (T[]) new Object[100];
size = 0;
}

/* Returns true if this map contains a mapping for specified key. */
public boolean contains(T x) {
for (int i = 0; i < size; ++i) {
if (items[i].equals(x)) {
return true;
}
}
return false;
}

/* Associates the specified value with the specified key in this map.
Throws an IllegalArgumentException if the key is null. */
public void add(T x) {
if (x == null) {
throw new IllegalArgumentException("can't add null");
}
if (contains(x)) {
return;
}
items[size] = x;
size += 1;
}

/* Returns the number of key-value mappings in this map */
public int size() {
return size;
}

/* Returns an iterator into ME */
public Iterator<T> iterator() {
return new ArraySetIterator();
}

private class ArraySetIterator implements Iterator<T> {
private int wizPos;

public ArraySetIterator() {
wizPos = 0;
}

public boolean hasNext() {
return wizPos < size;
}

public T next() {
T returnItem = items[wizPos];
wizPos += 1;
return returnItem;
}
}

public static void main(String[] args) {
ArraySet<Integer> aset = new ArraySet<>();
aset.add(5);
aset.add(23);
aset.add(42);

// iteration
for (int i : aset) {
System.out.println(i);
}
}
}

JUnit Check

date: 2025-9-26 14:17:05

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public class MaxArrayDequeTest {
@Test
/** Adds a few things to the list, checking isEmpty() and size() are correct,
* finally printing the results.
*
*/
public void addIsEmptySizeTest() {
Comparator<String> c = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
MaxArrayDeque<String> mad1 = new MaxArrayDeque<>(c);

assertTrue("A newly initialized MADeque should be empty", mad1.isEmpty());
mad1.addFirst("front");

assertEquals(1, mad1.size());
assertFalse("mad1 should now contain 1 item", mad1.isEmpty());

mad1.addLast("middle");
assertEquals(2, mad1.size());

mad1.addLast("back");
assertEquals(3, mad1.size());

System.out.println("Printing out deque: ");
mad1.printDeque();
}

@Test
public void addRemoveTest() {
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
MaxArrayDeque<Integer> mad1 = new MaxArrayDeque<>(c);
assertTrue("mad1 should be empty upon initialization", mad1.isEmpty());

mad1.addFirst(1);
assertFalse("mad1 should contain 1 item", mad1.isEmpty());

mad1.removeFirst();
assertTrue("mad1 should be empty after removal", mad1.isEmpty());
}

@Test
public void removeEmptyTest() {
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
MaxArrayDeque<Integer> mad1 = new MaxArrayDeque<>(c);
mad1.addFirst(1);

mad1.removeLast();
mad1.removeFirst();
mad1.removeLast();
mad1.removeFirst();

int size = mad1.size();
String errorMsg = " Bad size returned when removing from empty deque.\n";
errorMsg += " student size() returned " + size + "\n";
errorMsg += " actual size() returned 0\n";

assertEquals(errorMsg, 0, size);
}

@Test
public void multipleParamTest() {
MaxArrayDeque<String> mad1 = new MaxArrayDeque<String>();
MaxArrayDeque<Double> mad2 = new MaxArrayDeque<Double>();
MaxArrayDeque<Boolean> mad3 = new MaxArrayDeque<Boolean>();

mad1.addFirst("string");
mad2.addFirst(3.14159);
mad3.addFirst(true);

String s = mad1.removeFirst();
double d = mad2.removeFirst();
boolean b = mad3.removeFirst();
}

@Test
public void bigMADequeTest() {
MaxArrayDeque<Integer> mad1 = new MaxArrayDeque<>();
for (int i = 0; i < 1000000; i++) {
mad1.addLast(i);
}

for (double i = 0; i < 500000; i++) {
assertEquals("Should have the same value", i, (double) mad1.removeFirst(), 0.0);
}

for (double i = 999999; i > 500000; i--) {
assertEquals("Should have the same value", i, (double) mad1.removeLast(), 0.0);
}
}

@Test
public void maxTest() {
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
MaxArrayDeque<Integer> mad1 = new MaxArrayDeque<>(c);

mad1.addFirst(1);
mad1.addLast(29);
mad1.addLast(18);
mad1.addLast(2912);
mad1.addLast(292);
mad1.addFirst(2912);

assertEquals((Integer) 2912, mad1.max());
}

}


toString and equals

date: 2025-10-3 15:06:53

toString

1
2
3
4
5
6
7
8
9
10
11
@Override
publiic String toString() {
String returnString = new String();
for (int i = 0; i < size - 1; ++i) {
returnString += items[i.toString();
returnString += ", ";
}
returnString += items[size - 1].toString;
returnString += ", ";
return returnString;
}

利用迭代拼接字符串 String 效率特别低(每次迭代会复制一个字符串), 复杂度为 O(n²), 故改用 StringBuilder

1
2
3
4
5
6
7
8
9
10
11
@Override
public String toString() {
StringBuilder returnSB = new StringBuilder("{");
for (int i = 0; i < size - 1; ++i) {
returnSB.append(items[i].toString());
returnSB.append(", ");
}
returnSB.append(items[size - 1]);
returnSB.append("}");
return returnSB.toString();
}

Shorter toString:

1
2
3
4
5
6
7
8
@Override
public String toString() {
List<String> listOfItems = new ArrayList<>();
for (T x : this) {
listOfItems.add(x.toString());
}
return "{" + String.join(", ", listOfItems) + "}";
}

这样的实现更简洁, 但还是效率低下, 如果不需要频繁调用 toString, 这样写更好看.


equals

1
2
3
4
5
6
7
@Override
public boolean equals(Object o) { // the parameter must be Object, not ArraySet or something, because this is a overriding method
if (o instanceof Dog uddaDog) {
return this.size == uddaDog.size;
}
return false;
}

instanceof:

  • checks to see if o’s dynamic type is Dog
  • implicitly casts o as a Dog into a variable called uddaDog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof ArraySet oas) {
if (this.size != oas.size) {
return false;
}
for (T x : this) {
if (!oas.contains(x)) {
return false;
}
}
return true;
}
return false;
}

.of

var arg (variable number of arguments)

new pattern of generic (of is a static method which can not access generic param T)

1
2
3
4
5
6
7
public static <Glerp> ArraySet<Glerp> of(Glerp... stuff) { // Glerp has nothing to do with T, it's just the declaration that this is a method which is generic
ArraySet<Glerp> returnSet = new ArraySet<Glerp>();
for (Glerp x : stuff) {
returnSet.add(x);
}
return returnSet;
}