Many STL containers have both ordered and unordered version, e.g. map and unondered_map, set and unordered_set. And on the other hand, some STL algorithms have to be fed in an ordered container to give the correct output. For instance, std::set_difference will not work correctly on unordered_set. So my question is, why isn't there a named requirement that specifies a container is always sorted, such that inputs to algorithms like set_difference are constraint to those sorted containers?
In a way, the STL concept contradicts the original idea of
object-oriented programming: The STL separates data and algorithms
rather than combining them. However, the reason for doing so is very
important. In principle, you can combine every kind of container with
every kind of algorithm, so the result is a very flexible but still
rather small framework.
-- from Nicolai M. Josuttis <<The C++ Standard Library A Tutorial and Reference Second Edition>>
There are three classes of STL containers :
sequence containers(array, vector, deque, forward_list, list)
associative containers(set, map, multiset, and multimap)
unordered associative containers(unordered_set, unordered_map, unordered_multiset, and unordered_multimap)
You can get sorted sequence containers by calling std::sort for array, vector, deque, sort() member function for forward_list and list.
Related
Hi i have used some stl algorithms on vectors like find_if,count_if, sort, push_back etc. Now i want to make a generic code for all container objects(vectors,lists,maps,sets). Do i need to modify my stl functions for them or are they all same?
For starters push_back is not an algorithm. It is a method of some sequential containers like std::vector or std::list. However std::forward_list does not have such a method.
Standard algoreithm std::sort requires that the underlaying container had random access iterators. Some sequential containers like std::list and std::forward_list have their own methods sort. The standard algorithm std::sort can not be applied to these containers.
On the other hand such associative containers like std;:set or std::map are already ordered containers.
As for standard algorithms std::find_if or std::count_if then they can be applied to all standard containers because they are based on input iterators.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Sort list using stl sort function
The C++ standard library gives strict linear sequence container, linear sequence container, associative container.
std::sort() is available for all kinds of containers. But why only it provides sort for list. std::list::sort()?
std::sort only works on random access containers. And the only non-random access container in the standard library that it makes sense to sort is std::list.
std::sort certainly doesn't work on associative containers as you seem to think. What sense would that make? Associative containers are accessed by the value of their key, not by position.
As noted by Mike, C++11 also has std::forward_list, which, by no accident, also has it's own sort function.
std::sort only works for random access iterators, but a std::list only provides biderectional iterators. Since it therefore cannot be used with std::sort, it needs its own implementation, which is also likely to be more optimized for a doubly linked list.
Likewise you cannot use std::map or std::set iterators with std::sort. But for these you don't need it anyway, as they are always sorted.
As a side note, there are also std::map::find and the like. These are indeed not required, as you can use all iterators with std::find. But the member function versions provide optimized algorithms for the individual containers, which are more efficient than std::find's linear complexity.
How to write generic operations on C++ STL containers? For example, Java has Collection interface, which every Java containers (except for maps) implements. I can do operations like add, remove, contains, and iterations regardless of whether the actual container is LinkedList, HashSet, ArrayBlockingQueue, etc. I find it very powerful.
C++ has iterators, but what about operations like add and remove?
vector has push_back, set has insert, queue has push. How to add something to C++ container in a generic way?
Iteration:
All standard containers have iterators which give ordered access to the elements of the containers. These can also be used in generic algorithms which work on any conforming iterator type.
Insertion:
All sequences and associative containers can have elements inserted into them by the expression c.insert(i, x) -- where c is a sequence or associative container, i is an iterator into c and x is a value that you want to add to c.
std::inserter and friends can be used to add elements to a sequence or associative container in a generic way.
Removal:
For any sequence or associative container the following code works:
while (true) {
X::iterator it(std::find(c.begin(), c.end(), elem));
if (it == c.end()) break;
c.erase(it);
}
Where X is the type of the container, c is a container object and elem is an object with the value that you want to remove from the container.
For sequences there is the erase-remove idiom, which looks like:
c.erase(std::remove(c.begin(), c.end(), elem), c.end());
For associative containers you can also do:
c.erase(k);
Where k is a key corresponding to the element that you want to erase.
A nice interface to all of this:
See Boost.Range.
Note -- these are compile time substitutable, whereas the java ones are run time substitutable. To allow run-time substitution it is necessary to use type erasure (that is -- make a templated sub-class that forwards the required interface to the container that it is instantiated with).
Look at the header <algorithm>. There are plenty of generic algorithms in there for finding, sorting, counting, copying, etc, that work on anything providing iterators with various specified characteristics.
C++ has std::inserter and friends to add elements to a container in a generic way. They are in the header file iterator.
There is a sort() method for lists in STL. Which is absurd, because I would be more inclined to sort an array/vector.
Why isn't sort() provided for vector? Is there some underlying philosophy behind the creation of the vector container or its usage, that sort is not provided for it?
As has already been said, the standard library provides a nonmember function template that can sort any range given a pair of random access iterators.
It would be entirely redundant to have a member function to sort a vector. The following would have the same meaning:
std::sort(v.begin(), v.end());
v.sort();
One of the first principles of the STL is that algorithms are not coupled to containers. How data is stored and how data is manipulated should be as loosely coupled as possible.
Iterators are used as the interface between containers (which store data) and algorithms (which operate on the data). In this way, you can write an algorithm once and it can operate on containers of various types, and if you write a new container, the existing generic algorithms can be used to manipulate its contents.
The reason that std::list provides its own sort function as a member function is that it is not a random accessible container; it only provides bidirectional iterators (since it is intended to represent a doubly linked list, this makes sense). The generic std::sort function requires random access iterators, so you cannot use it with a std::list. std::list provides its own sort function in order that it can be sorted.
In general, there are two cases in which a container should implement an algorithm:
If the generic algorithm cannot operate on the container, but there is a different, container-specific algorithm that can provide the same functionality, as is the case with std::list::sort.
If the container can provide a specific implementation of the algorithm that is more efficient than the generic algorithm, as is the case with std::map::find, which allows an element to be found in the map in logarithmic time (the generic std::find algorithm performs a linear search because it cannot assume the range is sorted).
There are already interesting elements of answer, but there is actually more to be said about the question: while the answer to "why doesn't std::vector has a sort member function?" is indeed "because the standard library provides member functions only when they offer more than generic algorithms", the real interesting question is "why does std::list have a sort member function?", and a few things haven't been explained yet: yes, std::sort only works with random-access iterators and std::list only provides bidirectional iterators, but even if std::sort worked with bidirectional iterators, std::list::sort would still offer more. And here is why:
First of all, std::list::sort is stable, while std::sort isn't. Of course there is still std::stable_sort, but it doesn't work with bidirectional iterators either.
std::list::sort generally implements a mergesort, but it knows that it is sorting a list and can relink nodes instead of copying things. A list-aware mergesort can sort the list in O(n log n) time with only O(log n) additional memory, while your typical mergesort (such as std::stable_sort) uses O(n) additional memory or has a O(n logĀ² n) complexity.
std::list::sort doesn't invalidate the iterators. If an iterator was pointing to a specific object in the list, it will still be pointing to the same object after the sort, even if its position in the list isn't the same than before the sort.
Last but not least, std::list::sort doesn't move or swap the objects around since it only relinks nodes. That means that it might be more performant when you need to sort objects that are expensive to move/swap around, but also that it can sort a list of objects that aren't even moveable, which is totally impossible for std::sort!
Basically, even if std::sort and std::stable_sort worked with bidirectional or forward iterators (and it would totally be possible, we know sorting algorithms that work with them), they still couldn't offer everything std::list::sort has to offer, and they couldn't relink nodes either since the standard library algorithms aren't allowed to modify the container, only the pointed values (relinking nodes counts as modifying the container). On the other hand, a dedicated std::vector::sort method wouldn't offer anything interesting, so the standard library doesn't provide one.
Note that everything that has been said about std::list::sort is also true for std::forward_list::sort.
A vector-specific sort would provide no advantage over std::sort from <algorithm>. However, std::list provides its own sort because it can use the special knowledge of how list is implemented to sort items by manipulating the links instead of copying objects.
You can easily sort a vector with:
sort(v.begin(), v.end());
UPDATE: (answer to the comment): Well, they have certainly provided it by default. The difference is that it's not a member function for vector. std::sort is a generic algorithm that's supposed to work for anything that provides iterators. However, it really expects a random access iterator to sort efficiently. std::list, being a linked list, cannot provide random access to its elements efficiently. That's why it provides its own specialized sort algorithm.
std::sort() in <algorithm> does sorting on containers with random access iterators like std::vector.
There is also std::stable_sort().
edit - why does std::list have its own sort() function versus std::vector?
std::list is different from both std::vector and std::deque (both random access iterable) in how it's implemented, so it contains its own sort algorithm that is specialized for its implementation.
Answering the question of "Why?"
A sorting algorithm for std::vector is the same as sorting a native array and is the same (probably) as sorting a custom vector class.
The STL was designed to separate containers and algorithms, and to have an efficient mechanism for applying an algorithm to data that has the right characteristics.
This lets you write a container that might have specific characteristics, and to get the algorithms free. Only where there is some special characteristic of the data that means the standard algorithm is unsuitable is a custom implementation supplied, as in the case of std::list.
I'm looking for a C++ container that will enjoy both map container and list container benefits.
map container advantages I would like to maintain:
O(log(n)) access
operator[] ease of use
sparse nature
list container advantages I would like to maintain:
having an order between the items
being able to traverse the list easily UPDATE: by a sorting order based on the key or value
A simple example application would be to hold a list of certain valid dates (business dates, holidays, some other set of important dates...), once given a specific date, you could find it immediately "map style" and then find the next valid date "list style".
std::map is already a sorted container where you can iterate over the contained items in order. It only provides O(log(n)) access, though.
std::tr1::unordered_map (or std::unordered_map in C++0x) has O(1) access but is unsorted.
Do you really need O(1) access? You have to use large datasets and do many lookups for O(log(n)) not being fast enough.
If O(log(n)) is enough, std::map provides everything you are asking for.
If you don't consider the sparse nature, you can take a look at the Boost Multi-Index library. For the sparse nature, you can take a look at the Boost Flyweight library, but I guess you'll have to join both approaches by yourself. Note that your requirements are often contradictory and hard to achieve. For instance, O(1) and order between the items is difficult to maintain efficiently.
Maps are generally implemented as trees and thus have logarithmic look up time, not O(1), but it sounds like you want a sorted associative container. Hash maps have O(1) best case, O(N) worst case, so perhaps that is what you mean, but they are not sorted, and I don't think they are part of the standard library yet.
In the C++ standard library, map, set, multimap, and multiset are sorted associative containers, but you have to give up the O(1) look up requirement.
According to Stroustrup, the [] operator for maps is O(log(n)). That is much better than the O(n) you'd get if you were to try such a thing with a list, but it is definitely not O(1). The only container that gives you that for the [] operator is vector.
That aside, you can already do all your listy stuff with maps. Iterators work fine on them. So if I were you, I'd stick with map.
having an order between the items
being able to traverse the list easily
Maps already do both. They are sorted, so you start at begin() and traverse until you hit end(). You can, of course, start at any map iterator; you may find map's find, lower_bound, and related methods helpful.
You can store data in a list and have a map to iterators of your list enabling you to find the actual list element itself. This kind of thing is something I often use for LRU containers, where I want a list because I need to move the accessed element to the end to make it the most recently accessed. You can use the splice function to do this, and since the 2003 standard it does not invalidate the iterator as long as you keep it in the same list.
How about this one: all dates are stored in std::list<Date>, but you look it up with helper structure stdext::hash_map<Date, std::list<Date>::iterator>. Once you have iterator for the list access to the next element is simple. In your STL implementation it could be std::tr1::unordered_map instead of stdext::hash_map, and there is boost::unordered_map as well.
You will never find a container that satisfies both O(log n) access and an ordered nature. The reason is that if a container is ordered then inherently it must support an arbitrary order. That's what an ordered nature means: you get to decide exactly where any element is positioned. So to find any element you have to guess where it is. It can be anywhere, because you can place it anywhere!
Note that an ordered sequence is not the same as a sorted sequence. A sorted nature means there is one particular ordering relation between any two elements. An ordered nature means there may be more than one ordering relation among the elements.