当我们在向量上使用唯一函数时,移位如何工作?

时间:2015-05-02 06:15:36

标签: c++ vector stl

所以,我目前正在阅读一些c ++的东西,我在cppreference上遇到了这个例子,我无法理解这种转变是如何工作的。

#include <iostream>
#include <algorithm>
#include <vector>

int main() 
{
    std::vector<int> v{1, 2, 2, 2, 3, 3, 2, 2, 1};
    std::vector<int>::iterator last;

    last = std::unique(v.begin(), v.end()); // 1 2 3 2 1 3 2 2 1
                                            //           ^
    for (std::vector<int>::iterator it = v.begin(); it != last; ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";
}

我理解当我们使用unique时会将事情转移过来,但我不确定我们如何从lastv.end()获取给我们的序列。

通过我自己在纸上的绘图,我了解了我们如何实现从v.begin()v.last()的序列,而不是如上所述从v.last()v.end()的序列。

这是reference site

2 个答案:

答案 0 :(得分:5)

std:unique确实只是根据需要将元素转移到开头。这种转变并不像你可能在想的那样。它不需要一次传播一个元素的东西。它可以利用元素必须 move-assignable 的要求。根据移动赋值的定义,移动元素后,其先前内容未指定。在你的情况下,它只保留&#34;值&#34;那里,但它不是指定的值。

简而言之,您所看到的是剩余价值,而其中一些可能非特定

以下是使用您的数据的简单演示。最初我们有两个插槽位置,R和一个W.我没有保证这是std::unique使用的 算法(老实说我不知道​​)。

根据琐碎的情况(0或1长度序列),当一个值保持时,它将在W上方的 next 槽中移动分配,W是先进的。无论是否保留,R始终是先进的。完成后,插槽过去 W是last(即左侧插槽中的第一个,其中一些可能具有未指定的值)。

鉴于您的数据,序列将是这样的:

1, 2, 2, 2, 3, 3, 2, 2, 1 - different, since the write target
W  R                        is the same as the read-point, do nothing,
                            and advance both R and W

1, 2, 2, 2, 3, 3, 2, 2, 1 - equivalent, advance R only
   W  R                     

1, 2, 2, 2, 3, 3, 2, 2, 1 - equivalent, advance R only
   W     R                     

1, 2, 2, 2, 3, 3, 2, 2, 1 - different, move the 3 to the next write
   W        R               point and advance both R and W

1, 2, 3, 2, 3, 3, 2, 2, 1 - equivalent, advance R only
      W        R

1, 2, 3, 2, 3, 3, 2, 2, 1 - different, move the 2 to the next write
      W           R         slot and advance both R and W

1, 2, 3, 2, 3, 3, 2, 2, 1 - equivalent, advance R only
         W           R      

1, 2, 3, 2, 3, 3, 2, 2, 1 - different, move the 1 to the next write
         W              R   slot and advance both R and W

1, 2, 3, 2, 1, 3, 2, 2, 1 - read is at end-of-sequence
            W             R 

此时,读者已完成。算法结束时,经过W的第一个时隙是last(如果原始序列没有重复,则可能确实是end)。我给你留下挑战,确定哪些元素(3,2,2,1)在&#34;未指定&#34;完成后的状态。

提示:被移动了什么?跳过了什么?什么被覆盖了?为什么这有关系?尝试在移动的任何内容的读取插槽上写0,并查看从lastend的序列中的剩余内容。

答案 1 :(得分:4)

独特的算法非常简单。给定范围[First,Last],使用两个迭代器InOut来删除连续的重复项。 InOut最初分配给序列中的第一个元素。

In = Out = First

如果范围不为空,则算法按如下方式进行。

while (++In != Last)
{
    if (*In != *Out)
    {
        ++Out;
        *Out = *In;
    }
}

现在只需继续直到In已达到范围的末尾。然后我们返回++Out

Out基本上是一个输出迭代器,将唯一元素写入相同的范围,因此我们可以通过在上面的算法完成后递增来检索最后一个迭代器。

如果您没有考虑转移输出(并覆盖)相同的范围,则会更容易。唯一棘手的部分是我们覆盖了我们正在阅读的相同范围。如果要将唯一元素推回到不同的序列并返回该序列,那么掌握算法要容易得多。现在,您只需应用一个小优化,以避免因覆盖正在读取的同一个输出范围而需要单独的输出范围。

这是一个快速实现我掀起的,可能比供应商版本更容易理解。它没有一些常见的漏洞,比如重用'first'作为输入迭代器,使用operator!=(而不是!和==)等。

template <class Iterator>
Iterator my_unique(Iterator first, Iterator last)
{
    if (first != last)
    {
        Iterator in = first;
        Iterator out = first;
        while (++in != last)
        {
            if (*in != *out)
            {
                ++out;
                *out = *in;
            }
        }
        return ++out;
    }
    return last;
}