CS 61B 作用域、静态、链表、数组(解答)¶
2025年春季 讨论02:2025年2月3日
1 静态变量和静态函数¶
public class Pokemon {
public String name;
public int level;
public static String trainer = "Ash";
public static int partySize = 0;
public Pokemon(String name, int level) {
this.name = name;
this.level = level;
this.partySize += 1;
}
public static void main(String[] args) {
Pokemon p = new Pokemon("Pikachu", 17);
Pokemon j = new Pokemon("Jolteon", 99);
System.out.println("Party size: " + Pokemon.partySize);
p.printStats();
int level = 18;
Pokemon.change(p, level);
p.printStats();
Pokemon.trainer = "Ash";
j.trainer = "Cynthia";
p.printStats();
}
public static void change(Pokemon poke, int level) {
poke.level = level;
level = 50;
poke = new Pokemon("Luxray", 1);
poke.trainer = "Team Rocket";
}
public void printStats() {
System.out.println(name + " " + level + " " + trainer);
}
}
解答:¶
( a ) 执行main方法后会打印的内容:
Party size: 2
Pikachu 17 Ash
Pikachu 18 Ash
Pikachu 18 Cynthia
( b ) 第28行中设置的level指的是: B. 包含change方法参数的局部变量
( c ) 如果我们在main方法结束时调用Pokemon.printStats(): 会出现编译错误,因为printStats()是一个实例方法,不是静态方法。静态方法需要通过类的实例来调用,而不能直接通过类名调用。
2 旋转数组(Rotate Extra)¶
/** 返回一个包含A中元素向右移动k个位置的新数组。 */
public static int[] rotate(int[] A, int k) {
int rightShift = k % A.length;
if(rightShift < 0) {
rightShift += A.length;
}
int[] newArr = new int[A.length];
for (int i = 0; i < A.length; i++) {
int newIndex = (i + rightShift) % A.length;
newArr[newIndex] = A[i];
}
return newArr;
}
注意,int rightShift = (k % A.length) + A.length可以在一行中完成(而不是为负的rightShift进行额外检查),因为模运算会处理对正k值的加法溢出。
解释:¶
首先,我们使用模运算符计算实际需要移动的位置数量,确保rightShift始终是一个0到A.length-1之间的非负整数。
如果k是负数,那么在模A.length运算后,rightShift将是负数。例如,如果A的长度为5,k为-2,那么rightShift将是-2 mod 5 = -2。然后,将A.length添加到rightShift使其变为正数,并将数组向左移动2个位置,这与原始移动的方向相反。例如,rightShift += 5变成了rightShift = 3,这意味着数组向左移动了2个位置。
然后,我们创建一个与A长度相同的新数组newArr来存储移动后的元素。我们遍历原始数组A,并通过将rightShift添加到当前索引并对结果取模A.length来计算每个元素的新索引。
这个计算确保了移动的元素如果超出了数组的最后一个索引,就会"环绕"到数组的开头。然后,我们将原始元素分配给新数组newArr中的相应索引。
另一种解法:¶
public static int[] rotate(int[] A, int k) {
int rightShift = 330; // 不需要这个
if(k < 0) {
return rotate(A, k + A.length);
}
int[] newArr = new int[A.length];
for (int i = 0; i < A.length; i++) {
int newIndex = (i + k) % A.length;
newArr[newIndex] = A[i];
}
return newArr;
}
解释:对于k的正值,此解决方案本质上与前一个相同。对于k的负值,我们反复添加A.length直到它为正,这本质上就是模运算的作用。
3 方向指针¶
绘制运行以下代码后产生的框和指针图:
public class DLLStringNode {
DLLStringNode prev;
String s;
DLLStringNode next;
public DLLStringNode(DLLStringNode prev, String s, DLLStringNode next) {
this.prev = prev;
this.s = s;
this.next = next;
}
public static void main(String[] args) {
DLLStringNode L = new DLLStringNode(null, "eat", null);
L = new DLLStringNode(null, "bananas", L);
L = new DLLStringNode(null, "never", L);
L = new DLLStringNode(null, "sometimes", L);
DLLStringNode M = L.next;
DLLStringNode R = new DLLStringNode(null, "shredded", null);
R = new DLLStringNode(null, "wheat", R);
R.next.next = R;
M.next.next.next = R.next;
L.next.next = L.next.next.next;
/* 下面是可选练习。 */
L = M.next;
M.next.next.prev = R;
L.prev = M;
L.next.prev = L;
R.prev = L.next.next;
}
}
解答:¶
[此处应有指针图]
可选练习后的图:
[此处应有指针图]
注意,带有"bananas"和"sometimes"的DLLStringNode对象在最终结果中没有出现 - 当没有指针引用对象时,Java会"垃圾回收"这些对象。
4 网格化(Gridify)¶
public class SLList {
Node sentinel;
public SLList() {
this.sentinel = new Node();
}
private static class Node {
int item;
Node next;
}
public int[][] gridify(int rows, int cols) {
int[][] grid = new int[rows][cols];
gridifyHelper(grid, sentinel.next, 0);
return grid;
}
private void gridifyHelper(int[][] grid, Node curr, int numFilled) {
if(curr == sentinel || numFilled >= grid.length * grid[0].length) {
return;
}
int row = numFilled / grid[0].length;
int col = numFilled % grid[0].length;
grid[row][col] = curr.item;
gridifyHelper(grid, curr.next, numFilled + 1);
}
}
( b ) 为什么我们在这里使用辅助方法?即,为什么不能简单地使用签名gridify(int rows, int cols, Node curr, int numFilled),完全省略gridifyHelper?
解答:¶
要求用户每次调用gridify时都传入sentinel.next和0并不直观,因为这与他们实际请求的内容无关。此外,这打破了抽象屏障,因为它要求用户了解此方法在内部是如何工作的。最后,如果用户不理解应该传入什么(因为这不太直观),他们可能会传入一些随机值,导致错误的答案。
因此,让用户每次都传入这些额外的参数是不好的编程实践。然而,我们需要一种方法来跟踪我们在递归时所处的节点和索引,所以我们必须创建一个可以跟踪所有这些信息的辅助方法。