std :: vector의 반복자 색인을 얻는 가장 효과적인 방법은 무엇입니까?


439

벡터를 반복하고 반복자가 현재 가리키는 색인이 필요합니다. AFAIK는 두 가지 방법으로 수행 할 수 있습니다.

  • it - vec.begin()
  • std::distance(vec.begin(), it)

이 방법의 장단점은 무엇입니까?

답변:


558

내가 선호하는 it - vec.begin()나빈에 의해 주어진 반대 이유에 대해 정확하게 : 그렇게 하지 않을 당신이 목록에 벡터를 변경하면 컴파일합니다. 반복 할 때마다이 작업을 수행하면 O (n) 알고리즘을 O (n ^ 2) 알고리즘으로 쉽게 전환 할 수 있습니다.

또 다른 옵션은 반복 중에 컨테이너에서 뛰어 다니지 않으면 인덱스를 두 번째 루프 카운터로 유지하는 것입니다.

참고 : it컨테이너 반복자의 일반 이름입니다 std::container_type::iterator it;.


3
동의했다. 빼기 부호가 가장 좋다고 말하지만 std :: distance를 사용하는 것보다 두 번째 루프 카운터를 유지하는 것이 좋습니다.이 기능은 느릴 수 있기 때문입니다.
Steven Sudit

28
도대체 무엇 it입니까?
Steinfeld

32
@Steinfeld는 반복자입니다. std::container_type::iterator it;
매트 먼슨

2
두 번째 루프 카운터를 추가하는 것은 내가 생각하지 못한 당혹스러운 해결책입니다.
Mordred

3
때문에 @Swapnil std::list, 자신의 위치에 의한 요소에 대한 직접 액세스를 제공하지 않습니다 당신이 할 수없는 경우에 그래서 list[5], 당신이 할 수 없어야합니다 list.begin() + 5.
José Tomás Tocino

135

std::distance(vec.begin(), it)코드를 변경하지 않고 컨테이너를 변경할 수 있기 때문에 선호합니다 . 예를 들어, 랜덤 액세스 반복자를 제공하지 않는 std::list대신 사용하기로 결정하면 std::vector코드는 여전히 컴파일됩니다. std :: distance는 반복자 특성에 따라 최적의 방법을 선택하므로 성능 저하가 없습니다.


50
랜덤 액세스 반복자가없는 컨테이너를 사용하는 경우 비효율적이므로 이러한 거리 계산 하지 않는 것이 가장 좋습니다.
Eli Bendersky

6
@ Elli : 나는 그것에 동의하지만, 매우 특별한 경우에 실제로 필요한 경우에도 그 코드는 여전히 작동합니다.
Naveen

9
컨테이너가 변경되면 코드를 변경해야한다고 생각합니다 .std :: list 변수라는 이름 vec은 나쁜 소식입니다. 컨테이너 유형을 템플릿 매개 변수로 사용하여 코드를 일반으로 다시 작성한 경우 비 랜덤 액세스 반복자를 처리하는 방법에 대해 이야기 할 수 있어야합니다 (
Steve Jessop

1
특정 용기에 대한 전문화.
ScaryAardvark

19
@SteveJessop : 이름 vec이 지정된 벡터를 갖는 것도 나쁜 소식입니다.
River Tam

74

UncleBens와 Naveen에서 알 수 있듯이 두 가지 이유가 있습니다. 어떤 행동이 "더 나은"행동인지에 따라 달라집니다. 일정한 시간 행동을 보장하고 싶습니까, 아니면 필요할 때 선형 시간으로 넘어 가기를 원하십니까?

it - vec.begin()일정한 시간 operator -이 걸리지 만 무작위 액세스 반복자에서만 정의되므로 코드는 목록 반복자와 함께 전혀 컴파일되지 않습니다.

std::distance(vec.begin(), it) 모든 반복자 유형에 대해 작동하지만 임의 액세스 반복자에서 사용되는 경우 일정 시간 동안 만 작동합니다.

어느 쪽도 "더 나은"것이 아닙니다. 필요한 것을하는 것을 사용하십시오.


1
나는 과거에 이것에 대한 파울에 빠졌다. 두 개의 std :: map 반복자에서 std :: distance를 사용하고 O (N) 일 것으로 예상합니다.
ScaryAardvark

6
@ScaryAardvark : O (1) 일 것으로 예상하지 않습니까?
jalf

12

나는 이것을 좋아한다 : it - vec.begin(), 나에게 그것은 "처음부터의 거리"를 분명히 나타 내기 때문이다. 반복자를 사용하면 산술 측면에서 생각하는 데 익숙하므로 -부호가 가장 분명한 표시기입니다.


19
말 그대로 단어를 사용하는 것보다 뺄셈을 사용하여 거리를 찾는 것이 더 분명합니다 distance.
Travis Gockel

4
@ 트래비스, 나에게. 맛과 관습의 문제입니다. 우리는 말을 it++하지 뭔가 std::increment(it)우리가하지? 덜 명확하지 않습니까?
Eli Bendersky

3
++연산자 우리 반복기를 증가 방법으로서 STL 시퀀스의 일부로서 정의된다. std::distance첫 번째 요소와 마지막 요소 사이의 요소 수를 계산합니다. -운영자가 일 한다는 사실 은 단지 우연의 일치 일뿐입니다.
Travis Gockel

3
@MSalters : 그리고 우리는 ++ :-)를 사용합니다
Eli Bendersky

