p art v - esminfo.prenhall.comesminfo.prenhall.com/computing/liang/closerlook/pdf/liangch17... · p...

29
P ART V DATA STRUCTURES AND COLLECTIONS FRAMEWORK The design and implementation of efficient data structures is an important subject in computer science. Data structures such as lists, stacks, queues, sets, maps, and binary trees have many applications in compiler construction, computer operating systems, and file management. Java provides the data structures for lists, stacks, sets, and maps in the collections framework. This part of the book introduces data structure concepts and implementation techniques in Chapter 17, “Object-Oriented Data Structures,” and introduces how to use the classes and interfaces in the Java collections framework in Chapter 18, “Java Collections Framework.” These two chapters are designed to be independent. You can skip Chapter 17 if you are only interested in learning how to use the predefined data structures in the Java collections framework. Chapter 17 Object-Oriented Data Structures Chapter 18 Java Collections Framework

Upload: haliem

Post on 26-Jun-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

PART VDATA STRUCTURESAND COLLECTIONS FRAMEWORKThe design and implementation of efficient data structures is an important subject incomputer science. Data structures such as lists, stacks, queues, sets, maps, and binarytrees have many applications in compiler construction, computer operating systems,and file management. Java provides the data structures for lists, stacks, sets, and mapsin the collections framework. This part of the book introduces data structure conceptsand implementation techniques in Chapter 17, “Object-Oriented Data Structures,”and introduces how to use the classes and interfaces in the Java collections frameworkin Chapter 18, “Java Collections Framework.” These two chapters are designed to beindependent. You can skip Chapter 17 if you are only interested in learning how to usethe predefined data structures in the Java collections framework.

Chapter 17Object-Oriented Data Structures

Chapter 18Java Collections Framework

Prerequisites for Part V

Chapter 9 Abstract Classes and Interfaces

Chapter 17 Object-Oriented Data Structures Chapter 18 Java Collections Framework

17chapter

Objectives✦ To describe what a data structure is (§17.1).

✦ To explain the limitations of arrays (§17.1).

✦ To implement a dynamic list using an array (§17.2.1).

✦ To implement a dynamic list using a linked structure (§17.2.2 Optional).

✦ To implement a stack using an array list (§17.3).

✦ To implement a queue using a linked list (§17.3).

✦ To implement a binary search tree (§17.4 Optional).

OBJECT-ORIENTEDDATA STRUCTURES

620 Part V Data Structures and Collections Framework

array limitation

liststackqueue

binary tree

17.1 IntroductionA data structure is a collection of data organized in some fashion. A data structure not only storesdata, but also supports the operations for manipulating data in the structure. For example, anarray is a data structure that holds a collection of data in sequential order. You can find the size ofthe array, and store, retrieve, and modify data in the array. Arrays are simple and easy to use, butthey have two limitations: (1) once an array is created, its size cannot be altered; (2) an array doesnot provide adequate support for insertion and deletion operations. This chapter introduces dy-namic data structures that grow and shrink at runtime.

Four classic dynamic data structures are introduced in this chapter: lists, stacks, queues, andbinary trees. A list is a collection of data stored sequentially. It supports insertion and deletionanywhere in the list. A stack can be perceived as a special type of list where insertions and dele-tions take place only at one end, referred to as the top of the stack. A queue represents a waitinglist, where insertions take place at the back (also referred to as the tail ) of the queue, and deletionstake place from the front (also referred to as the head) of the queue. A binary tree is a data struc-ture that supports searching, sorting, inserting, and deleting data efficiently.

In object-oriented thinking, a data structure is an object that stores other objects, referred to asdata or elements. Some people refer to data structures as container objects or collection objects. Todefine a data structure is essentially to declare a class. The class for a data structure should use datafields to store data and provide methods to support such operations as insertion and deletion. Tocreate a data structure is therefore to create an instance from the class. You can then apply themethods on the instance to manipulate the data structure, such as inserting an element into thedata structure or deleting an element from the data structure.

This chapter introduces the design and implementation of the classes for data structures: lists,stacks, queues, and binary trees. The whole chapter is optional and can be skipped.

17.2 Lists A list is a popular data structure for storing data in sequential order. For example, a list of stu-dents, a list of available rooms, a list of cities, and a list of books can all be stored using lists. Theoperations listed below are typical of most lists:

✦ Retrieve an element from a list.

✦ Insert a new element to a list.

✦ Delete an element from a list.

✦ Find how many elements are in a list.

✦ Find whether an element is in a list.

✦ Find whether a list is empty.

There are two ways to implement a list. One is to use an array to store the elements. Arrays aredynamically created. If the capacity of the array is exceeded, create a new, larger array and copy allthe elements from the current array to the new array. The other approach is to use a linked struc-ture. A linked structure consists of nodes. Each node is dynamically created to hold an element.All the nodes are linked together to form a list. Thus you can declare two classes for lists. For con-venience, let’s name these two classes MyArrayList and MyLinkedList. These two classes havecommon operations but different data fields. The common operations can be generalized in aninterface or an abstract class. As discussed in Section 10.6.5, “Using Interfaces or Abstract Class-es,” a good strategy is to combine the virtues of interfaces and abstract classes by providing bothan interface and an abstract class in the design so that the user can use either of them, whicheveris convenient. Such an abstract class is known as a convenience class.

Let us name the interface MyList and the convenience class MyAbstractList. Figure 17.1 showsthe relationship of MyList, MyAbstractList, MyArrayList, and MyLinkedList. The methods in

collection object

Chapter 17 Object-Oriented Data Structures 621

MyList MyAbstractList

MyArrayList

MyLinkedList

FIGURE 17.1 MyList defines a common interface for MyAbstractList, MyArrayList, and MyLinkedList.

+add(o: Object): void+add(index: int, o: Object): void+clear(): void+contains(o: Object): boolean+get(index: int): Object+indexOf(o: Object): int+isEmpty(): boolean+lastIndexOf(o: Object): int+remove(o: Object): boolean+size(): int+remove(index: int): Object+set(index: int, o: Object): Object

Appends a new element o at the end of this list.Adds a new element o at the specified index in this list.Removes the elements from this list.Returns true if this list contains the element o.Returns the element from this list at the specified index.Returns the index of the first matching element in this list.Returns true if this list contains no elements.Returns the index of the last matching element in this list.Removes all the elements from this list.Returns the number of elements in this list.Removes the element at the specified index.Sets the element at the specified index.

