Date post: | 18-Jan-2016 |
Category: |
Documents |
Upload: | percival-stevens |
View: | 217 times |
Download: | 0 times |
CS 261 Fall 2009
Dynamic Array Introduction
(aka Vector, ArrayList)
Arrays, Pro and Con
• Simple Arrays have nice feature that they are randomly accessible - can quickly get to any element
• Dark side - size must be fixed when created.
• Often you don’t know much much space you need until you are done
Dynamic Array (Vector, ArrayList)
• Dynamic Array (Java Vector, ArrayList, same thing, different API) get around this by encapsulating a partially filled array.
• Hide memory management details behind a simple API
• Is still randomly accessible, but now it grows as necessary
Partially Filled Array
Size vs Capacity
• The size is the “logical” size - The number of elements. What the programmer thinks. Managed by an internal data value.
• The capacity is the size of the physical array. Number of elements it can hold.
Adding an element
• Adding an element to end is sometimes easy. Just increase the (logical) size, and put new value at end.
• But what happens when the size reaches the capacity?
• Must reallocate new data array - but this detail is hidden from user.
Reallocation and copy
Adding to Middle
• Adding an element to middle can also force reallocation (if the current size is equal to capacity)
• But will ALWAYS require elements to moved up to make space
• Is therefore O(n) worst case
Picture of Adding to Middle
Must use a loop to make space for new value. Careful! Loop from top down
Removing an Element
• Removing an Element will also require “sliding over” to delete the value
• Therefore is O(n) worst case
Picture of Remove Element
Remove also requires loop. This time should it be from top or bottom?
Element Types
• How to make a general purpose container class?
• We define element type as symbolic preprocessor constant. Default double.
• Requires recompiling source for new element types. Not elegant, but workable.
Interface file#ifndef DyArray_H#define DyArray_H
# ifndef EleType# define EleType double# endif
# ifndef LT# define LT(a, b) (a < b)# endif
# ifndef EQ# define EQ(a, b) (a == b)# endif
Interface, continuedstruct dyArray { EleType * data; int size; int capacity;};
/* prototypes */void dyArrayInit (struct dyArray *da, int initCap);void dyArrayFree (struct dyArray *da);void dyArrayAdd (struct dyArray *da, EleType d);EleType dyArrayGet (struct dyArray *da, int index);EleType dyarraySet (struct dyArray *da, int index, EleType
newValue);int dyArraySize (struct dyArray *da);
dyArrayInit - initialization
void dyArrayInit (struct dyArray * da, int initCap){ assert (initCap >= 0); da->capacity = initCap; da->size = 0; da->data = (double *) malloc(da->capacity * sizeof(EleType)); assert (da->data != 0);}
dyArrayFree - clean up
Void dyArrayFree (struct dyArray * da)
{
free (da->data);
da->capacity = 0;
da->size = 0;
}
Size
int dyArraySize (struct dyArray * da)
{ return da->size; }
Even though one line, still a good idea to hide behind function call abstraction
Add a new Element
void dyArrayAdd (struct dyArray * da, EleType newValue)
{ if (da->size >= da->capacity) _dyArrayDoubleCapacity(da);
da->data[da->size] = newValue; da->size += 1; }
How to Double the Capacity
• Think it through. What are the steps? Need to make a new data buffer array that is twice the size. Need to copy elements from old buffer to new one.
• Next thought - can we REUSE anything we have done already. We have an init function that creates new buffer. What can we wrap around it?
Outline of operations
• Double the capacity, can do this by
• Calling Init the new buffer size
• Copy elements from old buffer
• (opps, need to keep reference to old buffer)
Double the Capacity
void _dyArrayDoubleCapacity (struct dyArray * da) {
EleType * oldbuffer = da->data; int oldsize = da->size; int i; dyArrayInit (da, 2 * da->capacity); for (i = 0; i < oldsize; i++) da->data[i] = oldbuffer[i]; da->size = oldsize; free(oldbuffer);}
Whats with the underscore?
• Underscore is just another character for the C compiler
• Common convention, function names beginning with underscore should not be called directly by user, but are “internal” functions.
Reuse, or not
• Notice how we reused the init function. • Should always look for places where
you can reuse code• But… Should we have reused add
instead of just placing elements into the new buffer
• Judgement call on very simple operations. (arguments pro and con)
What is O( ) for double
• What is the O( ) of doubleCapacity?
• What is therefore the worst case O( ) of add?
• But do you expect it to be this bad most of the time?
• Subtle issue, will leave for next class
get and Set, easy operationsEleType dyArrayGet (struct dyArray *da,
int index) {
assert(index >= 0 && index < da->size);
return da->data[index];
}
Set is similar
Adding to middle, a bit harder
Think about the steps
• Make sure index is legal
• Make sure you have enough space
• Slide the remaining elements over
• Move into the new location
• Increase size by 1
Do the easy parts first
Void dyArrayInsert(struct dyArray * da, int index, EleType newValue) {
assert(index >= 0 and index <= da->size); if (da->size >= da->capacity) _dyArrayDoubleCapacity(da); /* slide things over - need to do this */ da->data[index] = newValue; da->size++}
Then do the slide part
• Well, I need to leave SOMETHING for you to do
• Hint. It’s a loop.
If we have insert, do we really need add?
• How are add and insert similar?
• How are they different?
• What are some arguments for keeping add, for getting rid of it?
• In any design, there are tradeoffs. You should think about the alternatives.
Remove - lots of variations
• There are lots of ways to think about remove.
• Remove last element. Remove first element. Remove element at given index. Remove a given value.
• Think reuse - what is the most general function?
Steps in doing removeAt
• Check that the index is legal
• Remove by sliding values over
• Decrement the size
Implement removeAt
Void dyArray removeAt(struct dyArray *da, int index) {
assert(index >= 0 && index < da->size);
/* remove by sliding things over */
/* you get to do this */
da->size--;
}
Now we have a nice general purpose tool
/* prototypes */void dyArrayInit (struct dyArray *da, int initCap);void dyArrayFree (struct dyArray *da);void dyArrayAdd (struct dyArray *da, EleType d);EleType dyArrayGet (struct dyArray *da, int index);EleType dyArraySet (struct dyArray *da, int index, EleType
newValue);int dyArraySize (struct dyArray *da);Void dyArrayInsert (struct dyArray *da, int index, EleType
newValue);Void dyArrayRemoveAt (struct dyArray *da, int index);
Now lets look at making some ADTS
• How would we make a Bag? (Add, test, remove by value, size)
• How would we make a Stack (add to top, remove from top, test if empty)
• We are building these abstractions on top of the implementations we have done already
Stack API
Void dyArrayPush (struct dyArray * stk, EleType newValue)
{ … /* all one or two liners */ }EleType dyArrayTop (struct dyArray * stk) { assert(! dyArrayIsEmpty(stk)); … }Void dyArrayPop (struct dyArray * stk) { … }Int dyArrayIsEmpty (struct dyArray * stk) { … }
Design Decisions
• This is not the only way to do things - lots of possible design decisions
• Could have used the dyArray API directly, instead of making the stack wrappers
• Could have hidden the dynamic array inside a different structure.
• Think about some of the implications of these choices.
Implementing a Bag
• What about the Bag API?
• Bag operations - add, test, remove, size
• Some of these are already done (which?)
• Some require more than one line
How to do a test
Int dyArrayContains (struct dyArray *da, EleType testValue)
{
/* loop over all values, checking each */
/* if found, return 1 (true) */
/* if you get to end, return 0 (false) */
} /* you get to write this */
Remove
• This version of remove takes a value, and removes the FIRST instance of something equal to this
• What are some of the alternative versions of remove you could think of
• Can we reduce the problem to the remove we have written already?
Remove
void remove (struct dyArray *da, EleType testValue)
{ int i;
for (i = 0; i < da->size; i++) {
if (EQ(testValue, da->data[i])) {
dyArrayRemoveAt(da, i); return; }
}
}
Your chance
• At home finish implementation of all the operations we have left unspecific. Next move on to remove to complete the Bag. Then do the stack.
• Also your implementation will be used in programming assignment 2.