C ++에서 두 std :: set의 교차점을 찾는 방법은 무엇입니까?


93

C ++에서 두 std :: set 사이의 교차점을 찾으려고했지만 계속 오류가 발생합니다.

이를 위해 작은 샘플 테스트를 만들었습니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;

int main() {
  set<int> s1;
  set<int> s2;

  s1.insert(1);
  s1.insert(2);
  s1.insert(3);
  s1.insert(4);

  s2.insert(1);
  s2.insert(6);
  s2.insert(3);
  s2.insert(0);

  set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end());
  return 0;
}

후자의 프로그램은 출력을 생성하지 않지만 s3다음 값을 가진 새 세트 (라고 부르겠습니다)를 가질 것으로 예상 합니다.

s3 = [ 1 , 3 ]

대신 오류가 발생합니다.

test.cpp: In function ‘int main()’:
test.cpp:19: error: no matching function for call to ‘set_intersection(std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>, std::_Rb_tree_const_iterator<int>)

이 오류에서 내가 이해하는 것은 매개 변수로 set_intersection받아들이는 정의가 없다는 것 Rb_tree_const_iterator<int>입니다.

또한 std::set.begin()메서드가 이러한 유형의 객체를 반환 한다고 가정합니다 .

std::setC ++에서 두 교차점을 찾는 더 좋은 방법이 있습니까? 가급적 내장 기능?

감사합니다!


"나는 새로운 세트가있을 것으로 예상한다 (s3라고 부르 자)" 그러나 당신은 그렇지 않았다. 결과가 어디로 갈 것이라고 예상했는지 모르겠습니다. 또한 전달할 인수를 찾기 위해 문서를 읽지 않았습니다.
궤도의 경쾌함 경주

답변:


113

set_intersection에 대한 출력 반복기를 제공하지 않았습니다.

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator set_intersection ( InputIterator1 first1, InputIterator1 last1,
                                InputIterator2 first2, InputIterator2 last2,
                                OutputIterator result );

다음과 같이하여이 문제를 해결하십시오.

...;
set<int> intersect;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(),
                  std::inserter(intersect,intersect.begin()));

std::insert세트가 현재 비어 있으므로 반복기 가 필요합니다 . set가 해당 작업을 지원하지 않으므로 back_ 또는 front_inserter를 사용할 수 없습니다.


70
세트에 대한 그러한 기본적인 작업에 왜 그렇게 신비한 장황한 주문이 필요한지 이해하고 싶습니다. 간단한 set<T>& set::isect(set<T>&)방법이 아닌 이유는 무엇 입니까? (나는 물어 것입니다 set<T>& set::operator^(set<T>&),하지만 너무 멀리 가능성이 다리입니다.)
라이언 V. BISSELL에게

3
@ RyanV.Bissell 이것은 거의 모든 알고리즘과 비슷한 디자인으로 <algorithm>일관성이 유지됩니다. 이 스타일은 또한 유연성을 제공한다고 생각합니다. 그리고 알고리즘을 여러 컨테이너와 함께 사용할 수 있지만 여기서는 발생하지 않을 수 있습니다. 또한 서명이 작동하지 않을 수 있으며 값을 반환해야 할 수도 있습니다. 그리고 복사 의미론 이전에는 이중 복사가 될 것이라고 생각합니다. 나는 한동안 C ++을하지 않았으므로 이것을 소금 한 꼬집 또는 3 개로 가져 가라
Karthik T

4
나는 여전히 자신을 STL 초보자라고 생각하므로 소금 알갱이의 적용도 적용됩니다. 내 댓글 편집 창이 만료되어 참조로 반환 가짜 파를 수정할 수 없습니다. 내 의견은 일관성에 대한 불만이 아니었지만이 구문이 왜 그렇게 쓴 맛이 나는지에 대한 정직한 질문이었습니다. 아마도 나는 이것을 SO 질문으로 만들어야 할 것입니다.
Ryan V. Bissell 2014 년

3
실제로 대부분의 C ++ std lib는이 모호한 방식으로 설계되었습니다. 디자인의 우아함은 분명하지만 (일반성이뿐만 아니라) API의 복잡성은 엄청난 영향을 미칩니다 (주로 사람들은 컴파일러와 함께 제공되는 휠을 사용할 수 없기 때문에 휠을 계속 재발 명하기 때문입니다). 다른 세계에서는 디자이너가 사용자의 즐거움보다 자신의 즐거움을 더 선호했기 때문에 두들겨 맞았을 것입니다. 이 세상에는 ... 글쎄, 적어도 우리는 StackOverflow가 있습니다.