#size: int

MyList

MyAbstractList

The size of the list.

#MyAbstractList()#MyAbstractList(objects: Object[])+add(o: Object): void+isEmpty(): boolean+size(): int

Creates a default list.Creates a list from an array of objects.Implements the add method.Implements the isEmpty method.Implements the size method.

FIGURE 17.2 List supports many methods for manipulating a list. MyAbstractList provides a partial implementation of theList interface.

MyList and the methods implemented in MyAbstractList are shown in Figure 17.2. Listing 17.1gives the source code for MyList.

LISTING 17.1 MyList.java (The Interface for Lists)

1 public interface MyList {2 /** Add a new element o at the end of this list */3 public void add(Object o);4 5 /** Add a new element o at the specified index in this list */6 public void add(int index, Object o);7 8 /** Clear the list */9 public void clear();10 11 /** Return true if this list contains the element o */12 public boolean contains(Object o);13 14 /** Return the element from this list at the specified index */15 public Object get(int index);16 17 /** Return the index of the first matching element in this list.18 * Return –1 if no match. */19 public int indexOf(Object o);20 21 /** Return true if this list contains no elements */22 public boolean isEmpty();

622 Part V Data Structures and Collections Framework

size

no-arg constructor

23 24 /** Return the index of the last matching element in this list25 * Return –1 if no match. */26 public int lastIndexOf(Object o);27 28 /** Remove the first occurrence of the element o from this list.29 * Shift any subsequent elements to the left.30 * Return true if the element is removed. */31 public boolean remove(Object o);32 33 /** Remove the element at the specified position in this list34 * Shift any subsequent elements to the left.35 * Return the element that was removed from the list. */36 public Object remove(int index);37 38 /** Replace the element at the specified position in this list39 * with the specified element and return the new set. */40 public Object set(int index, Object o);41 42 /** Return the number of elements in this list */43 public int size();44 }

MyAbstractList declares variable size to indicate the number of elements in the list. Themethods isEmpty and size can be implemented in the class in Listing 17.2.

LISTING 17.2 MyAbstractList.java (Partially Implements MyList)

