서로 교차하지 않다
18386 단어 algorithmsdatastructures
서로 교차하지 않음(DSU)
개술
서로 교차하지 않는 데이터 구조는 두 항목이 같은 집중에 있는지 (또는 같은 효과로 두 정점이 같은 연결 구성 요소에 있는지 확인하는 것) 을 신속하게 확정할 수 있고, 두 개의 집합을 빠르게 통합할 수 있으며 (또는 같은 효과로 두 개의 연결 구성 요소를 하나의 연결 구성 요소로 통합할 수 있다.)
이음매
데이터 구조
가장 간단한 형식에서 DSU는parent라는 정수 그룹으로 그 중에서 각 i항의 부항은parent[i] 또는 그룹에 저장되며 이러한 관계는 하나 이상의 가상 트리를 만든다.
나무에서 6이 7 아래에 있는 것을 볼 수 있다. 왜냐하면 아버지[6]=7이기 때문이다.또한 2와 3에는 부모 대상이 없다. 왜냐하면 부모 대상[2]=2, 부모 대상[3]=3이기 때문이다.
모든 나무는 서로 교차하지 않는 집합이다.두 요소가 같은 나무에 있으면, 같은 교차하지 않는 집중에 있다.모든 나무의 뿌리 노드(또는 맨 위의 노드)를 집합의 대표라고 부른다.각 조는 모두 독특한 대표가 있다.보시다시피 내가 집합의 대표라면 아버지 [i]=i. 만약 내가 그의 집합의 대표가 아니라면 나무를 따라 위로 이동해서 대표를 찾을 때까지 이동할 수 있다.
단순(천진)한 실현
DSU의 간단한 구현을 작성할 수 있습니다.이것은 비효율적인 것이지만, 다음에 우리는 두 가지 기술을 사용하여 그것을 개선하여 작업 시간을 거의 고정시킬 것이다.
dsu의 세 가지 주요 기능은 다음과 같습니다.
vector < int > parent;
void initSet(int N)
{
parent.assign(N, 0);
for (int i = 0; i < N; i++)
parent[i] = i;
}
int findSet(int i)
{
if (i == parent[i])
return i;
return findSet(parent[i]);
}
void unionSet(int i, int j)
{
i = findSet(i);
j = findSet(j);
if (i != j)
parent[j] = i;
}
그러나 이러한 실현은 낮은 효과로 여겨진다. 큰 나무를 구축하면 서로 다른 체인의 깊이가 더 커지고findSet 함수의 최악의 상황은 O(n)이기 때문이다.효율적인 구현
더욱 효율적인 DSU를 구축하기 위해 경로 압축과 순서대로 조합하는 두 가지 최적화가 있다.
경로 압축
일반적으로findSet을 사용하여 원소 집합을 찾을 때, 귀속 함수는 원소의 전체 부모 체인을 이 집합의 대표나 유효한 부모 체인에 도달할 때까지 옮겨다니며, 이러한 조작의 복잡성은 O(n)로 하여금 원소의 직접적인 조상이 아니라 최고의 조상을 저장하는 것을 피하게 한다.
경로 압축 개념을 적용한 새findSet () 은 다음과 같습니다. 찾은 부모 단계로 돌아가지 않고 이 동작을 실행하기 전에 저장해야 합니다.
unionSet ()가findSet () 를 두 번 호출하고 복잡도 계산에서 무시한 다른 O (1) 작업을 실행하면 경로 압축 개념을 적용한 후의 복잡도는 O (logn) 에 불과하기 때문에findSet () 의 O (logn) 에 집중되었음을 증명합니다.
int findSet (int i) {
if (i == parent[i])
return i;
return parent[i] = findSet (parent[i]);
}
등급별 연합
이전의 개선은findSet () 동작을 변경하여 나무의 높이를 작게 했습니다.이러한 개선을'순차적 합집'(Union by Rank)이라고 하는데, 이는 유니온셋(unionSet) 조작을 바꾸어 나무의 높이를 작게 만든다.그 기본 사상은 두 개의 집합을 연결할 때 비교적 짧은 집합이 비교적 긴 집합의 하위 집합이 되면 나무의 높이가 작아진다는 것이다.
이런 기술은 두 가지 변체가 있는데 하나는 나무의 크기(정수리)를 바탕으로 나무를 정렬하고 다른 하나는 나무의 깊이를 바탕으로 한다.이 예에서 우리는 나무의 깊이와 집합의 질적 변수를 실현할 것이다.
이를 실현하기 위해 각 노드의 질은 처음에 0으로 설정된 다음에 새로운 노드를 추가할 때마다 점차 증가한다.
분류 기술만 사용하는 복잡성은 효과적으로 O(logn)로 감소
vector < int > parent;
vector < int > rank;
void initSet(int N)
{
rank.assign(N, 0); //Rank is now initialized
parent.assign(N, 0);
for (int i = 0; i < N; i++)
parent[i] = i;
}
void unionSet(int i, int j)
{
i = findSet(i);
j = findSet(j);
if (i != j)
{
int x = findSet(i), y = findSet(j);
if (rank[x] > rank[y])
parent[y] = x;
else
{
parent[x] = y;
if (rank[x] == rank[y])
rank[y]++; //Rank update
}
}
}
개선된 복잡성
하나의 경로의 집합 뒤에 하나의 경로의 복잡도를 덧붙여서(또는 하나의 경로의 집합 뒤에 하나의 경로의 복잡도를 덧붙여서)
이 두 가지 기술을 결합하면 복잡성이 고정된 시간에 가까워진다(1)
이 모든 것을 함께 놓아라
응용 경로의 압축과 순서에 따라 집합된 완전한 종류.세 가지 조작이 추가되었음을 주의하십시오
class UnionFind //OOP Style class
{
private:
vector <int>p, rank, setSize;
int numSets;
public:
UnionFind(int N)
{
setSize.assign(N, 1);
numSets = N;
rank.assign(N, 0);
p.assign(N, 0);
for (int i = 0; i < N; i++)
p[i] = i;
}
int findSet(int i) { return (p[i] == i) ? i : (p[i] = findSet(p[i])); }
bool isSameSet(int i, int j) { return findSet(i) == findSet(j); }
void unionSet(int i, int j)
{
if (!isSameSet(i, j))
{
numSets--;
int x = findSet(i), y = findSet(j);
if (rank[x] > rank[y])
{
p[y] = x;
setSize[x] += setSize[y];
}
else
{
p[x] = y;
setSize[y] += setSize[x];
if (rank[x] == rank[y])
rank[y]++;
}
}
}
int numDisjointSets() { return numSets; }
int sizeOfSet(int i) { return setSize[findSet(i)]; }
};</int>
DSU의 일부 응용 프로그램
Reference
이 문제에 관하여(서로 교차하지 않다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ragrag/disjoint-set-union-4393텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)