3
이것은 "일반 구문"입니다. 벡터와 목록에서 set_intersection을 수행하고 결과를 deque에 저장할 수도 있습니다.이 작업도 효율적으로 수행 할 수 있어야합니다 (물론 두 가지 모두 소스 컨테이너는 이것을 호출하기 전에 정렬됩니다). 나는 그것이 나쁘다고 생각하지 않는다. 내가 가진 유일한 문제는 set다른 세트와 교차하는 컨테이너 메서드가있을 수도 있다는 것이다 . 컨테이너 대신 전달의 주제는 .begin()- .end()다른 일이 - C ++이 개념을 가지고 일단이 해결 될 것입니다.
Ethouris

25

링크의 샘플을 살펴보십시오 : http://en.cppreference.com/w/cpp/algorithm/set_intersection

교차 데이터를 저장하려면 다른 컨테이너가 필요합니다. 아래 코드는 작동한다고 가정합니다.

std::vector<int> common_data;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(), std::back_inserter(common_data));

6
back_inserter작동하지 않습니다 set으로 set더없는 push_back기능.
Jack Aidley

6

std :: set_intersection을 참조하십시오 . 결과를 저장할 출력 반복기를 추가해야합니다.

#include <iterator>
std::vector<int> s3;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(), std::back_inserter(s3));

전체 목록 은 Ideone 을 참조 하십시오.


3
결과도 세트가되게하려면 back_inserter가 작동하지 않으며 Karthik과 같은 std :: inserter가 필요합니다.
Joseph Garvin 2015

4

여기에 댓글 만 달아주세요. 설정된 인터페이스에 결합, 교차 연산을 추가 할 때라고 생각합니다. 이것을 미래의 표준에서 제안합시다. 나는 오랫동안 std를 사용해 왔는데, set 작업을 사용할 때마다 std가 더 좋기를 바랐습니다. intersect와 같은 복잡한 집합 연산의 경우 다음 코드를 간단하게 (쉽게?) 수정할 수 있습니다.

template <class InputIterator1, class InputIterator2, class OutputIterator>
  OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1,
                                   InputIterator2 first2, InputIterator2 last2,
                                   OutputIterator result)
{
  while (first1!=last1 && first2!=last2)
  {
    if (*first1<*first2) ++first1;
    else if (*first2<*first1) ++first2;
    else {
      *result = *first1;
      ++result; ++first1; ++first2;
    }
  }
  return result;
}

http://www.cplusplus.com/reference/algorithm/set_intersection/ 에서 복사

예를 들어, 출력이 세트 인 경우 output.insert (* first1)을 사용할 수 있습니다. 또한 함수가 템플릿 화되지 않을 수 있습니다. 코드가 std set_intersection 함수를 사용하는 것보다 짧을 수 있다면 계속 진행하십시오.

두 집합의 합집합을 수행하려면 간단히 setA.insert (setB.begin (), setB.end ()); 이것은 set_union 메소드보다 훨씬 간단합니다. 그러나 이것은 벡터에서 작동하지 않습니다.


4

수락 된 답변 의 첫 번째 (잘 투표 된) 의견은 기존 표준 집합 작업에 대한 누락 된 연산자에 대해 불평합니다.

한편으로는 표준 라이브러리에 그러한 연산자가 없다는 것을 이해합니다. 다른 한편으로, 원하는 경우 (개인의 기쁨을 위해) 쉽게 추가 할 수 있습니다. 나는 과부하

  • operator *() 세트의 교차
  • operator +() 세트의 결합을 위해.

샘플 test-set-ops.cc:

#include <algorithm>
#include <iterator>
#include <set>

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC> operator * (
  const std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  std::set<T, CMP, ALLOC> s;
  std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
    std::inserter(s, s.begin()));
  return s;
}

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC> operator + (
  const std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  std::set<T, CMP, ALLOC> s;
  std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(),
    std::inserter(s, s.begin()));
  return s;
}

// sample code to check them out:

#include <iostream>

using namespace std;

template <class T>
ostream& operator << (ostream &out, const set<T> &values)
{
  const char *sep = " ";
  for (const T &value : values) {
    out << sep << value; sep = ", ";
  }
  return out;
}

int main()
{
  set<int> s1 { 1, 2, 3, 4 };
  cout << "s1: {" << s1 << " }" << endl;
  set<int> s2 { 0, 1, 3, 6 };
  cout << "s2: {" << s2 << " }" << endl;
  cout << "I: {" << s1 * s2 << " }" << endl;
  cout << "U: {" << s1 + s2 << " }" << endl;
  return 0;
}

컴파일 및 테스트 :

$ g++ -std=c++11 -o test-set-ops test-set-ops.cc 

$ ./test-set-ops     
s1: { 1, 2, 3, 4 }
s2: { 0, 1, 3, 6 }
I: { 1, 3 }
U: { 0, 1, 2, 3, 4, 6 }

