C ++에서 set과 unordered_set의 차이점은 무엇입니까?
이 좋은 질문을 보았습니다.이 질문은 비슷하지만 전혀 동일하지는 않습니다. 이것은 접근 자 / 뮤 테이터가 동기화되어 있기 때문에 해시 테이블 구현이 다른 Java에 대해 이야기하기 때문에 HashMap과 Hashtable의 차이점은 무엇입니까?
그래서 C ++에서 set과 unordered_set 구현의 차이점은 무엇입니까? 이 질문은 당연히 다른 C ++ 컨테이너에 대해 map vs unordered_map 등으로 확장 될 수 있습니다.
다음은 내 초기 평가입니다.
set : 표준은 명시 적으로 트리로 구현하도록 요청하지 않지만, 찾기 / 삽입을위한 작업에 대한 시간 복잡성 제약 조건은 항상 트리로 구현됨을 의미합니다. 일반적으로 높이 균형이 잡힌 RB 트리 (GCC 4.8에서 볼 수 있음)입니다. 높이가 균형을 이루기 때문에 find ()에 대해 예측 가능한 시간 복잡성이 있습니다.
장점 : 콤팩트 (다른 DS와 비교)
단점 : 액세스 시간 복잡도는 O (lg n)
unorder_set : 표준은 명시 적으로 트리로 구현하도록 요청하지 않지만, 찾기 / 삽입 작업에 대한 시간 복잡도 제약 조건은 항상 해시 테이블로 구현됨을 의미합니다.
장점 :
- 더 빠름 (검색을 위해 상각 된 O (1) 약속)
- tree-DS에 비해 기본 프리미티브를 스레드로부터 안전한 것으로 쉽게 변환
단점 :
- O가 보장되지 않는 조회 (1) 이론상 최악의 경우는 O (n)입니다.
- 나무만큼 콤팩트하지 않습니다. (실용 상 부하 계수는 1이 아닙니다)
참고 : 해시 테이블에 대한 O (1)은 충돌이 없다는 가정에서 비롯됩니다. 부하 계수가 .5 인 경우에도 매초마다 변수가 삽입되면 충돌이 발생합니다. 해시 테이블의로드 팩터는 해당 요소에 액세스하는 데 필요한 작업 수에 반비례한다는 것을 알 수 있습니다. 더 많은 작업, 희소 해시 테이블을 줄입니다. 저장된 요소의 크기가 포인터와 비슷한 경우 오버 헤드가 상당히 큽니다.
편집 : 대부분의 질문에 충분한 답변이 포함되어 있기 때문에 질문을 "성능 분석을위한 맵 / 세트 간의 차이점을 놓쳤습니까?"로 변경하고 있습니다.
나는 당신이 일반적으로 자신의 질문에 대답했다고 생각합니다.
나무만큼 콤팩트하지 않습니다. (실용 상 부하 계수는 1이 아닙니다)
반드시 사실은 아닙니다. 유형에 대한 트리의 각 노드 (빨강-검정 트리라고 가정) T
는 최소한 2 * pointer_size + sizeof(T) + sizeof(bool)
. 이는 3 * pointer size
트리가 parent
각 트리 노드에 대한 포인터를 포함하는지 여부에 따라 달라질 수 있습니다 .
이것을 해시 맵과 비교하십시오 load factor < 1
. 당신이 말했듯이 각 해시 맵에 대해 낭비되는 배열 공간이 있습니다 . 그러나 해시 맵이 연결을 위해 단일 연결 목록을 사용한다고 가정하면 (실제로는 그렇지 않을 이유가 없습니다) 삽입 된 각 요소는 sizeof(T) + pointer size
.
이 분석은 정렬에 사용되는 추가 공간에서 발생할 수있는 모든 오버 헤드를 무시합니다.
T
크기가 작은 요소 (기본 유형)의 경우 포인터의 크기와 기타 오버 헤드가 지배적입니다. > 0.5
예를 들어 로드 팩터에서는 std::unordered_set
실제로 동등한 것보다 적은 메모리를 사용할 수 있습니다 std::set
.
또 다른 큰 빠진 점은 a를 통해 반복 std::set
하면 주어진 비교 함수를 기반으로 가장 작은 std::unordered_set
것부터 가장 큰 순서로 순서가 보장 된다는 사실입니다.를 반복 하면 "무작위"순서로 값이 반환됩니다.
또 다른 차이점은 (성능과 관련이 없지만) set
삽입은 반복자를 무효화하지 않는 반면 unordered_set
삽입은 재해시를 트리거 할 수 있다는 것입니다. 실제로는 실제 요소에 대한 참조가 유효하기 때문에 매우 사소한 문제입니다.
Yuushi는 이미 공간 효율성 및 기타 사항을 잘 다루고 있습니다. 제가 언급 할 질문의 몇 가지 다른 부분 ...
해시 테이블에 대한 O (1)은 충돌이 없다는 가정에서 비롯됩니다.
그건 사실이 아니야. O (1)이 의미하는 바는 첫 번째 조회 시도가 항상 성공한다는 것이 아니라 값의 수가 증가함에 따라 증가하는 것이 아니라 평균적으로 일정한 횟수의 시도가 필요하다는 것입니다. 예를 들어, unordered_set
또는 ... _map
를 사용하면 구성 max_load_factor
시 기본값이 1.0으로 설정되고, 좋은 해시 함수를 사용하여 부하 계수가 이에 가까워지면 하나의 버킷에 해시 되는 평균 요소 수는 값 수에 관계없이 약 2 개가됩니다. 테이블에.
부하 계수가 .5 인 경우에도 매초마다 변수가 삽입되면 충돌이 발생합니다.
사실이지만 직관적으로 예상하는 것만 큼 심각하지는 않습니다. 1.0로드 팩터에서 평균 체인 길이가 2 인 것은 나쁘지 않습니다.
해시 테이블의로드 팩터는 해당 요소에 액세스하는 데 필요한 작업 수에 반비례한다는 것을 알 수 있습니다. 더 많은 작업, 희소 해시 테이블을 줄입니다.
확실히 상관 관계가 있습니다 (역이 아닙니다).
어떤 경우 set
에는 더 편리합니다.
예를 들어 vector
키로 사용 :
set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});
for(const auto& vec:s)
cout<<vec<<endl; // I have override << for vector
// 1 2
// 1 3
그 이유는 그 이유 vector<int>
에있을 수 set
있기 때문에 vector
무시 operator<
.
그러나 사용하는 경우 벡터에 해시 함수가 없기 때문에에 unordered_set<vector<int>>
대한 해시 함수를 만들어야 vector<int>
하므로 다음과 같이 정의해야합니다.
struct VectorHash {
size_t operator()(const std::vector<int>& v) const {
std::hash<int> hasher;
size_t seed = 0;
for (int i : v) {
seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
return seed;
}
};
vector<vector<int>> two(){
//unordered_set<vector<int>> s; // error vector<int> doesn't have hash function
unordered_set<vector<int>, VectorHash> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});
for(const auto& vec:s)
cout<<vec<<endl;
// 1 2
// 1 3
}
어떤 경우 unordered_set
에는 더 복잡 하다는 것을 알 수 있습니다 .
Mainly cited from: https://stackoverflow.com/a/29855973/6329006
More difference between unordered_set
and set
see this: https://stackoverflow.com/a/52203931/6329006
ReferenceURL : https://stackoverflow.com/questions/16075890/what-is-the-difference-between-set-and-unordered-set-in-c
'programing' 카테고리의 다른 글
JavaScript로 CSS 생성 콘텐츠에 액세스하는 방법 (0) | 2021.01.16 |
---|---|
반복되는 fmap으로 재미 (0) | 2021.01.16 |
C99에서 f () + g ()는 정의되지 않았습니까 아니면 단순히 지정되지 않았습니까? (0) | 2021.01.16 |
RxJS에서 Observable 연결 (0) | 2021.01.16 |
하위 컬렉션으로 Cloud Firestore 심층 가져 오기 (0) | 2021.01.16 |