📜  C++中的双向迭代器

📅  最后修改于: 2021-05-30 08:17:58             🧑  作者: Mango

在经过各种STL算法(例如std :: reverse,std :: next_permutation和std :: reverse_copy)的模板定义之后,您必须找到其模板定义,该模板定义由Bidirectional Iterator类型的对象组成。那么,它们是什么?为什么要使用它们?

双向迭代器是C++标准库中存在的五种主要迭代器之一,其他类型是输入迭代器输出迭代器正向迭代器随机访问迭代器

双向迭代器是一种迭代器,可用于在两个方向上(朝着末端和朝着起点)访问一定范围内的元素序列。它们与正向迭代器相似,不同之处在于它们也可以向后移动,与正向迭代器不同,正向迭代器只能向前移动。

要注意的是,像list,map,multimap,set和multiset之类的容器都支持双向迭代器。这意味着,如果我们为它们声明普通迭代器,则这些迭代器将是双向迭代器,就像矢量和双端队列一样,它们是随机访问迭代器。

要记住的一件事是,随机访问迭代器也是有效的双向迭代器,如上面的迭代器层次结构所示。

显着特征

  1. 可用性:由于前向迭代器可用于多遍算法,即涉及在不同遍中多次处理容器的算法,因此双向迭代器也可用于多遍算法。
  2. 相等/不相等比较:可以将双向迭代器与另一个迭代器进行相等性比较。由于迭代器指向某个位置,因此这两个迭代器仅在指向相同位置时才相等,否则就不相等。

    因此,如果A和B是双向迭代器,则以下两个表达式有效:

    A == B  // Checking for equality
    A != B  // Checking for inequality
    
  3. 取消引用:由于可以使用输入运算符*和->作为右值,而可以将输出迭代器作为左值,因此可以对输入迭代器进行解引用,因此正向迭代器(两者兼而有之)既可以用于这两个目的,也可以双向使用运算符也可以同时达到这两个目的。
    // C++ program to demonstrate bidirectional iterator
    #include
    #include
    using namespace std;
    int main()
    {
        listv1 = {1, 2, 3, 4, 5};
      
        // Declaring an iterator
        list::iterator i1;
      
        for (i1=v1.begin();i1!=v1.end();++i1)
        {
            // Assigning values to locations pointed by iterator
            *i1 = 1;
        }
      
        for (i1=v1.begin();i1!=v1.end();++i1)
        {
            // Accessing values at locations pointed by iterator
            cout << (*i1) << " ";
        }
          
        return 0;
    }
    

    输出:

    1 1 1 1 1
    

    因此,正如我们在此处看到的那样,我们既可以访问迭代器,也可以为其分配值,因此,迭代器是双向迭代器。

  4. 可递增的:双向迭代器可以递增,以便使用运算符++()引用序列中的下一个元素。

    因此,如果A是双向迭代器,则以下两个表达式有效:

    A++   // Using post increment operator
    ++A   // Using pre increment operator
    
  5. 可递减的:这是将双向迭代器与正向迭代器区分开的功能。就像我们可以将运算符++()与双向迭代器一起使用来增加它们一样,我们也可以减少它们。

    这就是为什么它的名称是双向的,这表明它可以在两个方向上移动

    // C++ program to demonstrate bidirectional iterator
    #include
    #include
    using namespace std;
    int main()
    {
        listv1 = {1, 2, 3, 4, 5};
      
        // Declaring an iterator
        list::iterator i1;
      
        // Accessing the elements from end using decrement
        // operator
        for (i1=v1.end();i1!=v1.begin();--i1)
        {
            if (i1 != v1.end())
            {
                cout << (*i1) << " ";
            }
        }
        cout << (*i1);
          
        return 0;
    }
    

    输出:

    5 4 3 2 1
    

    既然如此,我们从列表的末尾开始,然后通过递减指针而移到开头,这表明递减运算符可与此类迭代器一起使用。在这里,我们运行了循环,直到迭代器变得与begin()相等为止,这就是为什么第一个值未在循环内打印而我们将其单独打印的原因。

  6. 可交换的:这些迭代器指向的值可以交换或交换。

实际实施