$ 

내가 싫어하는 것은 연산자의 반환 값 복사본입니다. 아마도 이것은 이동 할당을 사용하여 해결할 수 있지만 여전히 내 기술을 넘어서는 것입니다.

이러한 "새로운 멋진"이동 의미 체계에 대한 제한된 지식으로 인해 반환 된 집합의 복사본을 유발할 수있는 연산자 반환에 대해 걱정했습니다. Olaf Dietschestd::set 는 이미 이동 생성자 / 할당을 갖추고 있으므로 이러한 문제가 불필요 하다고 지적했습니다 .

나는 그를 믿었지만 이것을 어떻게 확인할지 생각하고 있었다. 사실 아주 쉽습니다. 템플릿은 소스 코드로 제공되어야하므로 디버거를 사용하면됩니다. 따라서, 나는 오른쪽에 브레이크 포인트를 배치 return s;operator *()과에 즉시 나를 납을 첨가 한 단계로 진행 std::set::set(_myt&& _Right): 등함으로써 해결할 - 이동 생성자입니다. (나의) 깨달음을 주셔서 감사합니다, 올라프.

완전성을 위해 해당 할당 연산자도 구현했습니다.

  • operator *=() 세트의 "파괴적인"교차점
  • operator +=() 세트의 "파괴적인"결합을 위해.

샘플 test-set-assign-ops.cc:

#include <iterator>
#include <set>

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC>& operator *= (
  std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  auto iter1 = s1.begin();
  for (auto iter2 = s2.begin(); iter1 != s1.end() && iter2 != s2.end();) {
    if (*iter1 < *iter2) iter1 = s1.erase(iter1);
    else {
      if (!(*iter2 < *iter1)) ++iter1;
      ++iter2;
    }
  }
  while (iter1 != s1.end()) iter1 = s1.erase(iter1);
  return s1;
}

template <class T, class CMP = std::less<T>, class ALLOC = std::allocator<T> >
std::set<T, CMP, ALLOC>& operator += (
  std::set<T, CMP, ALLOC> &s1, const std::set<T, CMP, ALLOC> &s2)
{
  s1.insert(s2.begin(), s2.end());
  return s1;
}

// sample code to check them out:

#include <iostream>

using namespace std;

template <class T>
ostream& operator << (ostream &out, const set<T> &values)
{
  const char *sep = " ";
  for (const T &value : values) {
    out << sep << value; sep = ", ";
  }
  return out;
}

int main()
{
  set<int> s1 { 1, 2, 3, 4 };
  cout << "s1: {" << s1 << " }" << endl;
  set<int> s2 { 0, 1, 3, 6 };
  cout << "s2: {" << s2 << " }" << endl;
  set<int> s1I = s1;
  s1I *= s2;
  cout << "s1I: {" << s1I << " }" << endl;
  set<int> s2I = s2;
  s2I *= s1;
  cout << "s2I: {" << s2I << " }" << endl;
  set<int> s1U = s1;
  s1U += s2;
  cout << "s1U: {" << s1U << " }" << endl;
  set<int> s2U = s2;
  s2U += s1;
  cout << "s2U: {" << s2U << " }" << endl;
  return 0;
}

컴파일 및 테스트 :

$ g++ -std=c++11 -o test-set-assign-ops test-set-assign-ops.cc 

$ ./test-set-assign-ops
s1: { 1, 2, 3, 4 }
s2: { 0, 1, 3, 6 }
s1I: { 1, 3 }
s2I: { 1, 3 }
s1U: { 0, 1, 2, 3, 4, 6 }
s2U: { 0, 1, 2, 3, 4, 6 }

$

1
std::set이미 필요한 이동 생성자와 할당 연산자가 구현되어 있으므로 걱정할 필요가 없습니다. 또한 컴파일러는 대부분 고용 반환 값 최적화
올라프 DIETSCHE

@OlafDietsche 귀하의 의견에 감사드립니다. 나는 이것을 확인하고 각각 대답을 개선했습니다. RVO에 관해서는 VS2013의 디버거에서 (적어도 우리 개발 플랫폼에서) 일어나지 않는다는 것을 보여줄 때까지 동료들과 이미 특정 토론을했습니다. 실제로 코드가 성능에 중요한 경우를 제외하고는 그렇게 중요하지 않습니다. 후자의 경우, 지금은 RVO에 의존하지 않습니다. (즉 ... ++ 실제로 C에서 그 어려운)
Scheff를

@Scheff 잘 Scheff (Bose가 아님), 좋은 설명.
JeJo

지금도 C ++ 17의 보장 된 제거에 대한 VS의 지원은 끔찍합니다.
궤도에있는 가벼운 레이스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.