Java Stack 和Queues,先转过来,慢慢看

来源:互联网 发布:上海网站排名优化 编辑:程序博客网 时间:2024/06/05 04:07

Stacks and queues.

In this section, we introduce two closely-related data types for manipulatingarbitrarily large collections of objects: thestackand the queue.Each is defined by two basic operations:insert a new item, andremove an item.When we insert an item, our intent is clear. But when we removean item, which one do we choose?The rule used for a queue is to always remove the item that has been in the collection themost amount of time. This policy is knownas first-in-first-out orFIFO.The rule used for a stack is to always remove the item that has beenin the collection theleast amount of time.This policy is known as last-in first-out or LIFO.

Pushdown stacks.

A pushdown stack (or just a stack) is a collection that is based on the last-in-first-out (LIFO) policy. When you click a hyperlink, your browser displays the new page (and inserts it onto a stack). You can keep clicking on hyperlinks to visit new pages. You can always revisit the previous page by clicking the back button (remove it from a stack).The last-in-first-out policy offered by a pushdown stack providesjust the behavior that you expect.

Pushdown stack

By tradition, we name the stack insert method push()and the stackremove operation pop().We also include a method to test whether the stack is empty.The following API summarizes the operations:

API for a stack of strings
The asterisk indicates that we will be considering more than one implementationof this API.

Array implementation.

Representing stacks with arrays is a natural idea.The first problem that you might encounter is implementing the constructorArrayStackOfStrings(). An instance variablea[] with an array of strings to hold the stack items is clearly needed, but how big should it be?For the moment, We will finesse this problem by having the client provide anargument for the constructor that gives the maximum stack size. We keep the items in reverse order of their arrival.This policy allows us to add and remove items at the end without moving any ofthe other items in the stack.

We could hardly hope for a simpler implementation of ArrayStackOfStrings.java:all of the methods are one-liners! The instance variables are an arraya[] that hold the items in the stack and an integer N that counts the number of items in the stack. To remove an item, we decrementN and then return a[N]; to insert a new item, we set a[N] equal to the new item and thenincrementN. These operations preserve the following properties: the items in the array are in their insertion order the stack is empty when the value ofN is 0 the top of the stack (if it is nonempty) is at a[N-1]

Trace of ArrayStackOfStrings test client
The primary characteristic of this implementation is thatthe push and pop operations take constant time. The drawback of this implementation is that it requires the client to estimate the maximum size of the stack ahead of time and always uses space proportional to that maximum, which may be unreasonable in some situations.

Linked lists.

For classes such as stacks that implement collections of objects,an important objective is to ensure thatthe amount of space used is always proportional to the number of itemsin the collection.Now we consider the use of a fundamental data structure known as alinked list that can provide implementations of collections (and, in particular, stacks) that achieves this importantobjective.

A linked list is a recursive data structure defined as follows: a linked list is either empty (null) or a reference to a node having a reference to a linked list. Thenode in this definition is an abstract entity that might hold any kind of data in addition to the node reference that characterizes its role in buildinglinked lists. With object-oriented programming, implementing linked lists is not difficult. We start with a simple example of a class for the node abstraction:

class Node {    String item;    Node next; } 
A Node has two instance variables: a String and a Node.TheString is a placeholder in this example for any data that we might wantto structure with a linked list (we can use any set of instance variables);the instance variable of typeNode characterizes the linked nature of thedata structure. Now, from the recursive definition, we can represent a linked listby a variable of typeNode just by ensuring that its value is either null or a reference to aNode whose next field is a reference to a linked list.

We create an object of type Node by invoking its (no-argument) constructor.This creates a reference to aNode object whose instance variablesare both initialized to the value null. For example, to build a linked list that contains the items "to","be", and"or", we create a Node for each item:

linking together a linked list

Node first  = new Node(); Node second = new Node(); Node third  = new Node(); 
and set the item field in each of the nodes to the desired item value:
first.item  = "to"; second.item = "be"; third.item  = "or";
and set the next fields to build the linked list:
first.next  = second; second.next = third; third.next  = null;
When tracing code that uses linked lists and other linked structures, we use a visual representation of the changes where we draw a rectangle torepresent each object we put the values of instance variables within the rectangle we depict references as arrows that point to the referenced object This visual representation captures the essential characteristic of linked listsand allows us to focus on the links.
  • Insert.Suppose that you want to insert a new node into a linked list.The easiest place to do so is at the beginning of the list. For example, to insert the string"not" at the beginning of a given linkedlist whose first node is first, we savefirst in oldfirst, assign to first a new Node, assign itsitem field to "not"and its next field to oldfirst.

    inserting an item into a linked list
  • Remove.Suppose that you want to remove the first node from a list.This operation is even easier: simply assign tofirst the valuefirst.next. Normally, you would retrieve the value of theitem (by assigning it to some String variable) before doing this assignment, because once you change the value offirst,you may not have any access to the node to which it was referring. Typically, the node object becomes an orphan, and the memory it occupies is eventually reclaimed by the Java memory management system.

    removing an item from a linked list
These two operations take constant time (independent of the length of the list).

Implementing stacks with linked lists.