了解了它的功能之后,了解它的实际实现也非常重要。如前所述,双向迭代器可用于所有目的,正向迭代器可在需要减少迭代器的那些情况下一起使用。以下两种STL算法可以显示这一事实:

  • std :: reverse_copy:顾名思义,此算法用于将一个范围复制到另一个范围,但顺序相反。现在,就访问元素和分配元素而言,前向迭代器就可以了,但是一旦我们必须递减迭代器,就不能为此目的使用这些前向迭代器,这就是双向迭代器为我们提供帮助的地方。
    // Definition of std::reverse_copy()
    template 
    OutputIterator reverse_copy(BidirectionalIterator first,
                                BidirectionalIterator last,
                                OutputIterator result) 
    {
        while (first != last) 
        *result++ = *--last;
        return result;
    }
    

    在这里,我们可以看到我们已将last声明为双向迭代器,因为我们不能像last那样减少前向迭代器,因此我们不能在这种情况下使用它,而只能将其声明为双向迭代器。

  • std :: random_shuffle:众所周知,该算法用于随机地重排容器中存在的所有元素。因此,让我们看一下它的内部工作原理(不要再去研究细节了,而是看看可以在哪里使用双向迭代器以及在哪里不能使用双向迭代器):
    // Definition of std::random_shuffle()
    template 
    void random_shuffle(RandomAccessIterator first,
                        RandomAccessIterator last,
                        RandomNumberGenerator& gen)
    {
        iterator_traits::difference_type i, n;
        n = (last - first);
        for (i=n-1; i>0; --i) 
        {
            swap (first[i],first[gen(i+1)]);
        }
    }
    

    在这里,我们可以看到我们已经使用了随机访问迭代器,好像我们可以增加双向迭代器,也可以减少它,但是我们不能对其应用像+,–这样的算术运算运算符,这是该算法所需要的,其中(最后–首先)完成了,因此,由于这个原因,在这些情况下我们不能使用双向迭代器。

注意:众所周知,双向迭代器在层次结构中比正向迭代器高,而正向迭代器本身也比输入和输出迭代器高,因此,这三种类型的迭代器都可以用双向迭代器代替,而不会影响算法的工作。

因此,以上两个示例很好地说明了双向迭代器在何时,何地,为什么以及如何被实际使用。

局限性

在研究了显着特征之后,还必须知道其缺陷,尽管输入或输出迭代器中的缺陷不如层次结构中较高的迭代器。

  1. 关系运算符:尽管双向迭代器可以与相等运算符(==)一起使用,但是不能与其他关系运算符(如= =)一起使用。
    If A and B are Bidirectional iterators, then
    
    A == B     // Allowed
    A <= B     // Not Allowed
    
  2. 算术运算符: -等方面的关系运算符相似,而且还不能与算术运算运算符如+,使用。这意味着双向迭代器可以在两个方向上但顺序地移动。
    If A and B are Bidirectional iterators, then
    
    A + 1     // Not allowed
    B - 2     // Not allowed
    
    // C++ program to demonstrate bidirectional iterator
    #include
    #include
    using namespace std;
    int main()
    {
        listv1 = {1, 2, 3, 4, 5};
      
        // Declaring first iterator
        list::iterator i1;
      
        // Declaring second iterator
        list::iterator i2;
      
        // i1 points to the beginning of the list
        i1 = v1.begin();
      
        // i2 points to the end of the list
        i2 = v1.end();
      
        // Applying relational operator to them
        if ( i1 > i2)
        {
            cout << "Yes";
        } 
        // This will throw an error because relational
        // operators cannot be applied to bidirectional iterators
      
      
        // Applying arithmetic operator to them
        int count = i1 - i2;
        // This will also throw an error because arithmetic 
        // operators cannot be applied to bidirectional iterators  
        return 0;
    }
    

    输出:

    Error, because of use of arithmetic and relational operators 
    with bidirectional iterators 
    
  3. 使用偏移量解除引用运算符([]):双向迭代器不支持用于随机访问的偏移量解除引用运算符([])。
    If A is a Bidirectional iterator, then
    A[3]    // Not allowed 
    
要从最佳影片策划和实践问题去学习,检查了C++基础课程为基础,以先进的C++和C++ STL课程基础加上STL。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程”