10

이미 제한하는 경우 / A를 사용하여 알고리즘을 하드 코딩 std::vector::iteratorstd::vector::iterator만, 정말없이 사용하여 종료됩니다하지 않는 방법. 알고리즘 중 하나를 선택하면 차이가 생길 수있는 시점을 넘어서 알고리즘이 이미 구체화되었습니다. 둘 다 정확히 같은 일을합니다. 그것은 개인적인 취향의 문제 일뿐입니다. 나는 개인적으로 명시 적 뺄셈을 사용합니다.

반면에 알고리즘에서 더 높은 수준의 일반성을 유지하려는 경우, 즉 언젠가 미래에 다른 반복자 유형에 적용될 가능성을 허용하기 위해 최선의 방법은 의도에 달려 있습니다. . 여기에서 사용할 수있는 반복자 유형과 관련하여 얼마나 제한적인지에 따라 다릅니다.

  • 명시 적 뺄셈을 사용하는 경우 알고리즘은 다소 좁은 반복자 클래스 인 임의 액세스 반복자로 제한됩니다. (이것은 지금 당신이 얻는 것입니다 std::vector)

  • 을 사용 distance하면 알고리즘이 훨씬 광범위한 반복자 클래스 인 입력 반복자를 지원합니다.

물론, distance비 랜덤 액세스 반복자에 대한 계산 은 일반적으로 비효율적 인 작업입니다 (반복 액세스 조작의 경우 빼기만큼 효율적 임). 알고리즘 효율성에 따라 비 랜덤 액세스 반복자 에 적합한 지 결정하는 것은 사용자의 몫 입니다. 결과적으로 효율성의 손실은 알고리즘을 완전히 쓸모 없게 만드는 지점으로 치명적입니다. 그러면 빼기를 더 잘 사용해야 비효율적 인 사용을 금지하고 사용자가 다른 반복자 유형에 대한 대체 솔루션을 강요해야합니다. 비 랜덤 액세스 반복기의 효율성이 여전히 사용 가능한 범위에있는 경우 랜덤 액세스 반복기 distance에서 알고리즘이 더 잘 작동한다는 사실을 사용 하고 문서화 해야 합니다.



3

나는 -변형 std::vector만을 사용한다 -의미가 분명하고 조작의 단순성 (포인터 빼기 이상은 아닙니다)은 구문 ( distance, 다른 한편으로는 피타고라스와 같은 소리 )으로 표현됩니다 먼저 읽지 않습니까?). UncleBen이 지적했듯이 실수로로 변경되면 -정적 어설 션으로도 작동 vector합니다 list.

또한 나는 그것이 훨씬 흔하다고 생각합니다-그것을 증명할 숫자가 없습니다. 마스터 논증 :it - vec.begin() 소스 코드가 짧아 타이핑 작업이 적고 공간이 절약됩니다. 귀하의 질문에 대한 정답이 미각의 문제로 귀결된다는 것이 명백하기 때문에, 이것은 또한 유효한 주장 일 수 있습니다 .


0

다음은 인덱스와 함께 "all"어커런스를 찾는 예제입니다. 이것이 도움이 될 것이라고 생각했습니다.

void _find_all_test()
{
    vector<int> ints;
    int val;
    while(cin >> val) ints.push_back(val);

    vector<int>::iterator it;
    it = ints.begin();
    int count = ints.size();
    do
    {
        it = find(it,ints.end(), 10);//assuming 10 as search element
        cout << *it << " found at index " << count -(ints.end() - it) << endl;
    }while(++it != ints.end()); 
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.