CS61B - Lec 1-4
絮叨一下
大名鼎鼎的CS61B。开个博客记录一下一些值得注意的点。
目前感受如下:
- Josh Hug is the best teacher! 我一直以为聪明人不容易出好老师,因为他们觉得一切都很简单。。。碰到他发现我错了,他能理解很多学生会遇到的困难,懂得我们的心理,而且真的是一个有趣又可爱的人。
- Not just a data structure class. 以java为基础,各种数据结构都会手把手建一个类来实现,顺带入门了java,完全搞明白了面向对象编程思想。
- Coding philosophy. 跟老师一起建数据结构学了一些他的好习惯。
写于不知能否去.不知要不要转码.不知要不要读博的北美20fall准备期.
课程视频来源于b站,相关资料:https://sp18.datastructur.es/index.html
Lec1, Lec2 - Intro
Lec1: 课程安排,课程注意事项,为什么学这门课,不多说了
Lec 2重点
- java基于类的编写方式
- static和non-static(instance) members区别:外部需不需要通过对象调用,有了static关键字无需定义对象,直接通过类名.变量/方法调用。
Lec 3 - References and Recursion
这一章彻底让我搞懂了C++以前莫名其妙的引用概念(个人感觉这个词翻译的不好)。
引用详解
- 定义
java中有8种原始数据类型(primitive type),包括byte, short, int, long, float, double, boolean, char,其他类型,如数组,类都称之为reference type。
是不是觉得有一堆特别熟悉的名词。。。
- 一条真理
y = x,意为y拷贝了所有变量x中存储的bits。
- 分析
原始数据类型,变量存储的是大小,拷贝之后,x,y为两独立变量。
reference type,每次声明时,java会自动分配64位存储空间。每当用new
进行初始化时,new会返回一个64为数据,记录内存地址,拷贝进变量。所以,此时y和x在同一地址,一个改变对所有都生效。
很显然x不变,walrus will lose 100lbs
数组初始化几种方式
比较繁琐,这里记下方便找
int[] x = new int[5];
int[] x = null;
int[] x = new int[]{0, 1, 2};
int[] x = {0, 1, 2};
IntList
public class IntList {
public int first;
public IntList rest;
public IntList(int f, IntList l) {
first = f;
rest = l;
}
public int size() {
if (rest == null) {
return 1;
}
return 1 + rest.size();
}
public int iterativeSize() {
IntList l = this;
int num = 0;
while (l != null) {
num += 1;
l = l.rest;
}
return num;
}
/* Return the ith item of this IntList. */
/* Recursion. */
public int get(int i) {
if (i == 0) {
return first;
}
return rest.get(i - 1);
}
/** Iterative */
/*
public int get(int i) {
IntList l = this;
for (int n = 0; n < i; n += 1) {
l = l.rest;
}
return l.first;
}
*/
/* All values incremented by x; do not change values in l */
public static IntList incrList(IntList l, int x) {
if (l == null) {
return null;
}
IntList res = new IntList(l.first + x, null);
IntList ptr = res;
l = l.rest;
while (l != null) {
ptr.rest = new IntList(l.first + x, null);
ptr = ptr.rest;
l = l.rest;
}
return res;
}
/* All values in l incremented by x; not allowed to use 'new' */
public static IntList dincrList(IntList l, int x) {
IntList ptr = l;
while (ptr != null) {
ptr.first += x;
ptr = ptr.rest;
}
return l;
}
public static void main(String[] args) {
IntList l = new IntList(15, null);
l = new IntList(10, l);
l = new IntList(5, l);
System.out.println(l.get(2));
System.out.println(IntList.incrList(l, 2).get(2));
System.out.println(l.get(2));
}
}
链表类的建立。注意其中有很多recursive的方法,包括建立初始化也是recursive方式。
l = new IntList(10, l);
此句意为addFirst。
Lec4 - Node based lists
上节末尾的IntList有改进的空间。
建完之后会发现,对用户不太友好,用户需要有recursive的思想,以及丰富的references知识。
将IntList称之为“naked"链表。
IntList to SLList
如图,将IntList更名为IntNode,代表一个节点,将变量rest更名为next。IntNode作为新的SLList的nested class。为什么这么做呢?
首先,SLList更容易初始化。
SLList X = new SLList(5);
IntList Y = new IntList(5, null);
这好像没什么。
然后,如果想要向链表里加入数据,IntList是这样操作的。
Y = new IntList(6, Y);
Y = new IntList(7, Y);
比较绕而且麻烦。而SLList是这样:
X.addFirst(6);
X.addFirst(7);
那为什么不在IntList里加入addFirst()呢?
public class IntList {
public int first;
public IntList rest;
public IntList(int f, IntList l) {
first = f;
rest = l;
}
public void addFirst() {
//this = new IntList(x, this);
}
}
仔细看了一会,发现只能这么搞,但是肯定编译不通过啊没办法。除了this以外,没有变量能表明当前指针的位置,只有指向下一个数据的rest,所以不好搞。
IntList user want to have variables that point to the middle of the IntList,就是我上面说的意思。SLList中的IntNode就可以表明当前位置。
Private关键字
如图,如果开放first变量的权限变为public,那么用户有时候就会瞎改first,出错。为避免用户瞎闹腾,我们用private关键字把first封起来。同样,nested类IntNode也没必要,直接private。
Static关键字另一层含义
前文说到static方法可以无需定义对象直接通过类名调用,为什么会这样呢?
因为static关键字真实含义是,本方法不会用任何类中变量。既然各个对象中特有的变量值与我无关,则无需定义对象也可调用。
所以,可以把IntNode定义成static方法,然而因为是private,外面也不会动他,不加也问题不大,据说这样会省内存。
加入addLast()与Size()
addLast()比较简单
public void addLast(Item x) {
IntNode p = first;
while (p.next != null) {
p = p.next;
}
p.next = new IntNode(x, null);
size += 1;
}
size()用了很tricky的迭代方式
/** Return the size of a SLList. (recursion) */
/* Return the size of the list that starts at p. */
private static int size(IntNode p) {
if (p.next == null) {
return 1;
}
return 1 + size(p.next);
}
public int size() {
return size;
}
然而看着好看,无卵用。这个size()太慢了。现在有SLList了啊,只需要建立一个size变量,每次加入或减少数据,size相应地变化,所以:
public int size() {
return size;
}
要学会类的思想。
改进addLast()
addLast()此时仍有一个小bug
SLList s = new SLList();
s.addLast(5);
这样就坏掉了。因为对象为空时first也为空,没有next,无法往后遍历。这咋整呢?
加判断?那就不太整洁了。这里提出了invariance的思想。
无论链表是否为空,属性都应相同。
答案是建立sentinel node,代表链表头,下一个才是数据。
/** Constructor. */
/* The first item is sentinel.next */
private IntNode sentinel;
private int size;
/* Create an empty list. */
/* Just set the item of sentinel to null */
public SLList() {
sentinel = new IntNode(null, null);
}
/* Init an list with one item. */
public SLList(Item x) {
sentinel = new IntNode(null, null);
sentinel.next = new IntNode(x, null);
size = 1;
}
这样就解决了addLast的问题。
这一节信息量还是很大的。