【问题标题】:Passing beginning and ending positions of an array传递数组的开始和结束位置
【发布时间】:2021-02-04 14:47:25
【问题描述】:

我看到许多函数需要数组/向量的开始和结束位置。 我有两个疑问,一个在数组中,第二个在向量中:

  1. 此代码
#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;
}

很好,因为 .begin() 和 .end() 分别返回指向数组的第一个和最后一个元素的指针(顺便说一句,如果我在任何地方错了,请纠正我)。

但是请解释一下这段代码是如何工作的:

#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. 我知道如果您想对整个向量进行排序(或执行任何其他功能),您可以使用 .begin() 和 .end()。

但是,如果您只想对向量的一部分进行排序怎么办?就像左侧位置 L 和右侧位置 R 之间的向量一样。您将如何仅使用 .begin 和 .end 来做到这一点(不切片向量)?

非常感谢!

【问题讨论】:

  • 看看std::begin
  • 这个概念很简单,你不会在部分向量排序的情况下使用.end()。您也不必从.begin() 开始。 .end() 不是最后一个元素。使用 &lt;iterator&gt; 或 std::array 可以更好地编写您的 C 数组代码示例。
  • 这能回答你的问题吗? What are Iterators, C++?
  • int arr[4] 没有没有.begin()、.end()或任何其他成员。所以你发布的第一段代码不可能是“好的”。估计你抄错了。请粘贴实际代码。

标签: c++ arrays


【解决方案1】:

在您的第一个示例中,您应该编写。

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

因为int[] 是内置类型(来自C)并且没有任何成员函数。 存在std::begin() 和std::end() 的重载适用于T[],如上所示。

在 C 中,仅使用数组的 name 是一个表达式,它衰减 到该数组的开头。 例如,在:

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

p 是用数组中第一个元素的地址初始化的指针。 相当于

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

此外,在 C 中,存在一种称为 指针算术 的东西。 这意味着您可以使用指针递增、递减、计算差值。 这里,p+4 表示与p 指向的元素之后的第四个元素相关的指针;在这个特定的例子中,它就在你的数组结束之后。 所以,回到你的第二个例子,arr 等价于begin(arr),arr+4 等价于end(arr)。

我们不应该知道std::begin() 和std::end() 返回的迭代器 的确切性质的任何细节;我们唯一知道的是它们的行为就像指针的隐喻,以便告诉元素在哪里,移动到下一个元素...... 但是当这些元素的存储是内存的连续部分时(如T[]、std::array&lt;T&gt;、std::vector&lt;T&gt;...),在内部它们只不过是指针(我们不应该知道...但我们知道; ^) 此外,在这个特定的连续存储案例中,纯真指针是指针的完美隐喻......这就是为什么在设计时考虑到迭代器的 std::sort() 也适用于简单的指针,如 arr 和 @987654342 @。

您可能会对结束位置指向一个不存在的元素(就在数组中的最后一个元素之后)这一事实感到困惑。 这是许多算法的惯例:我们指定一个半开区间,包括第一个边界,排除第二个边界。 所以算法永远不会访问这个 one-step-too-far 元素;它会在到达之前停止。 这与经典的for() 循环一致:假设您想要五次迭代,您将编写for(int i=0; i&lt;5; ++i) 以指定0 和5 作为边界,但实际上您将使用值0、1, 2、3、4 但不是5。

当只对数组的一部分进行排序时,您可以对指针或迭代器使用算术运算。

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

假设 L 和 R 是您不想在数组的每个末端排序的元素数。

【讨论】:

  • 这个答案的技术部分都可以,我没有看到任何歧义。但是,我认为对于遇到此类问题的新手来说,答案可能有点难以理解。 @gabrupro,如果您想插话,请告诉我们您是否清楚。
【解决方案2】:

对于部分排序向量,您可以使用 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());

【讨论】:

  • 这是正确的代码,但可能不是上述问题的答案。它查看v 中的所有 元素,而不仅仅是前3 个。
【解决方案3】:

很好,因为 .begin() 和 .end() 返回指向数组第一个和最后一个元素的指针

两个函数都返回迭代器,而不是指针:

迭代器是任何对象,它指向元素范围(例如数组或容器)中的某个元素,能够使用一组运算符(至少具有递增 (++) 和取消引用 (*) 运算符)。

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

我们来看看std::sort的签名是什么。基本上是

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

上面对RandomIt有一些要求:来自cppreference.com:

  1. RandomIt 必须满足 ValueSwappable 和 LegacyRandomAccessIterator 的要求。
  2. 解引用的 RandomIt 的类型必须满足 MoveAssignable 和 MoveConstructible 的要求。

Bullet 2 是对数组元素类型的要求。让我们看看1.:LegacyRandomAccessIterator是什么?

LegacyRandomAccessIterator 是一个 LegacyBidirectionalIterator,可以在恒定时间内移动以指向任何元素。

基本上这是一个类似指针的结构,允许以任意值递增(i + 5 可以),它提供随机访问(即您通常的operator[] 数组访问)。这正是指针arr 的含义。

总之,std::sort 需要一个随机访问迭代器作为参数,arr 是一个。

但是如果您只想对向量的一部分进行排序呢?

由于arr.begin()/arr.end() 是一个随机访问迭代器,您可以对它们进行递增和递减:

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

您需要小心不要越界访问元素。

【讨论】:

    【解决方案4】:
    1. 看看这些功能。 std::begin 和 std::end。

    2. 对于第二部分,您可能需要使用 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;
      }
      

    【讨论】:

    • 这是一个不错的评论,但答案很差。
    • 目前制作正常,只是想让 OP 知道他可以使用什么
    • 当您知道+ 有效时,就不需要std::advance。 std::advance 有一个非常具体的用途:在通用代码中,您不知道是否有随机访问迭代器,但您可以接受 O(N) 迭代器增量,std::advance 将选择增加它的最佳方式迭代器。
    • 我刚刚重新阅读了 std::advance,我认为它是有界安全的。但是你可以比 end() 更进一步,也就是 UB。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多