第三章 表、栈、队列 ——【习题解答】数据结构与算法分析-Java语言描述 第三版

写作起因

最近因为找工作因为算法碰了不少壁。深感自己对算法的缺陷,于是买了《数据结构与算法分析-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 L1L2的过程。
3.5 给定两个已排序的表 L 1 L_1 L1 L 2 L_2 L2,只使用基本的表操作编写计算 L 1 ∪ L 2 L_1 \cup L_2 L1L2的过程。


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。

1
2
3
4
5
6
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);

查看其源码后可知:

  1. rangeCheck()为O(1)。
  2. elementData()为O(1)
  3. 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;
    }

查看其源码后可知:

  1. checkElementIndex()为O(1)。
  2. node()为O(n/2)=O(n)。
  3. 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)。 可能有两种原因:

  1. 第二版答案所用的java版本不同于java1.8
  2. 第二版认为,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()来实现移除。可知:

  1. checkForComodification()为O(1)。
  2. 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();
        }
    }

持续更新中…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值