1 public abstract class MyAbstractList implements MyList {2 protected int size; // The size of the list3 4 /** Create a default list */5 protected MyAbstractList() {6 }7 8 /** Create a list from an array of objects */9 protected MyAbstractList(Object[] objects) {10 for (int i = 0; i < objects.length; i++)11 this.add(objects[i]);12 }13 14 /** Add a new element o at the end of this list */15 public void add(Object o) {16 add(size, o);17 }18 19 /** Return true if this list contains no elements */20 public boolean isEmpty() {21 return size == 0;22 }23 24 /** Return the number of elements in this list */25 public int size() {26 return size;27 }28 }

The following sections give the implementation for MyArrayList and MyLinkedList, respectively.

17.2.1 Array Lists An array is a fixed-size data structure. Once an array is created, its size cannot be changed. Never-theless, you can still use arrays to implement dynamic data structures. The trick is to create a larg-er new array to replace the current array if the current array cannot hold new elements in the list.This section shows how to use arrays to implement MyArrayList.

Initially, an array, say data of Object[] type, is created with a default size. When inserting a newelement into the array, first make sure that there is enough room in the array. If not, create a new arraytwice as large as the current one. Copy the elements from the current array to the new array. The new

constructor

Chapter 17 Object-Oriented Data Structures 623

e0

e Insertion point

e1 . . . ei 1 ei ei 1 . . . ek 1 ek

0 1 . . . i i 1 . . . k 1 k

data.length–1

Before insertinge at insertion point i

e0

e inserted here

e1 . . . ei 1 e ei . . . ek 1 ek

0 1 . . . i i 1

ei 1

i 2 . . . k k 1

data.length–1

After insertinge at insertion point i,list size isincremented by 1

FIGURE 17.3 Inserting a new element to the array requires that all the elements after the insertionpoint be shifted one position to the right so that the new element can be inserted at the insertion point.

array now becomes the current array. Before inserting a new element at a specified index, shift all theelements after the index to the right and increase the list size by 1, as shown in Figure 17.3.

e0

Delete this element

e1 . . . ei 1 ei ei 1 . . . ek 1 ek

0 1 . . . i i 1 . . . k 1 k

data.length–1

Before deleting theelement at index i

e0 e1 . . . ei 1 ek l ek

0 1 . . .

. . .ei 1

i . . . k 2 k 1

data.length–1

After deleting theelement, list size isdecremented by 1

FIGURE 17.4 Deleting an element from the array requires that all the elements after the deletionpoint be shifted one position to the left.

-data: Object[]

MyArrayList

MyAbstractList

+MyArrayList()+MyArrayList(objects: Object[])–ensureCapacity(): void

Creates a default array list.Creates an array list from an array of objects.Doubles the current capacity if needed.

FIGURE 17.5 MyArrayList implements a list using an array.

3 NOTEThe data array is of type Object[]. Each cell in the array actually stores thereference of an object.

To remove an element at a specified index, shift all the elements after the index to the left byone position and decrease the list size by 1, as shown in Figure 17.4.

MyArrayList uses an array to implement MyAbstractList, as shown in Figure 17.5. Its imple-mentation is given in Listing 17.3.

624 Part V Data Structures and Collections Framework

LISTING 17.3 MyArrayList.java (Implementing List Using Array)

1 public class MyArrayList extends MyAbstractList {2 public static final int INITIAL_CAPACITY = 16;3 private Object[] data = new Object[INITIAL_CAPACITY];4 5 /** Create a default list */6 public MyArrayList() {7 }8 9 /** Create a list from an array of objects */10 public MyArrayList(Object[] objects) {11 data = objects;12 size = objects.length;13 }14 15 /** Add a new element o at the specified index in this list */16 public void add(int index, Object o) {17 ensureCapacity();18 19 // Move the elements to the right after the specified index20 for (int i = size – 1; i >= index; i––)21 data[i + 1] = data[i];22 23 // Insert new element to data[index]24 data[index] = o;25 26 // Increase size by 127 size++;28 }29 30 /** Create a new larger array, double the current size */31 private void ensureCapacity() {32 if (size >= data.length) {33 Object[] newData = new Object[data.length * 2];34 System.arraycopy(data, 0, newData, 0, data.length);35 data = newData;36 }37 }38 39 /** Clear the list */40 public void clear() {41 data = new Object[INITIAL_CAPACITY];42 }43 44 /** Return true if this list contains the element o */45 public boolean contains(Object o) {46 for (int i = 0; i < size; i++)47 if (o.equals(data[i])) return true;48 49 return false;50 }51 52 /** Return the element from this list at the specified index */53 public Object get(int index) {54 return data[index];55 }56 57 /** Return the index of the first matching element in this list.58 * Return –1 if no match. */59 public int indexOf(Object o) {60 for (int i = 0; i < size; i++)61 if (o.equals(data[i])) return i;62 63 return –1;64 }

initial capacityarray

double capacity

Chapter 17 Object-Oriented Data Structures 625

65 66 /** Return the index of the last matching element in this list67 * Return –1 if no match. */68 public int lastIndexOf(Object o) {69 for (int i = size – 1; i >= 0; i––)70 if (o.equals(data[i])) return i;71 72 return –1;73 }74 75 /** Remove the first occurrence of the element o from this list.76 * Shift any subsequent elements to the left.77 * Return true if the element is removed. */78 public boolean remove(Object o) {79 for (int i = 0; i < size; i++)80 if (o.equals(data[i])) {81 remove(i);82 return true;83 }84 85 return false;86 }87 88 /** Remove the element at the specified position in this list89 * Shift any subsequent elements to the left.90 * Return the element that was removed from the list. */91 public Object remove(int index) {92 Object o = data[index];93 94 // Shift data to the left95 for (int j = index; j < size – 1; j++)96 data[j] = data[j + 1];97 98 // Decrement size99 size––;100 101 return o;102 }103 104 /** Replace the element at the specified position in this list105 * with the specified element. */106 public Object set(int index, Object o) {107 data[index] = o;108 return o;109 }110 111 /** Override toString() to return elements in the list */112 public String toString() {113 StringBuffer result = new StringBuffer(“[“);114 115 for (int i = 0; i < size; i++) {116 result.append(data[i]);117 if (i < size – 1) result.append(“, “);118 }119 120 return result.toString() + “]”;121 }122 }

The constant INITIAL_CAPACITY (Line 2) is used to create an initial array data of type Object(Line 3).

The add(int index, Object o) method (Lines 16–28) adds element o at the specified indexin the array. This method first invokes ensureCapacity() (Line 17), which ensures that there is aspace in the array for the new element. It then shifts all the elements after the index one position

626 Part V Data Structures and Collections Framework

to the right before inserting the element (Lines 20–21). After the element is added, size is incre-mented by 1 (Line 27). Note that variable size is defined as protected in MyAbstractList, so itcan be accessed in MyArrayList.

The ensureCapacity() method (Lines 31–37) checks whether the array is full. If so, create anew array that doubles the current array size, copy the current array to the new array using theSystem.arraycopy method, and set the new array as the current array.

The clear() method (Lines 40–42) creates a brand-new array with initial capacity.The contains(Object o) method (Lines 45–50) checks whether element o is contained in the

array by comparing o with each element in the array using the equals method.The get(int index) method (Lines 53–55) simply returns data[index]. The implementa-

tion of this method is simple and efficient.The indexOf(Object o) method (Lines 59–64) compares element o with the elements in the

array starting from the first one. If a match is found, the index of the element is returned; other-wise, it returns –1.

The lastIndexOf(Object o) method (Lines 68–73) compares element o with the elements inthe array starting from the last one. If a match is found, the index of the element is returned; oth-erwise, it returns –1.

The remove(Object o) method (Lines 78–86) compares element o with the elements in thearray. If a match is found, the first matching element in the list is deleted.

The remove(int index) method (Lines 91–102) shifts all the elements before the index oneposition to the left and decrements size by 1.

The set(int index, Object o) method (Lines 106–109) simply assigns o to data[index] toreplace the element at the specified index with element o

The toString() method (Lines 112–121) overrides the toString method in the Object classto return a string representing all the elements in the list.

EXAMPLE 17.1 USING ARRAY LISTS

ProblemWrite a program that creates a list using MyArrayList. It then uses the add method to addstrings to the list and the remove method to remove strings from the list.

SolutionListing 17.4 gives the solution to the problem. A sample run of the program is shown inFigure 17.6.

FIGURE 17.6 The program uses a list to store and process strings.

EXAMPLE 17.1 (CONTINUED)

LISTING 17.4 TestList.java (Using Lists)

1 public class TestList {2 public static void main(String[] args) {3 // Create a list4 MyList list = new MyArrayList();5 6 // Add elements to the list7 list.add(“Tom”); // Add it to the list8 System.out.println(“(1) “ + list);9 10 list.add(0, “John”); // Add it to the beginning of the list11 System.out.println(“(2) “ + list);12 13 list.add(“George”); // Add it to the end of the list14 System.out.println(“(3) “ + list);15 16 list.add(“Michael”); // Add it to the end of the list17 System.out.println(“(4) “ + list);18 19 list.add(2, “Michelle”); // Add it to the list at index 220 System.out.println(“(5) “ + list);21 22 list.add(5, “Samantha”); // Add it to the list at index 523 System.out.println(“(6) “ + list);24 25 list.add(0, “Daniel”); // Same as list.addFirst(“Daniel”)26 System.out.println(“(7) “ + list);27 28 // Remove elements from the list29 list.remove(“Daniel”); // Same as list.remove(0) in this case30 System.out.println(“(8) “ + list);31 32 list.remove(2); // Remove the element at index 233 System.out.println(“(9) “ + list);34 35 list.remove(list.size() – 1); // Remove the last element36 System.out.println(“(10) “ + list);37 }38 }

ReviewMyArrayList is implemented using arrays. Although an array is a fixed-size data structure,MyArrayList is a dynamic data structure. The user can create an instance of MyArrayList tostore any number of elements. The internal array in MyArrayList is encapsulated. The usermanipulates the list through the public methods in MyArrayList.

The list can hold any objects. A primitive data type value cannot be directly stored in alist. However, you can create an object for a primitive data type value using the correspon-ding wrapper class. For example, to store number 10, use the following method:

list.add(new Integer(10));

Chapter 17 Object-Oriented Data Structures 627

17.2.2 Linked Lists (Optional)Since MyArrayList is implemented using an array, the methods get(int index) and set(intindex, Object o) for accessing and modifying an element through an index and the add(Object o)for adding an element at the end of the list are efficient. However, the methods add(int index,

628 Part V Data Structures and Collections Framework

node nnode2node1

. . .element

next

element

next

element

next

first last

FIGURE 17.7 A linked list consists of any number of nodes chained together.

The variable first refers to the first node in the list, and the variable last refers to the lastnode in the list. If the list is empty, both are null. For example, you can create three nodes tostore three circle objects (radius 1, 2, and 3) in a list:

Node first, last;

// Create a node to store the first circle objectfirst = new Node(new Circle(1));last = first;

// Create a node to store the second circle objectlast.next = new Node(new Circle(2));last = last.next;

// Create a node to store the third circle objectlast.next = new Node(new Circle(3));last = last.next;

The process of creating a new linked list and adding three nodes is shown in Figure 17.8.

new Circle(1) new Circle(2)

next

new Circle(1)

next: null

next: null

new Circle(2)

next

first

new Circle(1)

nextfirst

first

first: null

last: null

last

last

last

After the third node is inserted

After the second node is inserted

After the first node is inserted

Before nodes are inserted

new Circle(3)

next:null

FIGURE 17.8 Three nodes are added to a new linked list.

Object o) and remove(int index) are inefficient because they require shifting a potentially largenumber of elements. You can use a linked structure to implement a list to improve efficiency foradding and removing an element anywhere in a list.

A linked list consists of nodes, as shown in Figure 17.7. Each node contains an element, andeach node is linked to its next neighbor. Thus a node can be defined as a class, as follows:

class Node {Object element;Node next;

public Node(Object o) {element = o;

}}

MyLinkedList uses a linked structure to implement a dynamic list. It extends MyAbstractList.In addition, it provides the methods addFirst, addLast, removeFirst, removeLast, getFirst,and getLast, as shown in Figure 17.9. Its implementation is given in Listing 17.5.

Chapter 17 Object-Oriented Data Structures 629

addFirstcreate node

addLast

-first: Node-last: Node

1

m

Link

1

Creates a default linked list.Creates a linked list from an array of objects.Adds the object to the head of the list.Adds the object to the tail of the list.Returns the first object in the list.Returns the last object in the list.Removes the first object from the list.Removes the last object from the list.

MyAbstractList

MyLinkedList

+LinkedList()+LinkedList(objects: Object[])+addFirst(o: Object): void+addLast(o: Object): void+getFirst(): Object+getLast(): Object+removeFirst(): Object+removeLast(): Object

Node

element: Objectnext: Node

FIGURE 17.9 MyLinkedList implements a list using a linked list of nodes.

LISTING 17.5 MyLinkedList.java (Implementing Lists Using Linked Structures)

1 public class MyLinkedList extends MyAbstractList {2 private Node first, last;3 4 /** Create a default list */5 public MyLinkedList() {6 }7 8 /** Create a list from an array of objects */9 public MyLinkedList(Object[] objects) {10 super(objects);11 }12 13 /** Return the first element in the list */14 public Object getFirst() {15 if (size == 0) return null;16 else return first.element;17 }18 19 /** Return the last element in the list */20 public Object getLast() {21 if (size == 0) return null;22 else return last.element;23 }24 25 /** Add an element to the beginning of the list */26 public void addFirst(Object o) {27 Node newNode = new Node(o);28 newNode.next = first;29 first = newNode;30 size++;31 32 if (last == null)33 last = first;34 }35 36 /** Add an element to the end of the list */37 public void addLast(Object o) {38 if (last == null) {39 first = last = new Node(o);40 }41 else {42 last.next = new Node(o);43 last = last.next;44 }

630 Part V Data Structures and Collections Framework

45 46 size++;47 }48 49 /** Adds a new element o at the specified index in this list50 * The index of the first element is 0 */51 public void add(int index, Object o) {52 if (index == 0) addFirst(o);53 else if (index >= size) addLast(o);54 else {55 Node current = first;56 for (int i = 1; i < index; i++)57 current = current.next;58 Node temp = current.next;59 current.next = new Node(o);60 (current.next).next = temp;61 size++;62 }63 }64 65 /** Remove the first node and66 * return the object that is contained in the removed node. */67 public Object removeFirst() {68 if (size == 0) return null;69 else {70 Node temp = first;71 first = first.next;72 size––;73 return temp.element;74 }75 }76 77 /** Remove the last node and78 * return the object that is contained in the removed node. */79 public Object removeLast() {80 // Implementation left as an exercise81 return null;82 }83 84 /** Removes the element at the specified position in this list85 * Returns the element that was removed from the list. */86 public Object remove(int index) {87 if ((index < 0) || (index >= size)) return null;88 else if (index == 0) return removeFirst();89 else if (index == size – 1) return removeLast();90 else {91 Node previous = first;92 93 for (int i = 1; i < index; i++) {94 previous = previous.next;95 }96 97 Node current = previous .next;98 previous.next = current.next;99 size––;100 return current.element;101 }102 }103 104 /** Override toString() to return elements in the list */105 public String toString() {106 StringBuffer result = new StringBuffer(“[“);107 108 Node current = first;109 for (int i = 0; i < size; i++) {110 result.append(current.element);

add

removeFirst

removeLast

remove

Chapter 17 Object-Oriented Data Structures 631

111 current = current.next;112 if (current != null)113 result.append(“, “); // Seperate two elements with a comma114 else115 result.append(“]”); // Insert the closing ] in the string116 }117 118 return result.toString();119 }120 121 /** Clear the list */122 public void clear() {123 first = last = null;124 }125 126 /** Return true if this list contains the element o */127 public boolean contains(Object o) {128 // Implementation left as an exercise129 return true;130 }131 132 /** Return the element from this list at the specified index */133 public Object get(int index) {134 // Implementation left as an exercise135 return null;136 }137 138 /** Returns the index of the first matching element in this list.139 * Returns –1 if no match. */140 public int indexOf(Object o) {141 // Implementation left as an exercise142 return 0;143 }144 145 /** Returns the index of the last matching element in this list146 * Returns –1 if no match. */147 public int lastIndexOf(Object o) {148 // Implementation left as an exercise149 return 0;150 }151 152 /** Remove the first node that contains the specified element153 * Return true if the element is removed154 * Return false if no element is removed155 */156 public boolean remove(Object o) {157 // Implementation left as an exercise158 // return true;159 }160 161 /** Replace the element at the specified position in this list162 * with the specified element. */163 public Object set(int index, Object o) {164 // Implementation left as an exercise165 return null;166 }167 168 private static class Node {169 Object element;170 Node next;171 172 public Node(Object o) {173 element = o;174 }175 }176 }

632 Part V Data Structures and Collections Framework

The variables first and last (Line 2) refer to the first and last nodes in the list, respectively.The getFirst() and getLast() methods (Lines 14–23) return the first and last elements in thelist, respectively.

Since variable size is defined as protected in MyAbstractList, it can be accessed inMyLinkedList. When a new element is added to the list, size is incremented by 1, and when an el-ement is removed from the list, size is decremented by 1. The addFirst(Object o) method (Lines26–34) creates a new node to store the element and insert the node to the beginning of the list. Afterthe insertion, first should refer to this new element node (Line 29), as shown in Figure 17.10.

e0

next

ei

next

first

o

next

. . . ei+1

next

ek

next

last

ei+1

next

. . .

ek

next

last

. . .

A new nodeto be insertedhere

(a) Before a new node is inserted.

o

next

ei

next

first

e0

next

. . .

(b) After a new node is inserted.

New node inserted here

FIGURE 17.10 A new element o is added to the beginning of the list.

e0

next

ei

next

first

o

next

. . . ei+1

next

ek

next

last

ei+1

next

. . .

ek

next

last

. . .

A new nodeto be insertedhere

(a) Before a new node is inserted.

o

next

ei

next

first

e0

next

. . .

(b) After a new node is inserted.

New node inserted here

FIGURE 17.11 A new element o is added at the end of the list.

The addLast(Object o) method (Lines 37– 47) creates a node to hold element o and insertsthe node at the end of the list. After the insertion, last should refer to this new element node(Line 43), as shown in Figure 17.11.

Chapter 17 Object-Oriented Data Structures 633

The add(int index, Object o) method (Lines 51– 63) adds an element o to the list at thespecified index. Consider three cases: (1) if index is 0, invoke addFirst(o) to insert the elementat the beginning of the list; (2) if index is greater than or equal to size, invoke addLast(o) to in-sert the element at the end of the list; (3) create a new node to store the new element and locatewhere to insert it. As shown in Figure 17.12, the new node is to be inserted between the nodescurrent and temp. The method assigns the new node to current.next and assigns temp to thenew node’s next.

e0

next

ei

next

first

o

next

. . . ei+1

next

ek

next

last

. . .

A new nodeto be insertedhere

(a) Before a new node is inserted.

(b) After a new node is inserted.

ei+1

next

ek

next

temp

. . .o

next

ei

next

first lastcurrent

tempcurrent

ei

next

. . .

New node inserted here

FIGURE 17.12 A new element is inserted in the middle of the list.

The removeFirst() method (Lines 67–75) removes the first node from the list by pointingfirst to the second node, as shown in Figure 17.13. The removeLast() method (Lines 79–82)removes the last node from the list. Afterwards, last should refer to the former second-last node.

e0

next

e1

next

first

ei+1

next

ei

next

last

. . . ek

next

. . .

Delete this node

(a) Before the node is deleted.

e1

next

first

ei+1

next

ei

next

last

. . . ek

next

. . .

(b) After the first node is deleted.

FIGURE 17.13 The first node is deleted from the list.

634 Part V Data Structures and Collections Framework

element

next

first

element

next

last

element

next

previous

element

next

current

element

next

current.next

. . . . . .

(a) Before the node is deleted.

Node to be deleted

element

next

first

element

next

last

element

next

previous

element

next

current.next

. . . . . .

(b) After the node is deleted.

FIGURE 17.14 A node is deleted from the list.

inheritance

composition

The implementation of methods removeLast(), clear(), contains(Objecto), get(intindex), indexOf(Object o), lastIndexOf(Object o), remove(Object o), and set(int index,Object o) is omitted and left as an exercise.

To test MyLinkedList, simply replace MyArrayList on Line 7 by MyLinkedList in TestList inExample 17.1. The output should be the same as shown in Figure 17.6.

17.3 Stacks and Queues A stack can be viewed as a special type of list whose elements are accessed, inserted, and deletedonly from the end (top), of the stack. A queue represents a waiting list. A queue can be viewed asa special type of list whose elements are inserted into the end (tail) of the queue, and are accessedand deleted from the beginning (head) of the queue.

Since the insertion and deletion operations on a stack are made only at the end of the stack, itis more efficient to implement a stack with an array list than with a linked list. Since deletions aremade at the beginning of the list, it is more efficient to implement a queue using a linked list thanan array list. This section implements a stack class using an array list and a queue using a linked list.

There are two ways to design stack and queue classes:

✦ Using inheritance: You can declare a stack class by extending the array list class, and aqueue class by extending the linked list class.

✦ Using composition: You can declare an array list as a data field in the stack class, and alinked list as a data field in the queue class.

The remove(int index) method (Lines 86–102) finds the node at the specified index andthen removes it. Consider four cases: (1) if index is beyond the range of the list (i.e., index <0 ||index >= size), return null; (2) if index is 0, invoke removeFirst() to remove the firstnode; (3) if index is size – 1, invoke removeLast() to remove the last node; (4) locate thenode at the specified index. Let current denote this node and previous denote the node be-fore this node, as shown in Figure 17.14. Assign current.next to previous.next to eliminatethe current node.

Chapter 17 Object-Oriented Data Structures 635

LISTING 17.6 MyStack.java (Implementing Stack)

1 public class MyStack {2 private MyArrayList list = new MyArrayList();3 4 public boolean isEmpty() {5 return list.isEmpty();6 }7 8 public int getSize() {9 return list.size();10 }11 12 public Object peek() {13 return list.get(getSize() – 1);14 }15 16 public Object pop() {17 Object o = list.get(getSize() – 1);18 list.remove(getSize() – 1);19 return o;20 }21 22 public Object push(Object o) {23 list.add(o);24 return o;25 }26 27 public int search(Object o) {28 return list.lastIndexOf(o);29 }30 31 public String toString() {32 return “stack: “ + list.toString();33 }34 }

An array list is created to store the elements in a stack (Line 2). The isEmpty() method (Lines4–6) is the same as list.isEmpty(). The getSize() method (Lines 8–10) is the same aslist.size(). The peek() method (Lines 12–14) looks at the element at the top of the stack withoutremoving it. The pop() method (Lines 16–20) removes the top element from the stack and returns it.

array list

-list: MyArrayList

+isEmpty(): boolean+getSize(): int+peek(): Object+pop(): Object+push(o: Object): Object+search(o: Object): int

Returns true if this stack is empty.Returns the number of elements in this stack.Returns the top element in this stack.Returns and removes the top element in this stack.Adds a new element to the top of this stack.Returns the position of the first element in the stack from the top that matches the specified element.

MyStack

FIGURE 17.15 MyStack uses an array list to provide a last-in/first-out data structure.

Both designs are fine, but using composition is better because it enables you to declare a com-pletely new stack class and queue class without inheriting unnecessary and inappropriate methodsfrom the array list and linked list. The stack class named MyStack is shown in Figure 17.15. Itsimplementation is given in Listing 17.6.

636 Part V Data Structures and Collections Framework

-list: MyLinkedList

+enqueue(element: Object): void+dequeue(): Object+getSize(): int

MyQueue

Adds an element to this queue.Removes an element from this queue.Returns the number of elements from this queue.

FIGURE 17.16 MyQueue uses a linked list to provide a first-in/first-out data structure.

LISTING 17.7 MyQueue.java (Implementing Queue)

1 public class MyQueue {2 private MyLinkedList list = new MyLinkedList();3 4 public void enqueue(Object o) {5 list.addLast(o);6 }7 8 public Object dequeue() {9 return list.removeFirst();10 }11 12 public int getSize() {13 return list.size();14 }15 16 public String toString() {17 return “Queue: “ + list.toString();18 }19 }

A linked list is created to store the elements in a queue (Line 2). The enqueue(Object o)method (Lines 4–6) adds element o into the tail of the queue. The dequeue() method (Lines8–10) removes an element from the head of the queue and returns the removed element. ThegetSize() method (Lines 12–14) returns the number of elements in the queue.

linked list

EXAMPLE 17.2 USING STACKS AND QUEUES

ProblemWrite a program that creates a stack using MyStack and a queue using MyQueue. It then usesthe push (enqueue) method to add strings to the stack (queue) and the pop (dequeue)method to remove strings from the stack (queue).

SolutionListing 17.8 gives the solution to the problem. A sample run of the program is shown inFigure 17.17.

The push(Object element) method (Lines 22–24) adds the specified element to the stack. Thesearch(Object element) method checks whether the specified element is in the stack.

The queue class named MyQueue is shown in Figure 17.16. Its implementation is given inListing 17.7.

Chapter 17 Object-Oriented Data Structures 637

EXAMPLE 17.2 (CONTINUED)

LISTING 17.8 TestStackQueue.java (Using Stack and Queue)

1 public class TestStackQueue {2 public static void main(String[] args) {3 // Create a stack4 MyStack stack = new MyStack();5 6 // Add elements to the stack7 stack.push(“Tom”); // Push it to the stack8 System.out.println(“(1) “ + stack);9 10 stack.push(“John”); // Push it to the stack11 System.out.println(“(2) “ + stack);12 13 stack.push(“George”); // Push it to the stack14 stack.push(“Michael”); // Push it to the stack15 System.out.println(“(3) “ + stack);16 17 // Remove elements from the stack18 System.out.println(“(4) “ + stack.pop());19 System.out.println(“(5) “ + stack.pop());20 System.out.println(“(6) “ + stack);21 22 // Create a queue23 MyQueue queue = new MyQueue();24 25 // Add elements to the queue26 queue.enqueue(“Tom”); // Add it to the queue27 System.out.println(“(7) “ + queue);28 29 queue.enqueue(“John”); // Add it to the queue30 System.out.println(“(8) “ + queue);31 32 queue.enqueue(“George”); // Add it to the queue33 queue.enqueue(“Michael”); // Add it to the queue34 System.out.println(“(9) “ + queue);35 36 // Remove elements from the queue37 System.out.println(“(10) “ + queue.dequeue());38 System.out.println(“(11) “ + queue.dequeue());39 System.out.println(“(12) “ + queue);40 }41 }

FIGURE 17.17 The program uses a stack and a queue to store and process strings.

638 Part V Data Structures and Collections Framework

EXAMPLE 17.2 (CONTINUED)

ReviewFor a stack, the push(o) method adds an element to the top of the stack, and the pop()method removes the top element from the stack and returns the removed element.

For a queue, the enqueue(o) method adds an element to the tail of the queue, and thedequeue() method removes the element from the head of the queue.

17.4 Binary Trees (Optional)A list, stack, or queue is a linear structure that consists of a sequence of elements. A binary tree isa hierarchical structure. It is either empty or consists of an element, called the root, and two dis-tinct binary trees, called the left subtree and right subtree. Examples of binary trees are shown inFigure 17.18.

binary search tree

60

55 100

57 67 10745

G

F R

M TA

(b)(a)

FIGURE 17.18 Each node in a binary tree has zero, one, or two branches.

60

55

45 57 67 107

100

root

FIGURE 17.19 A binary tree can be represented using a set of linked nodes.

rootleft subtreeright subtree

leafbinary search tree

The root of a left (right) subtree of a node is called a left (right) child of the node. A node with-out children is called a leaf. A special type of binary tree called a binary search tree is often useful.A binary search tree (with no duplicate elements) has the property that for every node in the tree,the value of any node in its left subtree is less than the value of the node, and the value of anynode in its right subtree is greater than the value of the node. The binary trees in Figure 17.18 areall binary search trees. This section is concerned with binary search trees.

17.4.1 Representing Binary Trees A binary tree can be represented using a set of linked nodes. Each node contains a value and twolinks named left and right that reference the left child and right child, respectively, as shown inFigure 17.19.

Chapter 17 Object-Oriented Data Structures 639

A node can be defined as a class, as follows:

class TreeNode {Object element;TreeNode left;TreeNode right;

public TreeNode(Object o) {element = o;

}

}

The variable root refers to the root node of the tree. If the tree is empty, root is null. The fol-lowing code creates the first three nodes of the tree in Figure 17.19:

// Create the root nodeTreeNode root = new TreeNode(new Integer(60));

// Create the left child node root.left = new TreeNode(new Integer(55));

// Create the right child node root.right = new TreeNode(new Integer(100));

17.4.2 Inserting an Element into a Binary Search Tree If a binary tree is empty, create a root node with the new element. Otherwise, locate the parentnode for the new element node. If the new element is less than the parent element, the node for thenew element becomes the left child of the parent. If the new element is greater than the parent el-ement, the node for the new element becomes the right child of the parent. Here is the algorithm:

if (root == null) root = new TreeNode(element);

else {// Locate the parent nodecurrent = root;while (current != null) if (element value < the value in current.element) {parent = current;current = current.left;

}else if (element value > the value in current.element) {parent = current;current = current.right;

}else return false; // Duplicate node not inserted

// Create the new node and attach it to the parent nodeif (element < parent.element)parent.left = new TreeNode(elemenet);

elseparent.right = new TreeNode(elemenet);

return true; // Element inserted}

For example, to insert 101 into the tree in Figure 17.19, the parent is the node for 107. Thenew node for 101 becomes the left child of the parent. To insert 59 into the tree, the parent is thenode for 57. The new node for 59 becomes the right child of the parent. Both of these insertionsare shown in Figure 17.20.

17.4.3 Tree TraversalTree traversal is the process of visiting each node in the tree exactly once. There are several ways to tra-verse a tree. This section presents inorder, preorder, postorder, depth-first, and breadth-first traversals.

640 Part V Data Structures and Collections Framework

inorder

postorder

preorderdepth-first

breadth-first

With inorder traversal, the left subtree of the current node is visited first, then the currentnode, and finally the right subtree of the current node.

With postorder traversal, the left subtree of the current node is visited first, then the right sub-tree of the current node, and finally the current node itself.

With preorder traversal, the current node is visited first, then the left subtree of the currentnode, and finally the right subtree of the current node. Depth-first traversal is the same as preordertraversal.

With breadth-first traversal, the nodes are visited level by level. First the root is visited, then allthe children of the root from left to right, then the grandchildren of the root from left to right,and so on.

For example, in the tree in Figure 17.20, the inorder is 45 55 57 59 60 67 100 101 107. Thepostorder is 45 59 57 55 67 101 107 100 60. The preorder is 60 55 45 57 59 100 67 107 101.The breadth-first traversal is 60 55 100 45 57 67 107 59 101.

17.4.4 The Binary Tree ClassLet us define a binary tree class named BinaryTree with insert, inorder traversal, postorder tra-versal, and preorder traversal, as shown in Figure 17.21. Its implementation is given inListing 17.9.

60

55

45 57 67 107

100

59 101

root

FIGURE 17.20 Two new elements are inserted into the tree.

-root: TreeNode

+BinaryTree()+BinaryTree(objects: Object[])+insert(o: Object): boolean+inorder(): void+preorder(): void+postorder(): void

1

m

element: Objectleft: TreeNoderight: TreeNode

Link

1

Creates a default binary tree.Creates a binary tree from an array of objects.Adds an element to the binary tree.Prints the nodes in inorder traversal.Prints the nodes in preorder traversal.Prints the nodes in postorder traversal.

BinaryTreeTreeNode

FIGURE 17.21 BinaryTree implements a binary tree with operations insert, inorder, preorder, and postorder.

LISTING 17.9 BinaryTree.java (Implementing Binary Tree)

1 public class BinaryTree {2 private TreeNode root;3 4 /** Create a default binary tree */5 public BinaryTree() {6 }7

no-arg constructor

Chapter 17 Object-Oriented Data Structures 641

constructor

insert

inorder

postorder

8 /** Create a binary tree from an array of objects */9 public BinaryTree(Object[] objects) {10 for (int i = 0; i < objects.length; i++)11 insert(objects[i]);12 }1314 /** Insert element o into the binary tree15 * Return true if the element is inserted successfully */16 public boolean insert(Object o) {17 if (root == null)18 root = new TreeNode(o); // Create a new root19 else {20 // Locate the parent node21 TreeNode parent = null;22 TreeNode current = root;23 while (current != null)24 if (((Comparable)o).compareTo(current.element) < 0) {25 parent = current;26 current = current.left;27 }28 else if (((Comparable)o).compareTo(current.element) > 0) {29 parent = current;30 current = current.right;31 }32 else33 return false; // Duplicate node not inserted34 35 // Create the new node and attach it to the parent node36 if (((Comparable)o).compareTo(parent.element) < 0)37 parent.left = new TreeNode(o);38 else39 parent.right = new TreeNode(o);40 }41 42 return true; // Element inserted43 }44 45 /** Inorder traversal */46 public void inorder() {47 inorder(root);48 }49 50 /** Inorder traversal from a subtree */51 private void inorder(TreeNode root) {52 if (root == null) return;53 inorder(root.left);54 System.out.print(root.element + “ “);55 inorder(root.right);56 }57 58 /** Postorder traversal */59 public void postorder() {60 postorder(root);61 }62 63 /** Postorder traversal from a subtree */64 private void postorder(TreeNode root) {65 if (root == null) return;66 postorder(root.left);67 postorder(root.right);68 System.out.print(root.element + “ “);69 }70 71 /** Preorder traversal */72 public void preorder() {73 preorder(root);74 }75 76 /** Preorder traversal from a subtree */77 private void preorder(TreeNode root) {78 if (root == null) return;

preorder

642 Part V Data Structures and Collections Framework

79 System.out.print(root.element + “ “);80 preorder(root.left);81 preorder(root.right);82 }83 84 /** Inner class tree node */85 private static class TreeNode {86 Object element;87 TreeNode left;88 TreeNode right;89 90 public TreeNode(Object o) {91 element = o;92 }93 }94 }

The insert(Object o) method (Lines 16–43) creates a node for element o and inserts it intothe tree. If the tree is empty, the node becomes the root. Otherwise, the method finds an appro-priate parent for the node to maintain the order of the tree. If the element is already in the tree,the method returns false; otherwise it returns true.

The inorder() method (Lines 46–48) invokes inorder(root) to traverse the entire tree. Themethod inorder(TreeNode root) traverses the tree with the specified root. This is a recursivemethod. It recursively traverses the left subtree, then the root, and finally the right subtree. Thetraversal ends when the tree is empty.

The postorder() method (Lines 59–61) and the preorder() method (Lines 72–74) are im-plemented similarly using recursion.

EXAMPLE 17.3 USING BINARY TREES

ProblemWrite a program that creates a binary tree using BinaryTree. Add strings into the binarytree and traverse the tree in inorder, postorder, and preorder.

SolutionListing 17.10 gives the solution to the problem. A sample run of the program is shown inFigure 17.22.

LISTING 17.10 TestBinaryTree.java (Using BinaryTree)

1 public class TestBinaryTree {2 public static void main(String[] args) {3 BinaryTree tree = new BinaryTree();4 tree.insert(“George”);5 tree.insert(“Michael”);6 tree.insert(“Tom”);7 tree.insert(“Adam”);8 tree.insert(“Jones”);9 tree.insert(“Peter”);

FIGURE 17.22 The program creates a tree, inserts elements into it, and displays them ininorder, postorder, and preorder.

create treeinsert

tree node

Chapter 17 Object-Oriented Data Structures 643

inorder

postorder

EXAMPLE 17.3 (CONTINUED)10 tree.insert(“Daniel”);11 System.out.print(“Inorder: “);12 tree.inorder();13 System.out.print(“\nPostorder: “);14 tree.postorder();15 System.out.print(“\nPreorder: “);16 tree.preorder();17 }18 }

ReviewAfter all the elements are inserted, the tree should appear as shown in Figure 17.23.

If the elements are inserted in a different order (e.g., Daniel, Adam, Jones, Peter, Tom,Michael, George), the tree will look different. However, the inorder is the same as long asthe set of elements is the same.

preorder

KEY TERMS

binary search tree 638binary tree 638data structure 620dynamic data structure 620inorder traversal 640list 620

object-oriented data structure 620postorder traversal 640 preorder traversal 640 queue 620 stack 620 tree traversal 639

George

MichaelAdam

Daniel Jones Tom

Peter

root

FIGURE 17.23 A binary search tree is pictured here after Line 10 is executed.

CHAPTER SUMMARY

✦ You have learned the concept of object-oriented data structures and how to create lists,stacks, queues, and binary trees using object-oriented approach.

✦ To define a data structure is essentially to declare a class. The class for a data structureshould use data fields to store data and provide methods to support such operations as in-sertion and deletion.

✦ To create a data structure is to create an instance from the class. You can then apply themethods on the instance to manipulate the data structure, such as inserting an element tothe data structure or deleting an element from the data structure.

644 Part V Data Structures and Collections Framework

REVIEW QUESTIONS

Sections 17.1–17.2

17.1 What is a data structure? What is an object-oriented data structure?

17.2 What are the limitations of the array data type?

17.3 MyArrayList is implemented using an array, and an array is a fixed-size data structure.Why is MyArrayList considered as a dynamic data structure?

17.4 What are the benefits of defining both the MyList interface and the MyAbstractListclass? What is a convenience class?

17.5 What is wrong in the following code?

MyArrayList list = new MyArrayList();

list.add(100);

17.6 What is wrong if Lines 11–12 in MyArrayList.java

data = objects;size = objects.length;

are replaced by

super(objects);

17.7 If the number of elements in the program is fixed, what data structure should you use?If the number of elements in the program changes, what data structure should you use?

17.8 If you have to add or delete the elements anywhere in a list, should you use ArrayListor LinkedList?

Section 17.3 Stacks and Queues

17.9 You can use inheritance or composition to design the data structures for stacks andqueues. Discuss the pros and cons of these two approaches.

17.10 Which lines of the following code are wrong?

MyList list = new MyArrayList();list.add(“Tom”);list = new MyLinkedList();list.add(“Tom”);list = new MyStack();list.add(“Tom”);

Section 17.4 Binary Trees

17.11 If a set of the same elements is inserted into a binary tree in two different orders, will thetwo corresponding binary trees look the same? Will the inorder traversal be the same?Will the postorder traversal be the same? Will the preorder traversal be the same?

PROGRAMMING EXERCISES

Section 17.2 Lists

17.1 (Adding set operations in MyAbstractList) Add and implement the following methods inMyAbstractList:

/** Add the elements in otherList to this list.* Returns true if this list changed as a result of the call */

public boolean addAll(MyList otherList)

Chapter 17 Object-Oriented Data Structures 645

/** Remove all the elements in otherList from this list * Returns true if this list changed as a result of the call */

public boolean removeAll(MyList otherList)

/** Retain the elements in this list if they are also in otherList * Returns true if this list changed as a result of the call */

public boolean retainAll(MyList otherList)

Write a test program that creates two MyArrayLists, list1 and list2, with the initial val-ues {”Tom”, “George”, “Peter”, “Jean”, “Jane”} and {”Tom”, “George”, “Michael”,“Michelle”, “Daniel”}, then invokes list1.addAll(list2), list1.removeAll(list2),and list1.retainAll(list2), and displays the resulting new list1.

17.2* (Completing the implementation of MyLinkedList) The implementations of methodsremoveLast(), contains(Object o), get(int index), indexOf(Object o), lastIndex-Of(Object o), remove(Object o), and set(int index, Object o) are omitted in thetext. Implement these methods.

17.3* (Creating a two-way linked list) The MyLinkedList class in Listing 17.5 is a one-way di-rectional linked list that enables one-way traversal of the list. Modify the Node class toadd the new field name previous to refer to the previous node in the list, as follows:

public class TreeNode {Object element;TreeNode next;TreeNode previous;

public TreeNode(Object o) {element = o;

}}

Simplify the implementation of the add(Object element, int index) method and theremove(int index) and remove(Object element) to take advantage of the bi-direc-tional linked list.

Section 17.3 Stacks and Queues

17.4 (Implementing MyStack using inheritance) In Section 17.3, “Stacks and Queues,”MyStack is implemented using composition. Create a new stack class that extendsMyArrayList.

17.5 (Implementing MyQueue using inheritance) In Section 17.3, “Stacks and Queues,”MyQueue is implemented using composition. Create a new queue class that extendsMyLinkedList.

Section 17.4 Binary Trees

17.6* (Adding new methods in BinaryTree) Add the following new methods in BinaryTree.

/** Search element o in this binary tree */public boolean search(Object o)

/** Return the number of nodes in this binary tree */public int size()

/** Return the depth of this binary tree. Depth is the * number of the nodes in the longest path of the tree */

public int depth()

17.7** (Implementing inorder traversal using a stack) Implement the inorder method inBinaryTree using a stack instead of recursion.