0

I see that many functions require the beginning and ending positions of an array/vector. I have two doubts, one in arrays and the second in vectors:

  1. This code
#include<iostream>
using namespace std;

int main(){

int arr[4] = {5, 3, 4, 7};
sort(begin(arr), end(arr));
for(int& x : arr) cout << x << " ";
return 0;
}

is fine as .begin() and .end() return the pointers to the first and last elements of the array respectively (btw please correct me if I am wrong anywhere).

But please explain how this code works:

#include<iostream>
using namespace std;

int main(){
int arr[4] = {5, 3, 4, 7};
sort(arr, arr + 4);
for(int& x : arr) cout << x << " ";
return 0;
}


  1. I understand if you want to sort (or perform any other function on) the whole vector, you can use .begin() and .end().

But what if you want to sort only a portion of the vector? Like the vector between left position L and right position R. How would you do that by using .begin and .end only (without slicing the vector)?

Thanks a lot!

gabrupro
  • 39
  • 4
  • Have a look to [std::begin](https://en.cppreference.com/w/cpp/iterator/begin) – Vuwox Feb 04 '21 at 14:51
  • The notion is quite simply that you wouldn't use `.end()` for the case of a partial vector sort. You also don't have to start at `.begin()`. `.end()` is **not** the last element. Your C-array code example could be written better using `` or `std::array`. – sweenish Feb 04 '21 at 14:56
  • Does this answer your question? [What are Iterators, C++?](https://stackoverflow.com/questions/2305014/what-are-iterators-c) – dtell Feb 04 '21 at 15:09
  • `int arr[4]` **has** no `.begin()`, `.end()`, or any other members. So the 1st bit of code you posted can't possibly be "fine". I guess you transcribed wrongly. Please paste actual code. – underscore_d Feb 04 '21 at 15:39

4 Answers4

1

In your first example you should have written.

sort(begin(arr), end(arr));

because int[] is a builtin-type (from C) and does not have any member-function. There exists overloads of std::begin() and std::end() suitable for T[] as shown above.

In C, using just the name of an array is an expression that decays to the beginning of this array. For example, in:

int arr[4] = {5, 3, 4, 7};
int *p = arr;

p is a pointer initialised with the address of the first element in the array. It is equivalent to

int *p = &(arr[0]);

Moreover, in C, there exists something called pointer-arithmetic. This means that you can increment, decrement, compute the difference... with pointers. Here, p+4 means the pointer which relates to the fourth element after the one pointed-to by p; in this specific example, it is just after the end of your array. So, back to your second example, arr is equivalent to begin(arr) and arr+4 is equivalent to end(arr).

We should not know any details about the exact nature of the iterators returned by std::begin() and std::end(); the only thing we know is that they behave as metaphors of pointers, in order to tell where an element is, to move to the next element... But when the storage for these elements is a contiguous portion of memory (like T[], std::array<T>, std::vector<T>...), internaly they are nothing more than pointers (we should not know... but we know ;^) Moreover, in this specific contiguous storage case, a plain-true-real pointer is the perfect metaphor for a pointer... That's why std::sort() that was designed with iterators in mind also works with simple pointers as arr and arr+4.

You might be puzzled by the fact that the end position points-to a non-existing element (right after the last element in the array). This is a convention for many algorithms: we specify a half-open interval, the first bound is included, the second bound is excluded. So the algorithm will never access this one-step-too-far element; it will stop just before reaching it. This is consistent with the classical for() loop: imagine you want five iterations, you will write for(int i=0; i<5; ++i) to specify 0 and 5 as bounds, but you will actually use the values 0, 1, 2, 3, 4 but NOT 5.

When it comes to sorting just a portion of the array, you can use arithmetic on pointers or on iterators.

std::sort(std::begin(arr)+L, std::end(arr)-R);
std::sort(arr+L, arr+4-R);

assuming L and R are the number of elements you don't want to sort on each extremity of the array.

prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • The technical parts of this answer are all OK, and I don't see any ambiguity. However, I think the answer might be a bit inaccessible to the novices which struggle with questions like this. @gabrupro, if you want to chime in, please let us know if this is clear to you. – MSalters Feb 04 '21 at 16:49
0

is fine as .begin() and .end() return the pointers to the first and last elements of the array

Both functions return iterators, not pointers:

An iterator is any object that, pointing to some element in a range of elements (such as an array or a container), has the ability to iterate through the elements of that range using a set of operators (with at least the increment (++) and dereference (*) operators).

(https://www.cplusplus.com/reference/iterator/)

Let's check what the signature of std::sort. It is basically

template<class RandomIt>
void sort( RandomIt first, RandomIt last );

There are some requirements for RandomIt above: From cppreference.com:

  1. RandomIt must meet the requirements of ValueSwappable and LegacyRandomAccessIterator.
  2. The type of dereferenced RandomIt must meet the requirements of MoveAssignable and MoveConstructible.

Bullet 2 is a requirement on the type of the array-elements. Let's look at 1.: What is a LegacyRandomAccessIterator?

A LegacyRandomAccessIterator is a LegacyBidirectionalIterator that can be moved to point to any element in constant time.

Basically this a pointer-like structure that allows to be incremented by an arbitrary value (i + 5 is ok), it provides random access (that is your usual operator[] array access). This is exactly what the pointer arr is.

In summary, std::sort expects a random access iterator as arguments and arr is one.

But what if you want to sort only a portion of the vector?

Since arr.begin()/arr.end() are a random-access iterator you can just increment and decrement them:

std::sort(std::begin(arr) + 2, std::end(arr) - 1);

You need be be cautious to not access elements out-of-bounds.

dtell
  • 2,488
  • 1
  • 14
  • 29
0

For sorting vector partially you can use std::partial_sort

vector<int> v = { 1, 3, 1, 10, 3, 3, 7, 7, 8 }; 

// Using std::partial_sort 
std::partial_sort(v.begin(), v.begin() + 3, v.end());
Dharman
  • 30,962
  • 25
  • 85
  • 135
Naveen
  • 356
  • 2
  • 10
  • 2
    This is correct code, but probably not the answer to the question above. It looks at **all** elements in `v`, not just the first 3. – MSalters Feb 04 '21 at 15:16
-1
  1. Have a look to those function. std::begin and std::end.

  2. For second part, you might need to move your iterator by index using std::advance

    int main()
    {
       int arr[4] = {5, 3, 4, 7};
       auto pos = std::begin(arr);
    
       // Will move the 'begin' iterator one element further.
       std::advance(pos , 1);
    
       // Will sort the elements after the first index.
       std::sort(start, std::end(arr));
       for(int& x : arr) cout << x << " ";
       return 0;
    }
    
Vuwox
  • 2,331
  • 18
  • 33
  • 1
    This is a decent comment but a poor answer. – sweenish Feb 04 '21 at 14:55
  • Currently making it properly, just wanted the OP to know what he could use – Vuwox Feb 04 '21 at 14:55
  • There's no need for `std::advance` when you know `+` works. `std::advance` has a very specific use: In generic code, where you don't know if you have a random-access iterator, but you can accept O(N) iterator increments, `std::advance` will select the best way to increment that iterator. – MSalters Feb 04 '21 at 16:51
  • I just re-read the std::advance, and I thought that it was bounds-safe. But you can advance further than end(), which is UB. – Vuwox Feb 04 '21 at 18:18