Program LinkedStackOfStrings.javauses a linked list to implement a stack of strings.The implementation is based on anested class Node like the one we have been using. Java allows us to define and use other classes within classimplementations in this natural way. The class isprivate because clients do not need to know any of the details of the linked lists.

trace of stack implementation using a linked list of strings

List traversal.

One of the most common operations we perform on collections is to iterate through the items in the collection. For example, we might wish to implement thetoString() method to facilitate debugging our stack code with traces.ForArrayStackOfStrings, this implementation is familiar:
public String toString() {    String s = "";    for (int i = 0; i < N; i++)       s += a[i] + " ";    return s; } 
As usual, this solution is intended for use only when N is small - it takes quadratictime because string concatenation takes linear time. Our focus now is just on theprocess of examining every item. There is a corresponding idiom for visiting theitems in a linked list: We initialize a loop index variable x thatreferences the the firstNode of the linked list. Then, we find the value of the item associated withx by accessingx.item, and then update x to refer to the nextNodein the linked list assigning to it the value of x.next, repeatingthis process untilx is null (which indicates that we have reached the end of the linked list). This process is known astraversingthe list, and is succinctly expressed in this implementation oftoString() for LinkedStackOfStrings:
public String toString() {    String s = "";    for (Node x = first; x != null; x = x.next)       s += x.item + " ";    return s; } 

Array doubling.

Next, we consider an approach to accommodating arbitrary growth and shrinkage in adata structure that is an attractive alternative to linked lists. As with linked lists,The idea is to modify the array implementation to dynamically adjust the size of the arraya[] so that it is (i) both sufficiently large to hold all of the items and (ii) not so large as to waste anexcessive amount of space.ProgramDoublingStackOfStrings.javais a modification of ArrayStackOfStrings.javathat achieves these objectives.

First, in push(), we check whether the array is too small. In particular, we check whether there is room for the new item in the array by checking whether the stack sizeN is equal to the array size a.length.If not, we just insert it witha[N++] = item as before; if so,we double the size of the array, by creating a new array of twice the size,copying the stack items to the new array, and resetting thea[]instance variable to reference the new array. Similarly, in pop(), we begin by checking whether the array is too large, and wehalve its size if that is the case.

Pushdown stack

Parameterized data types.

We have developed one stack implementation that allows us to build a stack of one particular type (String).In other applications we might need a stack ofintegers or a stack of oranges or a queue of customers.
  • Create a stack of Objects.We could develop one stack implementation StackOfObjects.javawhose elements are of type Object.Using inheritance, we can insert an object of any type.However, when we pop it, we must cast it back to the appropriate type.This approach can expose us to subtlebugs in our programs that cannot be detected until runtime. Forexample, there is nothing to stop a programmer from putting differenttypes of objects on the same stack, then encountering a runtimetype-checking error, as in the following example:
    StackOfObjects stack = new StackOfObjects();Apple  a = new Apple();Orange b = new Orange();stack.push(a);stack.push(b);a = (Apple)  (stack.pop());   // throws a ClassCastExceptionb = (Orange) (stack.pop());
    This toy example illustrates a basic problem.When we use type casting with an implementation such asStackfor different types of items, we are assuming that clients will castobjects popped from the stack to the proper type. This implicit assumption contradicts our requirement for ADTs that operations are tobe accessed only through an explicit interface.One reason that programmers use precisely defined ADTsis to protect future clients against errors that arise from such implicit assumptions.The code cannot be type-checked at compile time: there might be an incorrectcast that occurs in a complex piece of code that could escape detection untilsome particular runtime circumstance arises. Such an error isto be avoided at all costs because it could happen long after animplementation is delivered to a client, who would have no way to fixit.
  • Java generics.We use Java generics to limit the objects on a stack or queueto all be of the same type within a given application.The primary benefit is to discover type mismatch errors at compile-timeinstead of run-time.This involves a small bit of new Java syntax.We name the generic class Stack. It is identicaltoStackOfStrings except that we replace every occurrence of String withItemand declare the class as follows:
    public class Stack<Item>
    Program Stack.java implements a generic stack using this approach. The client
    Stack<Apple> stack = new Stack<Apple>();Apple  a = new Apple();Orange b = new Orange();stack.push(a);stack.push(b);     // compile-time error
    Program DoublingStack.java implements a generic stack using an array. For technical reasons, one castis needed when allocating the array of generics.

Autoboxing.

We have designed our stacks so that they can store anygeneric object type. We now describe the Java languagefeature, known asauto-boxing and auto-unboxing,that enables us to reuse the same code with primitive types as well.Associated with each primitive type, e.g.int,is a full blown object type, e.g., Integer. When we assigna primitive to the corresponding object type (or vice versa), Javaautomatically performs the transformation. This enables us to writecode like the following.
Stack<Integer> stack = new Stack<Integer>();stack.push(17);            // auto-boxing   (converts int to Integer)int a = stack.pop();       // auto-unboxing (converts Integer to int)

The value 17 is automatically cast to be of type Integer when wepass it to thepush() method.The pop() method returns an Integer, and this valueis cast to anint when we assign it to the variable a.We should be aware of what is going on behind thescenes since this can affect performance.

