views:

412

answers:

5

Accordingly to the Wikipedia article on dynamic arrays, inserting/deleting at the end of the array is O(1) while inserting/deleting from the middle is O(n). Why exactly is that?

Also - if I have a dynamic array with 5 elements and I insert a new element at position 6 the operation is O(n) whereas if I use the function to append to the end of the array it is O(1). Isn't this the same operation assuming the array doesn't need to resize in either case? Or does appending to the dynamic array really insert the new element somewhere besides position 6?

Thanks!

EDIT: I think my main confusion is the difference between inserting at the end of the array and inserting at a specific position that is equivalent to the end of the array.

I suppose a pointer to the memory address of the end of the array is kept handy and that is why the append operation is quick. Conversely, if I specify a precise position (even if it is the end of the array) it won't know that inserting at that position is tantamount to using the aforementioned memory address so it has to traverse the entire array, eh?

+8  A: 

To insert at the end of an array, you simply have to put the item there.

To insert into the middle of an array, you need to move the items after that point up by one.

To delete from the end of an array, you just drop its count by one.

To delete from the middle you have to do that and shift the other items down.

It's the shifting that turns it into O(n).

paxdiablo
+1  A: 

It's pretty simple:

Inserting in the middle involves moving each later element over by 1. To insert at the end, if there is additional space reserved, the item is just stored there, but if not new space is allocated. Thus this operation is done in amortized constant time.

rlbond
+7  A: 

The order of magnitude would depend entirely on what sort of data structure the "dynamic array" actually is ("dynamic array" isn't a strictly defined data structure, it's more of a desired result achieved by employing a particular data structure). The example that you give would be that reflect by a dynamic array achieved by employing a linked list. Adding to the end could be O(1) if the list structure kept a pointer to the final element. Inserting (regardless of the index) would require traversing the linked list, meaning one operation per node up until the desired index.

Adam Robinson
This makes sense. Unfortunately on the platform I'm referring to in my example (UniData) I don't really know how the dynamic array is implemented under the covers...but from what you're saying I think it is a linked list. Dunno why they chose that over an array.
vg1890
Many languages use a linked-list structure for their "dynamic arrays". Many also use actual native arrays that are declared with buffer arrays. Once the buffers fill up, they either chain a new array (creating a sort of array/linked-list hybrid) or allocate a brand new array with the proper new buffer size, then copy all of the existing data and destroy the old array. This can lead to quite a bit of memory fragmentation, though.
Adam Robinson
A: 

Actually, it's not Big-O but Big-Theta.

Colin Burnett
No, it's Big-O. Inserting into a position could take constant time, if the position happens to located at the end. It is not bounded below by n.
Andrei Krotkov
A: 

Adding to Adam Robinson's excellent summary: This isn't just theory. I've seen any number of situations in which a dynamic array was constructed by repeatedly appending to the end of the array. This works out to o(N**2) performance, because the array repeatedly needs to be re-allocated, forcing each of its members to be copied into the new array structure. The re-allocations may happen on only 1/10 of the append operations, but that's bad enough, and is still o(N**2) performance-wise.

In STL, this performance penalty can be avoided by calling vector::reserve(N) before writing into the vector; but that step is often overlooked.

Dan Breslau
I suppose that's why it's typical to double the size of the array every time you re-allocate memory.
vg1890
It's a typical practice for application writers. But don't *assume* that the class library will do this; many common ones grow by constant increments instead. 10 is the number I remember from MFC.
Dan Breslau