写作起因
最近因为找工作因为算法碰了不少壁。深感自己对算法的缺陷,于是买了《数据结构与算法分析-Java语言描述 第三版》复习一下自己的薄弱的算法基础。
结果做完题的时候,居然找不到一个像样的答案。这就很苦恼了。
Pearson官网必须要有教师资格证才能拿到答案。国内的出版社貌似也一样。既然没有,那就由我来为中文程序猿社区做点贡献吧。
不敢说全对,大体是没问题的。欢迎大家斧正。
3.1
给定一个表L和另一个表P,它们包含以升序排列的整数。操作printLots(L,P)将打印L中那些由P所指定的位置上的元素。例如,如果P=1,3,4,6,那么,L中位于第1、第3、第4和第6个位置上的元素被打印出来。写出过程printLots(L,P)。只可使用public型的Collections API容器操作。该过程的运行时间是多少?
根据题意,L存数据,P存L的下标,然后打印下标对应的元素。要求必须用Collections API来操作。 我特地去看了一下Collections的API,完全无法实现这个操作。看了第二版的题解(题在第二版是一样的),结果用的是List来当容器。好嘛,原来用的是Collection的API。(捂脸)
关于运行时间,应该说的是时间复杂度。
若L长为m,P长为n,那么时间复杂度为:O(m+n)
代码如下
import java.util.*;
public class ThreeOne {
public static void main(String[] args) {
Character[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
Integer[] idxs = {1, 4, 7, 10, 17, 25};
ArrayList<Character> L = new ArrayList<Character>(Arrays.asList(chars));
ArrayList<Integer> P = new ArrayList<Integer>(Arrays.asList(idxs));
long startTime = System.nanoTime();
System.out.println("startTime = " + startTime + "nanosec");
printLots(L, P);
long endTime = System.nanoTime();
System.out.println("endTime = " + endTime + "nanosec");
System.out.println("Duration: " + (endTime - startTime) + " nanosec or " + (endTime - startTime) / 1e6 + " milisec");
}
public static <T> void printLots(Collection<T> L, Collection<Integer> P) {
Iterator iterP = P.iterator();
Iterator iterL = L.iterator();
int idxL = 0;
T itemL = null;
while (iterP.hasNext() && iterL.hasNext()) {
int idx = (int) iterP.next();
while (iterL.hasNext() && idxL < idx) {
itemL = (T) iterL.next();
idxL++;
}
System.out.print(itemL + " ");
}
System.out.println();
}
}
3.2
通过只调整链(而不是数据)来交换两个相邻的元素,使用
a.单链表。
b.双链表。
很明显,这题要使用链表来实现。
代码如下
//For convenience, use double linked node as singly linked node.
class Node {
int value;
Node pre;
Node next;
}
public class ThreeTwo {
/**
* @param before the node before the two nodes to be swapped.
*/
public static void swapNodes(Node before) {
if (before == null || (before != null && before.next == null) || (before != null && before.next != null && before.next.next == null)) {
throw new IllegalArgumentException("There are no nodes to swap.");
}
Node first = before.next;
Node second = first.next;
before.next = second;
first.next = second.next;
second.next = first;
}
/**
* @param first the first node to be swapped.
*/
public static void swapNodesD(Node first) {
if (first == null || (first != null && first.next == null)) {
throw new IllegalArgumentException("There are no nodes to swap.");
}
Node second = first.next;
first.pre.next = second;
first.next = second.next;
second.pre = first.pre;
first.pre = second;
second.next = first;
}
/**
* Main
*
* @param args
*/
public static void main(String[] args) {
Node dummyHead = new Node();
Node cur = dummyHead;
for (int i = 0; i < 10; i++) {
cur.next = new Node();
cur.next.pre = cur;
cur.next.value = i;
cur = cur.next;
}
//swap 3 with 4
Node n = dummyHead.next.next.next;
swapNodes(n);
//swap 6 with 7
n = n.next.next.next.next;
swapNodesD(n);
Node p = dummyHead.next;
while (p != null) {
System.out.print(p.value + " ");
p = p.next;
}
}
}
3.3
实现MyLinkedList的contains例程。
题目没啥好解释的。这里我给出了两种方法。
//使用了MyLinkedList实现的Iterable接口。
public Node<AnyType> contains(AnyType value){
Iterable<AnyType> iter = this.iterator();
while(iter.hasNext()){
T next = iter.next();
if(next.data == value){
return next;
}
}
return null;
}
//使用了链表的基础遍历方法。
public Node<AnyType> contains(AnyType value){
Node<AnyType> p = beginMarker.next()
while(p != endMarker){
if(p.data == value)
return p;
p = p.next;
}
return null;
}
倒是在第二版中给出的答案挺有意思的。 在一定程度上,代码更简洁了,不过理解难度有一点点的上升。emm…突然感觉自己的答案不香了。一种bad code smell的感觉。(捂脸)
public boolean contains(AnyType x) {
Node<AnyType> p = beginMarker.next;
while (p != endMarker & !(p.data.equa1s(x))) { //将判断置于while循环条件里。
p = p.next;
}
return (p != endMarker); //这里返回的是 是否到达末尾。更简化了,而不是上述答案中的两个return。
}
3.4、3.5
3.4 给定两个已排序的表
L
1
L_1
L1和
L
2
L_2
L2,只使用基本的表操作编写计算
L
1
∩
L
2
L_1 \cap L_2
L1∩L2的过程。
3.5 给定两个已排序的表
L
1
L_1
L1和
L
2
L_2
L2,只使用基本的表操作编写计算
L
1
∪
L
2
L_1 \cup L_2
L1∪L2的过程。
public class ThreeFour {
public static void main(String[] args) {
Integer[] ints1 = new Integer[]{3, 5, 7, 9};
Integer[] ints2 = new Integer[]{1, 4, 5, 7, 10};
List<Integer> i1 = Arrays.asList(ints1);
List<Integer> i2 = Arrays.asList(ints2);
//自己答案的调用
List<Integer> intersection = intersection(i1, i2);
System.out.println("intersection = " + intersection);
List<Integer> union = union(i1, i2);
System.out.println("union = " + union);
//官方答案的调用
intersection.clear();
union.clear();
intersection(i1, i2, intersection);
System.out.println("intersection = " + intersection);
union(i1, i2, union);
System.out.println("union = " + union);
}
}
public static <T> List<T> intersection(List<T> l1, List<T> l2) {
ArrayList<T> list = new ArrayList<T>();
for (T t : l1) {
if (l2.contains(t)) list.add(t);
}
return list;
}
public static <T> List<T> union(List<T> l1, List<T> l2) {
ArrayList<T> list = new ArrayList<T>();
for (T t : l1) {
list.add(t);
}
for (T t : l2) {
list.add(t);
}
return list;
}
}
这里附上第二版官方答案。我也偷懒一下,不写根据已排序特性的算法了。
public static <AnyType extends Comparable<? super AnyType>> void intersection(List<AnyType> L1, List<AnyType> L2, List<AnyType> Intersect) {
ListIterator<AnyType> iterL1 = L1.listIterator();
ListIterator<AnyType> iterL2 = L2.listIterator();
AnyType itemL1 = null, itemL2 = null;
// get first item in each list
if (iterL1.hasNext() && iterL2.hasNext()) {
itemL1 = iterL1.next();
itemL2 = iterL2.next();
}
while (itemL1 != null && itemL2 != null) {
int compareResult = itemL1.compareTo(itemL2);
if (compareResult == 0) {
Intersect.add(itemL1);
itemL1 = iterL1.hasNext() ? iterL1.next() : null;
itemL2 = iterL2.hasNext() ? iterL2.next() : null;
} else if (compareResult < 0)
itemL1 = iterL1.hasNext() ? iterL1.next() : null;
else
itemL2 = iterL2.hasNext() ? iterL2.next() : null;
}
}
public static <AnyType extends Comparable<? super AnyType>> void union(List<AnyType> L1, List<AnyType> L2, List<AnyType> Result) {
ListIterator<AnyType> iterL1 = L1.listIterator();
ListIterator<AnyType> iterL2 = L2.listIterator();
AnyType itemL1 = null, itemL2 = null;
// get first item in each list
if (iterL1.hasNext() && iterL2.hasNext()) {
itemL1 = iterL1.next();
itemL2 = iterL2.next();
}
while (itemL1 != null && itemL2 != null) {
int compareResult = itemL1.compareTo(itemL2);
if (compareResult == 0) {
Result.add(itemL1);
itemL1 = iterL1.hasNext() ? iterL1.next() : null;
itemL2 = iterL2.hasNext() ? iterL2.next() : null;
} else if (compareResult < 0) {
Result.add(itemL1);
itemL1 = iterL1.hasNext() ? iterL1.next() : null;
} else {
Result.add(itemL2);
itemL2 = iterL2.hasNext() ? iterL2.next() : null;
}
}
}
3.6
Josephus问题(Josephus problem)是下面的游戏: N 个人编号从1到 N,围坐成一个圆圈。从1号开始传递一一个热土豆。经过 M 次传递后拿着热土豆的人被清除离座,围坐的圆圈缩紧,由坐在被清除的人后面的人拿起热土豆继续进行游戏。最后剩下的人取胜。因此,如果 M = 0 和 N = 5,则游戏人依序被清除,5号游戏人获胜。如果 M = 1 和 N = 5,那么被清除的人的顺序是 2,4,1,5。
a.编写一个程序解决 M 和 N 在一般值下的Josephus问题,应使程序尽可能地高效率,要确保能够清除各个单元。
b.你的程序的运行时间是多少?
先贴一下第二版答案,之后是我写的链表实现。
再看答案之前,要先明白一下 M 和 N 的关系。
1. 向前传递M次 = 向前传递 (M % N)次,可以去除绕圈行为。。这个很好理解吧。若M > N, 那就等于绕了 M / N 圈。然后在传递 M % N 次。
2. 向前传递M次 = 向后传递(M - N)次,当M > N/2时,这个关系就可减少传递,提高效率。。因为是一个圈。这个也成立。如下图,共7个元素。4向前走3步 -> 7 == 4向后走(3-7 = -4)步 -> 7。
明白了这两条关系,现在我们再来看答案的代码,就很好理解了。
public static void pass(int m, int n) {
int i, j, mPrime, numLeft;
ArrayList<Integer> L = new ArrayList<Integer>();
for (i = 1; i <= n; i++) {
L.add(i);
}
ListIterator<Integer> iter = L.listIterator();
Integer item = 0;
numLeft = n;
mPrime = m % n;
/* -------------- 开始 N 次清除 -------------- */
for (i = 0; i < n; i++) {
//第一条关系应用,去除不必要的绕圈。
mPrime = m % numLeft;
//第一条关系后,根据第二条关系判定要往前还是往后走。
if (mPrime <= numLeft / 2) {
//清除过后,热土豆传给下一个人,从下一个人开始传M次
if (iter.hasNext()) {
item = iter.next();
}
//传M次
for (j = 0; j < mPrime; j++) {
//在传递途中,若iter遍历到末尾,重新获取iter
if (!iter.hasNext()) {
iter = L.listIterator();
}
item = iter.next();
}
} else {
//第一条关系后,根据第二条关系判定要M > N/2, 往后走。
for (j = 0; j < numLeft - mPrime; j++) {
//若iter在指针在第一个元素之前,则获取指向最后一个元素之后的iter
if (!iter.hasPrevious()) {
iter = L.listIterator(L.size());
}
item = iter.previous();
}
}
System.out.print("Removed " + item + " ");
iter.remove();
//当remove最后一个元素后,重新或去iter,保证逻辑正确
if (!iter.hasNext()) {
iter = L.listIterator();
}
System.out.println();
for (Integer x : L) {
System.out.print(x + " ");
}
System.out.println();
numLeft--;
}
/* -------------- 结束 N 次清除 -------------- */
System.out.println();
}
关于listIterator,返回的ArrayList定义的内部类ListItr,实现了ListIterator 的接口。除了拥有Iterator的所有接口,ListIterator还有如下方法,从而实现对数组的操作。
- add()
- hasPrevious()
- previous()
- previousIndex()
- nextIndex()
- set()
3.7
下列程序的运行时间是多少?
public static List<Integer> makeList(int N) {
ArrayList<Integer> lst = new ArrayList<Integer>();
for (int i = 0; i < N; i++) {
lst.add(i);
lst.trimToSize();
}
return lst;
}
- for循环: n次
- lst.add(): O ( 1 ) O(1) O(1)
- lst.trimToSize():
O
(
n
)
O(n)
O(n)
所以,这个算法的时间复杂度是: O ( n 2 ) O(n^2) O(n2)
其中,trimToSize() 是将ArrayList中的数组大小 缩小 到它实际拥有元素的大小,追踪下去可以发现,调用的是系统的native方法。通过遍历元素,来创建新的数组。 所以,其方法必然是 O ( n ) O(n) O(n)的。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
3.8
下列例程删除作为参数被传递的表的前半部分:
public static void removeFirstHalf(List<?> lst) {
int theSize = lst.size() / 2;
for (int i = 0; i < theSize; i++) {
lst.remove(0);
}
}
a.为什么在进入for循环前存储theSize?
lst.size()
返回的大小,会因为remove()
而产生变动。从而导致删除不全。
b.如果lst是一个ArrayList, removeFirstHalf的运行时间是多少?
ArrayList的remove()
方法如下:
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).**/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* Copies an array from the specified source array, beginning at the
* specified position, to the specified position of the destination array.**/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
查看其源码后可知:
rangeCheck()
为O(1)。elementData()
为O(1)System.arraycopy()
为O(n),在remove()
里,是将删除元素之后的所有元素前移一位,且根据其注释可以知道其时间复杂度为O(n)。
因此,ArrayList的removeFirstHalf的时间复杂度是O(n2)。
c.如果lst是一个LinkedList, removeFirstHalf的运行时间是多少?
LinkedList的remove()
方法如下:
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.**/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
/**
* Unlinks non-null node x.
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
查看其源码后可知:
checkElementIndex()
为O(1)。node()
为O(n/2)=O(n)。unlink()
为O(1)。
因此,LinkedList的removeFirstHalf的时间复杂度是O(n2)。
而第二版答案中,认为LinkedList的removeFirstHalf的时间复杂度是O(n)。
(c)O(N). Each remove can be done in 0(1) time.
第二版答案中,认为LinkedList的removeFirstHalf的时间复杂度是O(n)。 可能有两种原因:
- 第二版答案所用的java版本不同于java1.8
- 第二版认为,
node()
为O(1)。对于该题确实为O(1),但是如果不限定删除的位置(index)的话,那么node()
应该为O(n).
d.对于这两种类型的List使用迭代器都能使removeFirstHalf更快吗?**
- ArrayList有两个实现了Iterator接口的类,分别为Itr和ListItr.
// An optimized version of AbstractList.Itr
private class Itr implements Iterator<E> {...}
// An optimized version of AbstractList.ListItr
private class ListItr extends Itr implements ListIterator<E> {...}
//ListIterator继承自Iterator
public interface ListIterator<E> extends Iterator<E> {...}
观察下方代码,可知不论是Itr还是ListItr,remove()
方法并没有优化,最终还是调用ArrayList的remove()
方法来移除元素。所以,不会更快。
//Itr的remove,ListItr的remove继承Itr的。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
- LinkedList有ListItr和DescendingIterator。但是,DescendingIterator的方法都是通过调用内部的ListItr来实现的。
// Adapter to provide descending iterators via ListItr.previous
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
...
}
private class ListItr implements ListIterator<E> {...}
观察下方代码,LinkedList的ListItr的remove()
方法和ArrayList有些不一样,调用的不是remove()
方法了。而是直接使用unlink()
来实现移除。可知:
checkForComodification()
为O(1)。unlink()
为O(1)。
且,ListItr的next()
方法为O(1),所以,整个过程为O(n/2) = O(n)。所以,会更快。
当然,如果根据上述提到的第二版答案的说法,这里的就是不会更快。
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
3.9、3.10
3.9
提供对MyArrayList类的addAll方法的实现。方法addAll将由items给定的特定集合的所有项添加到MyArrayList的末端。再提供上述实现的运行时间。你使用的方法声明与。Java Collections API中的略有不同,其形式如下:
public void addAll( Iterable<? extends AnyType> items )
public void addAll(Iterable<? extends AnyType> items ){
for (AnyType i : items) {
this.add(i);
}
}
this.add()
方法中有一个叫ensureCapacity()
的方法,这个方法会在容量不够的时候对数组扩容,并把旧数组复制到新数组。因此这个方法为O(n)。
所以,addAll()
方法的时间复杂度为:O(n2)。
以下是第二版答案:
public void addA11( Iterable<? extends AnyType> items ) {
Iterator<? extends AnyType> iter = items.iterator();
while (iter.hasNext()) {
add(iter.next());
}
}
This runs in O(N) time, where N is the size of the items collection.
这里第二版答案并没有考虑扩容的问题,所以我认为这个答案是错的。
3.10
提供对MyLinkedList类的removeAll方法的实现。方法removeAll将由items给定的特定集合的所有项从MyLinkedList中删除。再提供上述实现的运行时间。你使用的方法声明与Java Colletions API中的略有不同,其形式如下:
public void removeAll( Iterable<? extends AnyType> items )
public void removeAll( Iterable<? extends AnyType> items ){
for (AnyType i : items) {
LinkedListIterator itr = this.iterator();
while(itr.hasNext()){
if(itr.next().data == i) itr.remove();
}
}
}
时间复杂度为:O(mn)。
- m为items的长度
- n为MyLinkedList的长度。
3.11
假设单链表使用一个头节点实现,但无尾节点,并假设它只保留对该头节点的引用。编写一个类,包含
public class ThreeElevenMyLinkedList<T> {
private Node dummyHead;
private int theSize;
public ThreeElevenMyLinkedList() {
this.dummyHead = new Node(null, null);
this.theSize = 0;
}
class Node<T> {
private Node next;
private T value;
public Node(T value, Node next) {
this.next = next;
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
...
}
a.返回链表大小的方法。
public int size() {
return theSize;
}
b.打印链表的方法。
public void printList(){
Node n = dummyHead.next;
while (n != null) {
System.out.print(n.value + " ");
n = n.next;
}
System.out.println();
}
c.测试值x是否含于链表的方法。
/**
* check if the value exists in the list.
*
* @param v the value to be checked
* @return true if present, false if absent
*/
public boolean contains(T v) {
boolean contains = false;
Node n = dummyHead.next;
for (int i = 0; i < theSize; i++) {
if (!n.value.equals(v)) {
n = n.next;
} else {
contains = true;
break;
}
}
return contains;
}
d.如果值x尚未含于链表,添加值x到该链表的方法。
/**
* add the value into the list
*
* @param v the value to be add.
* @return the node added
*/
public Node<T> add(T v) {
dummyHead.next = new Node(v, dummyHead.next);
theSize++;
return dummyHead.next;
}
/**
* add the value into the list if absent.
*
* @param v the value to be add.
* @return the node added if absent, null if present.
*/
public Node<T> addIfAbsent(T v) {
if (!contains(v)) {
return add(v);
}
return null;
}
e.如果值x含于链表,将x从该链表中删除的方法。
/**
* remove the node whose value equals to parameter v if present.
*
* @param v the value to be removed
* @return return the node if present or null if absent.
*/
public Node<T> remove(T v) {
Node n = dummyHead.next;
Node pre = n;
while (n != null) {
if (!n.value.equals(v)) {
pre = n;
n = n.next;
} else {
pre.next = n.next;
theSize--;
break;
}
}
return n;
}
/**
* same as {@link #remove(T v)}
*/
public Node<T> removeIfPresent(T v) {
return remove(v);
}
3.12
保持单链表以排序的顺序重复练习3.11。
唯一有变化的就是add方法,所以,这里只展示类的代码和add方法的代码。
public class ThreeTweleveMyLinkedList<T extends Comparable<T>> {
private Node<T> dummyHead;
private int theSize;
public ThreeTweleveMyLinkedList() {
this.dummyHead = new Node<T>(null, null);
this.theSize = 0;
}
class Node<T extends Comparable<T>> {
private Node<T> next;
private T value;
public Node(T value, Node<T> next) {
this.next = next;
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
...
}
d.如果值x尚未含于链表,添加值x到该链表的方法。
/**
* Comapare then add the value into the list
*
* @param v the value to be add.
* @return the node added
*/
public Node<T> add(T v) {
Node<T> n = dummyHead.next;
Node<T> pre = dummyHead;
while (n != null) {
if (v.compareTo((T) n.value) < 0) {
break;
}
pre = n;
n = n.next;
}
pre.next = new Node<T>(v, n);
theSize++;
return dummyHead.next;
}
/**
* add the value into the list if absent.
*
* @param v the value to be add.
* @return the node added if absent, null if present.
*/
public Node<T> addIfAbsent(T v) {
if (!contains(v)) {
return add(v);
}
return null;
}
3.13
添加ListIterator对MyArrayList类的支持。java.util 中的ListIterator 接口比3.3.5节所述含有更多的方法。注意,你要编写一个listIterator方法返回新构造的ListIterator,并且还要注意现存的迭代器方法可以返回一个新构造的ListIterator。这样,你将改变ArrayListIterator,使得它实现ListIterator而不是Iterator。对于3.3.5节中未列出的那些方法抛出UnsupportedOperationException异常。
以下为MyArrayList类的源码:
public class ThreeThirteenMyArrayList<AnyType> implements Iterable<AnyType>{
private static final int DEFAULT_CAPACITY = 10;
private AnyType[] theItems;
private int theSize;
/**
* Construct an empty ArrayList.
*/
public ThreeThirteenMyArrayList() {
doClear();
}
/**
* Returns the number of items in this collection.
*
* @return the number of items in this collection.
*/
public int size() {
return theSize;
}
/**
* Returns true if this collection is empty.
*
* @return true if this collection is empty.
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Returns the item at position idx.
*
* @param idx the index to search in.
* @throws ArrayIndexOutOfBoundsException if index is out of range.
*/
public AnyType get(int idx) {
if (idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Index " + idx + "; size " + size());
return theItems[idx];
}
/**
* Changes the item at position idx.
*
* @param idx the index to change.
* @param newVal the new value.
* @return the old value.
* @throws ArrayIndexOutOfBoundsException if index is out of range.
*/
public AnyType set(int idx, AnyType newVal) {
if (idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Index " + idx + "; size " + size());
AnyType old = theItems[idx];
theItems[idx] = newVal;
return old;
}
@SuppressWarnings("unchecked")
public void ensureCapacity(int newCapacity) {
if (newCapacity < theSize)
return;
AnyType[] old = theItems;
theItems = (AnyType[]) new Object[newCapacity];
for (int i = 0; i < size(); i++)
theItems[i] = old[i];
}
/**
* Adds an item to this collection, at the end.
*
* @param x any object.
* @return true.
*/
public boolean add(AnyType x) {
add(size(), x);
return true;
}
/**
* Adds an item to this collection, at the specified index.
*
* @param x any object.
* @return true.
*/
public void add(int idx, AnyType x) {
if (theItems.length == size())
ensureCapacity(size() * 2 + 1);
for (int i = theSize; i > idx; i--)
theItems[i] = theItems[i - 1];
theItems[idx] = x;
theSize++;
}
/**
* Removes an item from this collection.
*
* @param idx the index of the object.
* @return the item was removed from the collection.
*/
public AnyType remove(int idx) {
AnyType removedItem = theItems[idx];
for (int i = idx; i < size() - 1; i++)
theItems[i] = theItems[i + 1];
theSize--;
return removedItem;
}
/**
* Change the size of this collection to zero.
*/
public void clear() {
doClear();
}
private void doClear() {
theSize = 0;
ensureCapacity(DEFAULT_CAPACITY);
}
/**
* Returns a String representation of this collection.
*/
public String toString() {
StringBuilder sb = new StringBuilder("[ ");
for (AnyType x : this) {
sb.append(x + " ");
}
sb.append("]");
return new String(sb);
}
public java.util.Iterator<AnyType> iterator() {
return new ArrayListIterator();
}
public java.util.ListIterator<AnyType> listIterator() {
return new ArrayListIterator();
}
...
}
以下为实现listIterator的源码:
private class ArrayListIterator implements java.util.ListIterator<AnyType> {
private int current = 0;
boolean backwards = false;
@Override
public boolean hasNext() {
return current < size();
}
@Override
public AnyType next() {
if (!hasNext())
throw new java.util.NoSuchElementException();
backwards = false;
return theItems[current++];
}
@Override
public boolean hasPrevious() {
return current > 0;
}
@Override
public AnyType previous() {
if (!hasPrevious())
throw new java.util.NoSuchElementException();
backwards = true;
return theItems[--current];
}
@Override
public void add(AnyType x) {
ThreeThirteenMyArrayList.this.add(current++, x);
}
@Override
public void remove() {
if (backwards)
ThreeThirteenMyArrayList.this.remove(current--);
else
ThreeThirteenMyArrayList.this.remove(--current);
}
@Override
public void set(AnyType newVal) {
ThreeThirteenMyArrayList.this.set(current, newVal);
}
@Override
public int nextIndex() {
throw new java.lang.UnsupportedOperationException();
}
@Override
public int previousIndex() {
throw new java.lang.UnsupportedOperationException();
}
}
}
3.14
如练习3.13所述,添加ListIterator对MyLinkedList类的支持。
第二版的源码虽然写了对并发的检查,但是存有bug,一下为修改版。
以下为MyLinkedList类的源码(带有并发修改检查):
/**
* LinkedList class implements a doubly-linked list.
*/
public class ThreeFourteenMyLinkedList<AnyType> implements Iterable<AnyType> {
private int theSize;
private int modCount = 0;
private Node<AnyType> beginMarker;
private Node<AnyType> endMarker;
/**
* Construct an empty LinkedList.
*/
public ThreeFourteenMyLinkedList() {
doClear();
}
private void clear() {
doClear();
}
/**
* Change the size of this collection to zero.
*/
public void doClear() {
beginMarker = new Node<>(null, null, null);
endMarker = new Node<>(null, beginMarker, null);
beginMarker.next = endMarker;
theSize = 0;
modCount++;
}
/**
* Returns the number of items in this collection.
*
* @return the number of items in this collection.
*/
public int size() {
return theSize;
}
public boolean isEmpty() {
return size() == 0;
}
/**
* Adds an item to this collection, at the end.
*
* @param x any object.
* @return true.
*/
public boolean add(AnyType x) {
add(size(), x);
return true;
}
/**
* Adds an item to this collection, at specified position. Items at or after
* that position are slid one position higher.
*
* @param x any object.
* @param idx position to add at.
* @throws IndexOutOfBoundsException if idx is not between 0 and size(),
* inclusive.
*/
public void add(int idx, AnyType x) {
addBefore(getNode(idx, 0, size()), x);
}
/**
* Adds an item to this collection, at specified position p. Items at or after
* that position are slid one position higher.
*
* @param p Node to add before.
* @param x any object.
* @throws IndexOutOfBoundsException if idx is not between 0 and size(),
* inclusive.
*/
private void addBefore(Node<AnyType> p, AnyType x) {
Node<AnyType> newNode = new Node<>(x, p.prev, p);
newNode.prev.next = newNode;
p.prev = newNode;
theSize++;
modCount++;
}
/**
* Returns the item at position idx.
*
* @param idx the index to search in.
* @throws IndexOutOfBoundsException if index is out of range.
*/
public AnyType get(int idx) {
return getNode(idx).data;
}
/**
* Changes the item at position idx.
*
* @param idx the index to change.
* @param newVal the new value.
* @return the old value.
* @throws IndexOutOfBoundsException if index is out of range.
*/
public AnyType set(int idx, AnyType newVal) {
return set(getNode(idx), newVal);
}
private AnyType set(Node<AnyType> p, AnyType newVal) {
AnyType oldVal = p.data;
p.data = newVal;
modCount++;
return oldVal;
}
/**
* Gets the Node at position idx, which must range from 0 to size( ) - 1.
*
* @param idx index to search at.
* @return internal node corresponding to idx.
* @throws IndexOutOfBoundsException if idx is not between 0 and size( ) - 1,
* inclusive.
*/
private Node<AnyType> getNode(int idx) {
return getNode(idx, 0, size() - 1);
}
/**
* Gets the Node at position idx, which must range from lower to upper.
*
* @param idx index to search at.
* @param lower lowest valid index.
* @param upper highest valid index.
* @return internal node corresponding to idx.
* @throws IndexOutOfBoundsException if idx is not between lower and upper,
* inclusive.
*/
private Node<AnyType> getNode(int idx, int lower, int upper) {
Node<AnyType> p;
if (idx < lower || idx > upper)
throw new IndexOutOfBoundsException("getNode index: " + idx + "; size: " + size());
if (idx < size() / 2) {
p = beginMarker.next;
for (int i = 0; i < idx; i++)
p = p.next;
} else {
p = endMarker;
for (int i = size(); i > idx; i--)
p = p.prev;
}
return p;
}
/**
* Removes an item from this collection.
*
* @param idx the index of the object.
* @return the item was removed from the collection.
*/
public AnyType remove(int idx) {
return remove(getNode(idx));
}
/**
* Removes the object contained in Node p.
*
* @param p the Node containing the object.
* @return the item was removed from the collection.
*/
private AnyType remove(Node<AnyType> p) {
p.next.prev = p.prev;
p.prev.next = p.next;
theSize--;
modCount++;
return p.data;
}
/**
* Returns a String representation of this collection.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[ ");
for (AnyType x : this)
sb.append(x + " ");
sb.append("]");
return new String(sb);
}
/**
* Obtains an Iterator object used to traverse the collection.
*
* @return an iterator positioned prior to the first element.
*/
@Override
public java.util.Iterator<AnyType> iterator() {
return new LinkedListIterator();
}
public java.util.ListIterator<AnyType> listIterator() {
return new LinkedListIterator();
}
/**
* This is the doubly-linked list node.
*/
private static class Node<AnyType> {
public Node(AnyType d, Node<AnyType> p, Node<AnyType> n) {
data = d;
prev = p;
next = n;
}
public AnyType data;
public Node<AnyType> prev;
public Node<AnyType> next;
}
...
}
以下为实现listIterator的源码(带有并发修改检查):
private class LinkedListIterator implements java.util.ListIterator<AnyType> {
private Node<AnyType> current = beginMarker.next;
private int expectedModCount = modCount;
private boolean okToRemove = false;
public boolean hasNext() {
return current != endMarker;
}
public AnyType next() {
if (modCount != expectedModCount)
throw new java.util.ConcurrentModificationException();
if (!hasNext())
throw new java.util.NoSuchElementException();
AnyType nextItem = current.data;
current = current.next;
okToRemove = true;
return nextItem;
}
public boolean hasPrevious() {
return current.prev != beginMarker;
}
public AnyType previous() {
if (modCount != expectedModCount)
throw new java.util.ConcurrentModificationException();
if (!hasPrevious())
throw new java.util.NoSuchElementException();
current = current.prev;
AnyType previousItem = current.data;
okToRemove = true;
return previousItem;
}
public void add(AnyType x) {
if (modCount != expectedModCount)
throw new java.util.ConcurrentModificationException();
ThreeFourteenMyLinkedList.this.addBefore(current.next, x);
expectedModCount++;
}
public void remove() {
if (modCount != expectedModCount)
throw new java.util.ConcurrentModificationException();
if (!okToRemove)
throw new IllegalStateException();
ThreeFourteenMyLinkedList.this.remove(current.prev);
expectedModCount++;
okToRemove = false;
}
public void set(AnyType newVal) {
if (modCount != expectedModCount)
throw new java.util.ConcurrentModificationException();
ThreeFourteenMyLinkedList.this.set(current.next, newVal);
expectedModCount++;
}
public int nextIndex() {
throw new java.lang.UnsupportedOperationException();
}
public int previousIndex() {
throw new java.lang.UnsupportedOperationException();
}
}