Download Data structures and algorithms and more Lecture notes Computer Science in PDF only on Docsity!
Abstract Data Types
and Data Structures
Computer Science S-
Harvard University
David G. Sullivan, Ph.D.
Unit 6, Part 1
Congrats on completing the first half!
• In the second half, we will study fundamental data structures.
• ways of imposing order on a collection of information
• sequences: lists, stacks, and queues
• trees
• hash tables
• graphs
• We will also:
• study algorithms related to these data structures
• learn how to compare data structures & algorithms
• Goals:
• learn to think more intelligently about programming problems
• acquire a set of useful tools and techniques
Sample Problem I: Finding Shortest Paths
- Given a set of routes between pairs of cities, determine the
shortest path from city A to city B.
BOSTON
PORTSMOUTH
PORTLAND
CONCORD
ALBANY WORCESTER
PROVIDENCE
NEW YORK
84
(^74 )
49
54
49
185
42
(^13444)
63
Sample Problem II: A Data "Dictionary"
- Given a large collection of data, how can we arrange it
so that we can efficiently:
- add a new item
- search for an existing item
- Some data structures provide better performance than others
for this application.
- More generally, we’ll learn how to characterize the efficiency
of different data structures and their associated algorithms.
Algorithm 2 for Finding a Phone Number
findNumber(person) {
min = the number of the first page
max = the number of the last page
while (min <= max) {
mid = (min + max) / 2 // page number of the middle page
if person is found on page mid {
return the person's number
} else if the person’s name comes earlier in the book {
max = mid – 1
} else {
min = mid + 1
return NOT_FOUND
- If there were 1,000 pages in the phonebook, how many pages
would this look at in the worst case?
- What if there were 1,000,000 pages?
Searching a Collection of Data
- The phonebook problem is one example of a common task:
searching for an item in a collection of data.
- another example: searching for a record in a database
- Algorithm 1 is known as sequential search.
- also called linear search
- Algorithm 2 is known as binary search.
- only works if the items in the data collection are sorted
Abstract Data Types
- An abstract data type (ADT) is a model of a data structure
that specifies:
- the characteristics of the collection of data
- the operations that can be performed on the collection
- It’s abstract because it doesn’t specify how the ADT will be
implemented.
- does not commit to any low-level details
- A given ADT can have multiple implementations.
A Simple ADT: A Bag
- A bag is just a container for a group of data items.
- The positions of the data items don’t matter (unlike a list).
- {3, 2, 10, 6} is equivalent to {2, 3, 6, 10}
- The items do not need to be unique (unlike a set).
- { 7 , 2, 10, 7 , 5} isn’t a set, but it is a bag
Implementing an ADT Using a Class
- To implement an ADT, we define a class:
public class ArrayBag implements Bag {
public boolean add(Object item) { … }
- When a class header includes an implements clause,
the class must define all of the methods in the interface.
- if the class doesn't define them, it won't compile
All Interface Methods Are Public
- Methods specified in an interface must be public,
so we don't use the keyword public in the definition:
public interface Bag { boolean add(Object item); boolean remove(Object item); boolean contains(Object item); int numItems(); Object grab(); Object[] toArray(); }
- However, when we actually implement the methods
in a class, we do need to use public:
public class ArrayBag implements Bag { … public boolean add(Object item) { …
One Possible Bag Implementation
- One way to store the items in the bag is to use an array:
public class ArrayBag implements Bag {
private ______________[] items;
- What type should the array be?
- This allows us to store any type of object in the items array,
thanks to the power of polymorphism:
ArrayBag bag = new ArrayBag();
bag.add("hello");
bag.add(new Rectangle(20, 30));
- How could we keep track of how many items are in a bag?
Another Example of Polymorphism
- An interface name can be used as the type of a variable:
Bag b;
- Variables with an interface type can refer to objects
of any class that implements the interface:
Bag b = new ArrayBag();
- Using the interface as the type allows us to write code
that works with any implementation of an ADT:
public void processBag(Bag b) { for (int i = 0; i < b.numItems(); i++) { … }
- the param can be an instance of any Bag implementation
- we must use method calls to access the object's internals,
because the fields are not part of the interface
Memory Management, Type II: Stack Storage
- Method parameters and local variables are stored in a region
of memory known as the stack.
- For each method call, a new stack frame is added to the top
of the stack.
public class Foo { public static int x(int i) { int j = i - 2; if (i >= 6) { return i; } return x(i + j); } public static void main(String[] args) { System.out.println(x(5)); } }
- When a method completes, its stack frame is removed.
args
return addr
i
j
return addr
i
j
x(8)
x(5)
Memory Management, Type III: Heap Storage
- Objects are stored in a memory region known as the heap.
- Memory on the heap is allocated using the new operator:
int[] values = new int[3];
ArrayBag b = new ArrayBag();
- new returns the memory address of the start of the object
on the heap.
- a reference!
- An object stays on the heap until there are no remaining
references to it.
- Unused objects are automatically reclaimed by a process
known as garbage collection.
- makes their memory available for other objects
Two Constructors for the ArrayBag Class
public class ArrayBag implements Bag {
private Object[] items;
private int numItems;
public static final int DEFAULT_MAX_SIZE = 50;
public ArrayBag() {
this.items = new Object[DEFAULT_MAX_SIZE];
this.numItems = 0;
public ArrayBag(int maxSize) {
- As we've seen before, we can have multiple constructors.
- the parameters must differ in some way
- The first one is useful for small bags.
- creates an array with room for 50 items.
- The second one allows the client to specify the max # of items.
Two Constructors for the ArrayBag Class
public class ArrayBag implements Bag {
private Object[] items;
private int numItems;
public static final int DEFAULT_MAX_SIZE = 50;
public ArrayBag() {
this.items = new Object[DEFAULT_MAX_SIZE];
this.numItems = 0;
public ArrayBag(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException(
"maxSize must be > 0");
this.items = new Object[maxSize];
this.numItems = 0;
- If the user inputs an invalid maxSize, we throw an exception.
Adding Items
- We fill the array from left to right. Here's an empty bag:
- After adding the first item:
- After adding the second item:
items numItems 0
null null null null
items numItems 1
"hello, world"
null null null
items numItems 2
"hello, world" "howdy"
null null
Adding Items (cont.)
- After adding the third item:
- After adding the fourth item:
- At this point, the ArrayBag is full!
- it's non-trivial to "grow" an array, so we don't!
- additional items cannot be added until one is removed
items numItems 3
null
items numItems 4
"hello, world" "howdy" "bye" "see ya!"
"hello, world" "howdy" "bye"
A Method for Adding an Item to a Bag
public class ArrayBag implements Bag { private Object[] items; private int numItems; ... public boolean add(Object item) { if (item == null) { throw new IllegalArgumentException("no nulls"); } else if (this.numItems == this.items.length) { return false; // no more room! } else { this.items[this.numItems] = item; this.numItems++; return true; // success! } } ... } (^) items
numItems 4
"hello, world" "howdy" "bye" "see ya!"
- takes an object of any type!
- returns a boolean to indicate if the operation succeeded
A Method for Adding an Item to a Bag
public class ArrayBag implements Bag { private Object[] items; private int numItems; ... public boolean add(Object item) { if (item == null) { throw new IllegalArgumentException("no nulls"); } else if (this.numItems == this.items.length) { return false; // no more room! } else { this.items[this.numItems] = item; this.numItems++; return true; // success! } } ... }
- Initially, this.numItems is 0, so the first item goes in position 0.
- We increase this.numItems because we now have 1 more item.
- and so the next item added will go in the correct position!
- takes an object of any type!
- returns a boolean to indicate if the operation succeeded
public static void main(String[] args) {
ArrayBag b1 = new ArrayBag(2);
ArrayBag b2 = new ArrayBag(4);
- The method modifies the items array and numItems.
- note that the array holds a copy of the reference to the item,
not a copy of the item itself.
Example: Adding an Item (cont.)
public static void main(String[] args) { String message = "hello, world"; ArrayBag b = new ArrayBag(4); b.add(message); … }
args …
message
b
stack items numItems
null null 1
this null
"hello, world"
item
heap
public boolean add(Object item) { … else { this.items[this.numItems] = item; this.numItems++; return true; } …
public static void main(String[] args) {
ArrayBag b1 = new ArrayBag(2);
ArrayBag b2 = new ArrayBag(4);
- After the method call returns, add's stack frame is removed
from the stack.
Example: Adding an Item (cont.)
public static void main(String[] args) { String message = "hello, world"; ArrayBag b = new ArrayBag(4); b.add(message); … }
args …
message
b
stack items numItems
null null 1
null
"hello, world"
heap
public boolean add(Object item) { … else { this.items[this.numItems] = item; this.numItems++; return true; } …
Extra Practice: Determining if a Bag Contains an Item
- Let’s write the ArrayBag contains() method together.
- should return true if an object equal to item is found,
and false otherwise.
_________________ contains(_____________ item) {
items numItems (^3)
"hello, world"
null null null null …
"oh my!" "what's in the bag?"
A Method That Takes a Bag as a Parameter
public boolean containsAll(Bag otherBag) { if (otherBag == null || otherBag.numItems() == 0) { return false; } Object[] otherItems = otherBag.toArray(); for (int i = 0; i < otherItems.length; i++) { if (! this.contains(otherItems[i])) { return false; } } return true; }
- We use Bag instead of ArrayBag as the type of the parameter.
- allows this method to be part of the Bag interface
- allows us to pass in any object that implements Bag
- We must use methods in the interface to manipulate otherBag.
- we can't use the fields, because they're not in the interface
A Type Mismatch
- Here are the headers of two ArrayBag methods:
public boolean add(Object item) public Object grab()
- Polymorphism allows us to pass String objects into add():
ArrayBag stringBag = new ArrayBag(); stringBag.add("hello"); stringBag.add("world");
- However, this will not work:
String str = stringBag.grab(); // compiler error
- the return type of grab() is Object
- Object isn’t a subclass of String, so polymorphism doesn't help!
- Instead, we need to use a type cast :
String str = (String)stringBag.grab();
- this cast doesn't actually change the value being assigned
- it just reassures the compiler that the assignment is okay