Java supplies built-in wrapper types for all of the primitivetypes:Boolean,Byte,Character, Double, Float,Integer, Long, andShort.These classes consist primarily of static methods (e.g., Integer.parseInt(),Integer.reverse()),but they also include some non-static methods(compareTo(),equals(), doubleValue()).

Queue.

A queue supports the insert and remove operations using a FIFOdiscipline. By convention, we name the queue insert operationenqueueand the remove operation dequeue.Lincoln tunnel.Student has tasks that must be completed. Put on a queue.Do the tasks in the same order that they arrive.

LIFO queue

public class Queue<Item> {   public boolean isEmpty();   public void enqueue(Item item);   public Item dequeue();}
  • Linked list implementation.Program Queue.javaimplements a FIFO queue of strings using a linked list.Like Stack, we maintain a referencefirstto the least-recently added Node on the queue.For efficiency, we also maintain a referencelast to theleast-recently added Node on the queue.

    Linked List implementation of a queue (insert)
  • Array implementation.Similar to array implementation of stack, but a little trickiersince need to wrap-around.ProgramDoublingQueue.javaimplements the queue interface.The array is dynamically resized using repeated doubling.

Iteration.

Sometimes the client needs to access all of the items of a collection, one at a time, without deleting them.To maintain encapsulation, we do not want to reveal theinternal representation of the queue (array or linked list) tothe client."Decouple the thing that needs to traverse the list from the detailsof getting each element from it."We solve this design challenge by using Java'sjava.util.Iterator interface:
public interface Iterator<Item> {    boolean hasNext();    Item next();    void remove();      // optional}
That is, any data type that implements the Iterator interface promisesto implement two methods:hasNext() and next().The client uses these methods to access the list elements one a time usingthe following idiom.
Queue<String> queue = new Queue<String>();...Iterator<String> i = queue.iterator();while (i.hasNext()) {   String s = i.next();   StdOut.println(s);}
  • Queue iterator in Java.Queue.javaillustrates how to implement anIterator when theitems are stored in a linked list.

    public Iterator iterator()  { return new QueueIterator();  }private class QueueIterator implements Iterator<Item> {    Node current = first;        public boolean hasNext()  { return current != null; }    public Item next() {        Item item = current.item;        current = current.next;         return item;    }}
    It relies on a private nested subclassQueueIterator that implements theIterator interface.The method iterator() creates an instance of typeQueueIterator and returns it as anIterator.This enforces the iteration abstraction since the client will onlythe items through thehasNext() and next() methods.The client has no access to the internals of theQueueor even the QueueIterator.It is the client's responsibility to only add elements to the listwhen no iterator is in action.
  • Enhanced for loop.Iteration is such a useful abstraction that Java provides compactsyntax (known as theenhanced for loop)to iterate over the elements of a collection (or array).

    Iterator<String> i = queue.iterator();while (i.hasNext()) {   String s = i.next();   StdOut.println(s);}for (String s : queue)    StdOut.println(s);

    To take advantage of Java's enhanced foreach syntax,the data type must implement Java'sIterable interface.

    public interface Iterable<Item> {    Iterator<Item> iterator();}
    That is, the data type must implementa method named iterator() that returns anIteratorto the underlying collection.Since our Queue ADT now includes such a method,we simply need to declare it as implementing theIterableinterface and we are ready to use the foreach notation.
    public class Queue<Item> implements Iterable<Item>

Stack and queue applications.

Stacks and queues have numerous useful applications.
  • Queue applications:Computing applications: serving requests of a single shared resource (printer,disk, CPU),transferring data asynchronously (data not necessarily received at same rate as sent)between two processes (IO buffers), e.g., pipes, file IO, sockets.Buffers on MP3 players and portable CD players, iPod playlist.Playlist for jukebox - add songs to the end, play fromthe front of the list.Interrupt handling: When programming a real-time system that can be interrupted (e.g., by a mouse click or wireless connection), it is necessary to attend to the interrupts immediately, before proceeding with the current activity. If the interrupts should be handles in the same order they arrive, then a FIFO queue is the appropriate data structure.
  • Arithmetic expression evaluation.Program Evaluate.javaevaluates a fully parenthesized arithmetic expression.

    An important application of stacks is in parsing. Forexample, a compiler must parse arithmetic expressionswritten usinginfix notation. For example the followinginfix expression evaluates to 212.

    ( 2 + ( ( 3 + 4 ) * ( 5 * 6 ) ) )
    We break the problem of parsing infix expressions into twostages. First, we convert from infix to a different representationcalledpostfix. Then we parse the postfix expression,which is a somewhat easier problem than directly parsing infix.
    • Evaluating a postfix expression.A postfix expression is....
      2 3 4 + 5 6 * * + 
      First, we describe how to parse and evaluate a postfix expression.We read the tokens in one at a time.If it is an integer, push it on the stack; if it is a binaryoperator, pop the top two elements from the stack, apply the operatorto the two elements, and push the result back on the stack.Program Postfix.java readsin and evaluates postfix expressions using this algorithm.
    • Converting from infix to postfix.Now, we describe how to convert from infix to postfix.We read in the tokens one at a time. If it is an operator,we push it on the stack; if it is an integer, we print it out;if it is a right parentheses,we pop the topmost element from the stack and print it out;if it is a left parentheses, we ignore it.ProgramInfix.java reads in an infix expression,and uses a stack to output an equivalent postfix expression usingthe algorithm described above.Relate back to the parse tree example in Section 4.3.
  • Function calls.Perhaps the most important application of stacks is to implement function calls.Most compilers implement function calls by using a stack.This also provides a technique for eliminating recursion from a program:instead of calling a function recursively, the programmer uses a stackto simulate the function calls in the same way that the compiler wouldhave done so.Conversely, we can often use recursion instead of using an explicit stack.Some programming languages provide a mechanism for recursion, but notfor calling functions.

