views:

437

answers:

7

I was having a nice look at my STL options today. Then I thought of something.

It seems a linked list (a std::list) is only of limited use. Namely, it only really seems useful if

  • The sequential order of elements in my container matters, and

  • I need to erase or insert elements in the middle.

That is, if I just want a lot of data and don't care about its order, I'm better off using an std::set (a balanced tree) if I want O(log n) lookup or a std::unordered_map (a hash map) if I want O(1) expected lookup or a std::vector (a contiguous array) for better locality of reference, or a std::deque (a double-ended queue) if I need to insert in the front AND back.

OTOH, if the order does matter, I am better off using a std::vector for better locality of reference and less overhead or a std::deque if a lot of resizing needs to occur.

So, am I missing something? Or is a linked list just not that great? With the exception of middle insertion/erasure, why on earth would someone want to use one?

+2  A: 

Order is important very often. When it is, linked lists are good. If you have a growing collection, you have the option of linked lists, array lists (vector in C++) and double-ended queues (deque). Use linked lists if you want to modify (add, delete) elements anywhere in the list often. Use array lists if fast retrieval is important. Use double-ended queues if you want to add stuff to both ends of the data structure and fast retrieval is important. For the deque vs vector question: use vector unless inserting/removing things from the beginning is important, in which case use deque. See here for an in-depth look at this.

If order isn't important, linked lists aren't normally ideal.

David Johnstone
+11  A: 

Any sort of insertion/deletion is O(1). Even std::vector isn't O(1) for appends, it approaches O(1) because most of the time it is, but sometimes you are going to have to grow that array.

It's also very good at handling bulk insertion, deletion. If you have 1 million records and want to append 1 million records from another list (concat) it's O(1). Every other structure (assuming stadard/naive implementations) are at least O(n) (where n is the number of elements added).

Talljoe
Due to growing of the array, appending to a vector is O(n). It is amortised O(1), however.
Stephan202
To just avoid confusion: concatenation is only O(1) if you're willing to have no independence between the 1 million records you're appending and the new, combined list. The O(1) operation — pointing the "Next" pointer of the last original list element at the first element of the 1 million records — results in a situation where any alteration made later to the original 1 million items is also reflected in the combined list. To make the new list independent requires copying all the elements, an O(n) operation.
Brandon Craig Rhodes
@Talljoe, I assume the second paragraph in your answer is about the splice() method?
bk1e
@bk1e, I was speaking in general terms, but yes, I think that is what splice does.You can optimize a vector (not necessarily std:vector) to account for splicing as amortized O(1) (treating it the same as a single insert) by using disjoint arrays at the cost of more expensive retrieves. It stops becoming a true vector at that point and is a completely different structure altogether.
Talljoe
Linked list Insertion/deletion is only O(1) if you have a pointer to the node to delete or insert before or after. If that node isn't the head or tail, then you had to search for it somehow, and that search normally should be included in the insertion/deletion time.
Robert Lamb
A: 

This question reminds me of this infamous one. Read it for the parallels as to why such simple data structures are important.

Shane Arney
A: 

Linked List is a fundamental data structure. Other data structures, like hash maps, may use linked lists internally.

Two different algorithms may have O(1) time complexity, for a look up, but that doesn't mean they have the same performance. For example the first one may be 10 or 100 times faster than the second.

Whenever you need to store, iterate and do something with a bunch of data, the normal (and fast) data stucture for that task is the Linked List. More complex data structures are for special cases, ie Set is suitable when you don't want repeated values.

Nick D
A: 

std::list has the following properties:

  • Sequence
  • Front Seuqence
  • Back Seuqence
  • Forward Container
  • Reverse Container

Of these properties std::vector does not have (Back Seuqence)
While std::set does not support any sequence properties or (Reverse Container)

So what does this mean?
Will a back sequence supports O(1) for rend() and rbegin() etc

For full information see:
http://stackoverflow.com/questions/181693/what-are-the-complexity-guarantees-of-the-standard-containers

Martin York
A: 

Linked lists are immutable and recursive datastructures whereas arrays are mutable and imperative (=change-based). In functional programming, there are usually no arrays - You don't change elements but transform lists into new lists. While linked lists don't even need additional memory here, this isn't possible efficiently with arrays.

You can easily build or decompose lists without having to change any value.

double [] = []
double (head:rest) = (2 * head):(double rest)

In C++, which is an imperative language, you won't use lists that often. One example could be a list of spaceships in a game from which you can easily remove all spaceships that have been destroyed since the previous frame.

Dario
The difference between lists in functional languages and containers in C++ has approximately nothing in common with the difference within C++ between std::list and other containers.
Steve Jessop
The question is tagged "language-agnostic" and when he asks "Or is a linked list just not that great? With the exception of middle insertion/erasure, why on earth would someone want to use one?" this is my answer!
Dario
OK, then: the difference between lists in functional languages and containers in imperative languages has approximately nothing in common with the difference between doubly-linked lists in imperative languages and other containers in imperative languages. You've explained why one might use a functional language, but I don't think that's relevant to the question, which is about specific data structures. Both doubly-linked lists and arrays are available in imperative languages, so linked lists aren't "functional". That's my guess as to why the downvote, anyway.
Steve Jessop
I agree with onebyone.livejournal.com. This is not what the OP asked for.
I disagree: this answer is relevant. You can use functional, non-side-effecting style programming in a lot of imperative languages. The advantage of a linked list in those cases is that it can simulate an immutable list efficiently. For example, I came across this thread looking for such a linked list implementation in JavaScript.
Jesse Hallett
+1  A: 

std::list is notable for its splice() method, which allows you to move one more more elements from one list to another in constant time, without copying or allocating any elements or list nodes.

bk1e
esp. useful when using a `std::list` as a work queue between threads.