    Programming languages have built in support for stacks (recursion), but no analogous mechanism for dealing with queues.

    Postscript and FORTH programming languages are stack based.Java bytecode is interpreted on (virtual) stack based processor.Microsoft Intermediate Language (MSIL) that .NET applicationsare compiled to.

  • M/M/1 queue.The Markov/Markov/Single-Server modelis a fundamental queueing model in operations research and probability theory.Tasks arrive according to aPoisson processat a certain rate λ.This means that λ customers arrive per hour. More specifically,the arrivals follow an exponential distribution with mean 1 / λ:the probability of k arrivals between time 0 and tis (λ t)^k e^(-λ t) / k!.Tasks are serviced in FIFO order according to a Poisson processwith rate μ.The two M's standard for Markov: it means that the systemismemoryless: the time betweenarrivals is independent, and the time between departures isindependent.

    Analysis of M/M/1 model.We are interested in understanding the queueing system.If λ > μ the queue size increases without limit.For simple models like M/M/1 we can analyze these quantities analyticallyusing probability theory.Assuming μ > λ, the probability of exactlyn customers in the system is (λ / μ)^n (1 - λ / μ).

    • L = average number of customers in the system = λ / (μ - λ).
    • LQ = average number of customers in the queue =λ2 / (μ (μ - λ)).
    • W = average time a customer spends in the system = 1 / (μ - λ).
    • WQ = average time a customer spends in the queue = W - 1 / μ.

    Program MM1Queue.javaFor more complex models we need to resort to simulationlike this.Variants: multiple queues, multiple servers, sequential multi-stageservers, using a finite queue and measuring number of customers that areturned away.Applications: customers in McDonalds, packets in an internet router,

    Little's law asserts thatthe average number of customers in a (stable) queueing system equals the average arrival ratetimes their average time in the system.But the variance of customer waiting times satisfies: Var(FIFO) < Var(SIRO) < Var(LIFO).

    The distribution of the number of customers in the system does not depend on the queueing discipline (so long as it is independent of their service times).Same for expected waiting time.

  • M/D/1 queue.Program MD1Queue.java is similar but the service occurs at a fixed rate (rather than random).
  • Load balancing.Write a program LoadBalance.javathat performs a load-balancing simulation.

Q + A.

Q. When do I use new with Node?

A. Just as with any other class, you should only use new when you want to create a newNode object (a new element in the linked list). You should not use new to create a new reference to an existing Node object. For example, the code

Node oldfirst = new Node(); oldfirst = first; 
creates a new Node object, then immediately loses track of the only reference to it. This code does not result in an error, but it is a bit untidy to create orphans for no reason.

Q. Why declare Node as a nested class? Why private?

A. By declaring the subclass Node to be privatewe restrict access to methods within the enclosing class.One characteristic of a private nested class is that its instance variables canbe directly accessed from within the enclosing class, but nowhere else,so there is no need to declare them public or private.Note : A nested class that is not static is known as aninner class, so technically our Node classes are inner classes, though the ones that are not generic could be static.

Q. Why does javac LinkedStackOfStrings.javacreates a fileLinkedStackOfStrings$Node.class as well as LinkedStackOfStrings.class?

A. That file is for the nested class Node. Java's naming convention is to use $ to separate the name of the outer class from the nested class.

Q. Should a client be allowed to insert null items onto a stack or queue? A. This question arises frequently when implementing collections in Java. Our implementation (and Java's stack and queue libraries) do permit the insertion of null values.

Q. Are there Java libraries for stacks and queues?

A. Yes and no. Java has a built in library called java.util.Stack, but you should avoid using it when you want a stack. It has several additional operations that arenot normally associated with a stack, e.g., getting the ith element. It also allowsadding an element to the bottom of the stack (instead of the top), so it can implementa queue! Although having such extra operations may appear to be a bonus, it is actually a curse. We use data types not because they provide every available operation, but rather because they allow us to precisely specify the operations we need. The prime benefit of doing so is that the system can prevent us from performing operations that we do not actually want. Thejava.util.Stack API is an example of a wide interface, which we generally strive to avoid.

Q. I want to use an array representation for a generic stack, but code like the following will not compile. What is the problem?

private Item[] a = new Item[max]; oldfirst = first; 

A. Good try. Unfortunately, creating arrays of generics is not allowed in Java 1.5. Experts still are vigorously debating this decision. As usual, complaining too loudly about a language feature puts you on the slippery slope towards becominga language designer. There is a way out, using a cast: you can write:

private Item[] a = (Item[]) new Object[max]; oldfirst = first; 
The underlying cause is that arrays in Java are covariant,but generics are not.In other words,String[] is a subtype ofObject[], but Stack<String> is not asubtype ofStack<Object>.To get around this defect, you need to perform an unchecked castas inDoublingStack.java.Many programmers consider covariant arrays to be a defect inJava's type system (and this resulted in the need for "reifiable types"and "type erasure").However, in a world without generics,covariant arrays are useful, e.g., toimplement Arrays.sort(Comparable[]) and have it be callable with aninput array of typeString[].

Q. Can I use the foreach construction with arrays?

A. Yes (even though arrays do not implement the Iterator interface). The following prints out the command-line arguments:

public static void main(String[] args) {   for (String s : args)      StdOut.println(s);} 

Q.Is iterating over a linked list more efficient with a loop or recursion?

A.An optimizing compiler will likely translate atail-recursive function into the equivalent loop, so theremay be no observable performance overhead of using recursion.

Q.How does auto-boxing handle the following code fragment?

Integer a = null;int b = a;

A.It results in a run-time error. Primitive type can store everyvalue of their corresponding wrapper type exceptnull.

Q.Why does the first group of statements print true,but the second two printfalse?

Integer a1 = 100;Integer a2 = 100;System.out.println(a1 == a2);   // trueInteger b1 = new Integer(100);Integer b2 = new Integer(100);System.out.println(b1 == b2);   // falseInteger c1 = 150;Integer c2 = 150;System.out.println(c1 == c2);   // false

A.The second prints false because b1 andb2 are references to different Integer objects.The first and third code fragments rely on autoboxing.Surprisingly the first prints true because values between -128 and127 appear to refer to the same immutable Integer objects (presumablythere is a pool of them that are reused),while Java creates new objects for each integer outside this range.Lesson: as usual, don't use== to compare whether two objectshave the same value.

Q.Are generics solely for auto-casting?

A.No, but this will be the only thing we use them for.This is known as "pure generics" or "concrete parameterized types."Concrete parameterized types work almost like normal typeswith a few exceptions (array creation, exception handling, with instanceof,and in a class literal).More advanced uses of generics, including "wildcards", are are useful for handling subtypes and inheritance.Here is agenerics tutorial.

Q.Why do I get an incompatible types compile-time error with thefollowing code?

Stack stack = new Stack<String>();stack.push("Hello");String s = stack.pop();

A.You forgot to specify the concrete type when declaring stack.It should be Stack<String>.

Q.Why do I get a uses unchecked or unsafe operations compile-timewarning with the following code?

Stack<String> stack = new Stack();stack.push("Hello");String s = stack.pop();

A.You forgot to specify the concrete type when calling the constructor.It should benew Stack<String>().


Exercises

  1. Add a method isFull() to ArrayStackOfStrings.java.
  2. Give the output printed by java ArrayStackOfStrings 5 for the input
    it was - the best - of times - - - it was - the - -  
  3. Suppose that a client performs an intermixed sequence of (stack) push and pop operations. The push operations put the integers 0 through 9 in order on to the stack; the pop operations print out the return value. Which of the following sequence(s) could not occur?
    (a)  4 3 2 1 0 9 8 7 6 5(b)  4 6 8 7 5 3 2 9 0 1 (c)  2 5 6 7 4 8 9 3 1 0(d)  4 3 2 1 0 5 6 7 8 9(e)  1 2 3 4 5 6 9 8 7 0 (f)  0 4 6 5 3 8 1 7 2 9 (g)  1 4 7 9 8 6 5 3 0 2 (h)  2 1 4 3 6 5 8 7 9 0 
  4. Write a stack client Reverse.javathat reds in strings from standard input and prints them in reverse order.
  5. Assuming that standard input has some unknown number N of double values. Write a method that reads all the values and returnsan array of lengthN containing them, in the other they appearon standard input.
  6. Write a stack client Parentheses.javathat reads in a text stream from standard input and uses a stack todetermine whether its parentheses are properly balanced.For example, your program should printtrue for [()]{}{[()()]()} and false for [(]). Hint : Use a stack.
  7. What does the following code fragment print when N is 50? Give a high-level description of what the code fragment does when presented with a positive integerN.
    Stack stack = new Stack();while (N > 0) {   stack.push(N % 2);   N = N / 2;}while (!stack.isEmpty())    StdOut.print(stack.pop());StdOut.println();

    Answer: prints the binary representation of N(110010 when N is 50).

  8. What does the following code fragment do to the queue q?
    Stack stack = new Stack();while (!q.isEmpty())   stack.push(q.dequeue());while (!stack.isEmpty())   q.enqueue(stack.pop());
  9. Add a method peek() to Stack.javathat returns the most recently inserted element on the stack (withoutpopping it).
  10. Give the contents and size of the array for DoublingStackOfStringswith the input
    it was - the best - of times - - - it was - the - - 
  11. Add a method length() toQueue.java that returns the number of elements on the queue. Hint: Make sure that your method takes constant time by maintaining an instance variableN that you initializeto 0, increment in enqueue(), decrement indequeue(),and return in length().
  12. Draw a memory usage diagram in the style of the diagrams in Section 4.1for the three-node example used to introduce linked lists in thissection.
  13. Write a program that takes from standard input an expression without left parentheses and prints the equivalent infix expression with the parentheses inserted.For example, given the input
     1 + 2 ) * 3 - 4 ) * 5 - 6 ) ) ) 
    your program should print
     ( ( 1 + 2 ) * ( ( 3 - 4 ) * ( 5 - 6 ) ) 
  14. Write a filter InfixToPostfix.javathat converts an arithmetic expression from infix to postfix.
  15. Write a program EvaluatePostfix.javathat takes a postfix expression from standard input, evaluates it, and printsthe value. (Piping the output of your program from the previous exercise to this program gives equivalent behavior toEvaluate.java.)
  16. Suppose that a client performs an intermixed sequence of (queue) enqueue anddequeue operations. The enqueue operations put the integers 0 through 9 in order on to the queue; the dequeue operations print out the return value. Which of the following sequence(s) couldnot occur?
    (a)  0 1 2 3 4 5 6 7 8 9 (b)  4 6 8 7 5 3 2 9 0 1 (c)  2 5 6 7 4 8 9 3 1 0 (d)  4 3 2 1 0 5 6 7 8 9 
  17. Write an iterable Stack client that has a static methods copy() that takes a stack of strings as argument and returns a copy of the stack.Note: This ability is a prime example of the value of having an iterator, because it allows development of such functionality without changing the basic API.
  18. Develop a class DoublingQueueOfStrings.javathat implements the queue abstraction with a fixed-size array, andthen extend your implementation to use array doubling to remove the size restriction.
  19. Write a Queue.java client that takes a command-lineargument k and prints thekth from the last string found on standard input.
  20. (For the mathematically inclined.) Prove that the array in DoublingStackOfStrings.javais never less than one-quarter full. Then prove that, for anyDoublingStackOfStrings client, the total cost of all of the stack operations divided by the number of operations is a constant.
  21. Modify MD1Queue.java to make a program MM1Queue.java that simulates a queue for which both the arrival and service times are Poisson processes.Verify Little's law for this model.
  22. Develop a class StackOfInts.javathat uses a linked-list representation (but no generics).Write a client that compares the performance of your implementation withStack<Integer> to determine the performance penalty fromautoboxing on your system.


Linked List Exercises

  1. Write a method delete() that takes an int argumentk and deletes thekth element in a linked list,if it exists.

    Solution.

    // we assume that first is a reference to the first Node in the listpublic void delete(int k) {    if (k <= 0) throw new RuntimeException("Invalid value of k");    // degenerate case - empty linked list    if (first == null) return;    // special case - removing the first node    if (k == 1) {        first = first.next;        return;    }    // general case, make temp point to the (k-1)st node    Node temp = first;    for (int i = 1; i < k; i++) {        temp = temp.next;        if (temp == null) return;   // list has < k nodes    }    if (temp.next == null) return;  // list has < k nodes    // change temp.next to skip kth node    temp.next = temp.next.next;}
  2. Write a method find() that takes a linked list and a string keyas arguments and returnstrue if some node in the list haskey as its item field,false otherwise.
  3. Suppose x is a linked-list node. What is the effect of the following code fragment?
    x.next = x.next.next;

    Answer: Deletes from the list the node immediately following x.

  4. Suppose that x is a linked-list node. What is the effect ofthe following code fragment?
    t.next = x.next;x.next = t;     

    Answer: Inserts node t immediately after node x.

  5. Why does the following code fragment not have the same effect asin the previous question?
    x.next = t;t.next = x.next;

    Answer: When it comes time to update t.next,x.next is no longer the original node followingx,but is instead t itself!

  6. Write a method removeAfter() that takes a linked-list Nodeas argument and removes the node following the given one (and does nothing if theargument or the next field in the argument node is null).
  7. Write a method insertAfter() that takes two linked-list Nodearguments and inserts the second after the first on its list (and doesnothing if either argument is null).
  8. Write a method remove() that takes a linked list and a string keyas arguments and removes all of the nodes in the list that havekeyas its item field.
  9. Write a method max() that a reference to the first node in a linked list as argument and returns the value of the maximum key in the list.Assume that all keys are positive integers, and return 0 if the list is empty.
  10. Develop a recursive solution to the previous exercise.
  11. Write a recursive method to print the elements of a linked list in reverse order.Do not modify any of the links.Easy: Use quadratic time, constant extra space.Also easy: Use linear time, linear extra space.Not so easy: Develop a divide-and-conquer algorithm that useslinearithmic time and logarithmic extra space.
  12. Write a recursive method to randomly shuffle the elements of a linked list bymodifying the links.Easy: Use quadratic time, constant extra space.Not so easy: Develop a divide-and-conquer algorithm that useslinearithmic time and logarithmic extra space.

Creative Exercises

  1. DequeA double-ended queue or deque (pronounced deck) is a combination of astack and and a queue. It stores a parameterized collection of items and supports the following API:

    Deque API

    Write a data type Deque.java that implements the dequeAPI using a singly linked list.

  2. Random queue. Create an abstract data type RandomizedQueue.javathat supports the following operations:isEmpty(),insert(), random(),and removeRandom(), wherethe deletion operation deletes and returns a random object.Hint: maintain an array of objects. To delete an object,swap a random object (indexed 0 through N-1) with the last object(index N-1). Then, delete and return the last object.

    Randomized queue API
  3. Listing files.A Unix directory is a list of files and directories.ProgramDirectory.java takes the name of a directory as a command line parameter and prints outall of the files containedin that directory (and any subdirectories) in level-order. It usesa queue.
  4. Josephus problemProgram Josephus.java usesa queue to solve the Josephus problem.
  5. Delete ith element. Create an ADT that supports the following operations:isEmpty,insert, and remove(int i), wherethe deletion operation deletes and returns the ith least recentlyadded object on the queue. Do it with an array, then do it witha linked list.See Exercise XYZ for a more efficient implementation that usesa BST.
  6. Dynamic shrinking. With the array implementations of stack and queue, we doubled the sizeof the array when it wasn't big enough to store the next element.If we perform a number of doubling operations, and then delete alotof elements, we might end up with an array that is much bigger thannecessary. Implement the following strategy: whenever the array is1/4 full or less, shrink it to half the size.Explain why we don't shrink it to half the size when it is 1/2full or less.
  7. Ring buffer.A ring buffer or circular queue is a FIFO data structure of a fixed size N.It is useful for transferring data between asynchronous processesor storing log files.When the buffer is empty, the consumer waits until data is deposited;when the buffer is full, the producer waits to deposit data.A ring buffer has the following methods:isEmpty(), isFull(), enqueue(), and dequeue().Write an generic data typeRingBufferusing an array (with circular wrap-around for efficiency).
  8. Merging two sorted queues.Given two queues with strings in ascending order, move all of thestrings to a third queue so that the third queues ends up with the strings in ascending order.
  9. Mergesort.Given N strings, create N queues, each containing one of thestrings. Create a queue of the N queues. Then repeatedlyapply the sorted merging operation to the first two queues and reinsertthe merged queue at the end.Repeat until the queue of queues contains only one queue.
  10. Queue with two stacks.Show how to implement a queue using two stacks.Hint: If you push elements onto a stack and then pop them all,they appear in reverse order. If you repeat this process, they're nowback in order.
  11. Move-to-front. Read in a sequence of characters from standard input and maintain thecharacters in a linked list with no duplicates. When you read in a previouslyunseen character, insert it at the front of the list.When you read in a duplicate character, delete it from the list and re-insertit at the beginning.This move-to-front strategy is useful for caching and data compression(Burrows-Wheeler) algorithms where items that have been recently accessed are morelikely to be re-accessed.
  12. Text editor buffer.Implement an ADT for a buffer in a text editor. It should support the followingoperations:
    • insert(c): insert character c at cursor
    • delete(): delete and return the character at the cursor
    • left(): move the cursor one position to the left
    • right(): move the cursor one position to the right
    • get(i): return the ith character in the buffer

    Hint: use two stacks.

  13. Topological sort.You have to sequence the order of N jobs on a processor. Some of the jobsmust complete before others can begin. Specifically, you are given a list of order pairs of jobs (i, j). Find a sequence of the jobs such that for each pair (i, j) job i is scheduled before job j.Use the following algorithm.... For each node, maintain a list ofoutgoing arcs using a queue. Also, maintain the indegree of each node.Finally, maintain a queue of all nodes whose indegree is 0.Repeatedly delete a node with zero indegree, and delete all of its outgoing arcs.Write a program TopologicalSorter.javato accomplish this.

    Alternate application: prerequisites for graduating in your major. Musttake COS 126 and COS 217 before COS 341, etc. Can you graduate?

  14. PERT / CPM.Modify the previous exercise to handle weights (i, j, w) means job i is scheduled at least w units of time before job j.
  15. Set of integers.Create a data type that represents a set of integers (no duplicates)between 0 and N-1. Support add(i), exists(i), remove(i), size(),intersect, difference, symmetricDifference, union, isSubset, isSuperSet, and isDisjointFrom.
  16. Indexing a book.Write a program that reads in a text file from standard input and compilesan alphabetical index of which words appear on which lines, as inthe following input. Ignore case and punctuation. Similar to FrequencyCount,but for each word maintain a list of location on which it appears.

    Reverse a linked list.Write a function that takes the first Node in a linkedlist, reverse it, and returns the first Node in the resultinglinked list.

    Solution. To accomplish this, we maintainreferences to three consecutive nodes in the linked list,reverse, first, and second.At each iteration we extract the nodefirstfrom the original linked list and insert it at the beginningof the reversed list.We maintain the invariant thatfirst is the first node of what's leftof the original list, second is the second node of what's left of theoriginal list, andreverseis the first node of the resulting reversed list.

    Reverse a linked list
    public static Node reverse(Node list) {   Node first   = list;   Node reverse = null;   while (first != null) {      Node second = first.next;      first.next  = reverse;      reverse     = first;      first       = second;   }   return reverse;}

    When writing code involving linked lists, we must always be careful to properly handle the exceptional cases (when the linked list is empty,when the list has only one or two nodes) and the boundary cases(dealing with the first or last items). This is usually the trickiest part,as opposed to handling the normal cases.

    Recursive solution.Assuming the linked list has N elements, we recursivelyreverse the last N-1 elements, then carefullyappend the first element to the end.

    public Node reverse(Node first) {    if (first == null || first.next == null) return first;    Node second = first.next;    Node rest = reverse(second);    second.next = first;    first.next  = null;    return rest;}

Web Exercises

  1. Quote.Develop a data type Quote.javathat implements the following API:
    public class Quote-------------------------------------------------------------------------------             Quote()                      create an empty quote        void addWord(String w)            add w to the end of the quote         int count()                      return number of words in quote      String getWord(int i)               return the ith word starting at 1        void insertWord(int i, String w)  add w after the ith word      String toString()                   return the entire quote as a String
    To do so, define a nested class Card that holdsone word of the quote and a link to the next word:
    private class Card {    private String word;    private Card next;    public Card(String word) {        this.word = word;        this.next = null;    }}
  2. Circular quote.Repeated the previous exercise, but write a programCircularQuote.javathat use a circular linked list.
  3. Write a recursive function that takes as input a queue, and rearranges it so that it is in reverse order.Hint:dequeue() the first element, recursively reverse the queue,and the enqueue the first element.
  4. Add a method Item[] multiPop(int k) toStack that pops k elements from the stack and returnsthem as an array of objects.
  5. Add a method Item[] toArray() to Queuethat returns all N elements on the queue as an array of length N.
  6. What does the following code fragment do?
    IntQueue q = new IntQueue();q.enqueue(0);q.enqueue(1);for (int i = 0; i < 10; i++) {    int a = q.dequeue();    int b = q.dequeue();    q.enqueue(b);    q.enqueue(a + b);    System.out.println(a);}

    Fibonacci

  7. What data type would you choose to implement an "Undo" feature ina word processor?
  8. Suppose you have a single array of size N and want to implementtwo stacks so that you won't get overflow until the total numberof elements on both stacks is N+1. How would you proceed?
  9. Suppose that you implemented push in the linked list implementation ofStackList with the following code.What is the mistake?
    public void push(Object value) {   Node second = first;   Node first = new Node();   first.value = value;   first.next = second;}

    Answer: By redeclaring first, you are create a new local variable namedfirst, which is different fromthe instance variable named first.

  10. Copy a queue. Create a new constructor so that LinkedQueue r = new LinkedQueue(q)makesr reference a new and independent queue.Hint: delete all of the elements fromq and add toboth q and this.
  11. Copy a stack. Create a new constructor for the linked list implementation ofStack.java so thatStack t = new Stack(s)makes t reference a new and independent copy of the stacks. You should be able to push and pop froms or twithout influencing the other.

    Should it work if argument is null?Recursive solution: create a copy constructor for aNodeand use this to create the new stack.

    Node(Node x) {   item = x.item;   if (x.next != null) next = new Node(x.next);}public Stack(Stack s) { first = new Node(s.first); }

    Nonrecursive solution (untested):

    Node(Node x, Node next) { this.x = x; this.next = next; }public Stack(Stack s) {   if (s.first != null) {      first = new Node(s.first.value, s.first.next) {      for (Node x = first; x.next != null; x = x.next)         x.next = new Node(x.next.value, x.next.next);   }}
  12. Stack with one queue.Show how to implement a stack using one queue.Hint: to delete an item, get all of the elements on the queueone at a time, and put them at the end, except for the last onewhich you should delete and return.
  13. Listing files with a stack. Write a program that takes the name of a directory as a command line argument, and prints out all of the filescontained in this directory and any subdirectories.Also prints out the file size (in bytes) of each file. Use a stack instead of a queue.Repeat using recursion andname your programDirectoryR.java.ModifyDirectoryR.java so thatit prints out each subdirectory and its total size. The size ofa directory is equal to the sum of all of the files it containsor that its subdirectories contain.
  14. Stack + max.Create a data structure that efficiently supports the stackoperations (pop and push) and also return the maximum element. Assume the elements are integers or reals so that you cancompare them.Hint: use two stacks, one to store all of the elementsand a second stack to store the maximums.
  15. Tag systems.Write a program that reads in a binary string from the command lineand applies the following (00, 1101) tag-system: if the first bit is 0, deletethe first three bits and append 00; if the first bit is 1, delete the firstthree bits and append 1101. Repeat as long as the string has at least 3 bits.Try to determine whether the following inputs will halt or go into an infiniteloop: 10010, 100100100100100100. Use a queue.
  16. Reverse.Write a method to read in an arbitrary number of strings from standard inputand print them in reverse order.
    public static void main(String[] args) {   Stack<String> stack = new Stack<String>();   while (!StdIn.isEmpty()) {      String s = StdIn.readString();      stack.push(s);   }   while (!stack.isEmpty()) {      String s = stack.pop();      StdOut.println(s);   }}
  17. Add a method int size() to DoublingStack.java andStack.javathat returns the number of elements on the stack.
  18. Add a method reverse() to Queuethat reverses the order of the elements on the queue.
  19. Add a method copy() to ArrayStackOfStrings.java

原创粉丝点击