---
# System prepended metadata

title: Zajęcia 10.03.2021
tags: [xivlo]

---

###### tags: `xivlo`

# Zajęcia 10.03.2021

## Notatka
Chcemy utrzymywać strukturę zbiorów rozłącznych, tj. mamy $n$ liczb (od $1$ do $n$):

- $find(x)$ → identyfikator zbioru w którym jest $x$
- $union(x,y)$ → złącz zbiory w których są $x$ i $y$
- $makeSet(n)$ → stwórz strukturę, gdzie każdy element jest w osobnym (jednoelementowym) zbiorze


> $find(x)$ == $find(y)$ sprawdza czy $x$ i $y$ są w tym samym zbiorze

### Identyfikatory zbiorów
Każdy zbiór będzie posiadał jeden wyróżniony element, który pozwoli nam identyfikować ten zbiór - będziemy go nazywać __reprezentantem zbioru__.

Jeśli elementy zbioru to liczby od $1$ do $n$, to reprezentantów możemy zapisać w tablicy `repr[]`, gdzie dla elementu $x$ zajrzymy w miejsce `repr[x]` i na podstawie tej informacji, podejmiemy dalszą analizę (tj. zajrzymy w `repr[repr[x]]`) lub zwrócimy wynik. Reprezentantem jest element $y$, dla którego zachodzi `repr[y]==y`. 

### Implementacja makeSet
Przykład dla zbioru wielkości 10.
```cpp=
int repr[11];

void makeSet(int n){
    for(int i=1; i<=10; i++){
        repr[i]=i;
    }
}
```
> Na początku każdy jest swoim reprezentantem.

### Implementacja Find (gorzej)
```cpp=
int Find(int x){
    if(repr[x]==x)
        return x;
    return Find(repr[x]);
}
```

### Implementacja Union (gorzej)
```cpp=
void Union(int x, int y){
    int rx = Find(x);
    int ry = Find(y);
    
    repr[rx] = ry;
}
```

### Find (lepiej) - optymalizacja ścieżek
Pierwsza implementacja, może doprowadzić do sytuacji, że utworzone długie zależności w tablicy reprezentantów, będą wymagały liniowego czasu przy każdym wywołaniu funkcji Find. To bardzo niedobrze. Możemy to naprawić, wymagając, by funkcja Find wracając z wynikiem (numerem reprezentanta zbioru) aktualizowała/*przepinała* elementy wprost pod reprezentanta.

Przykład:
![](https://i.imgur.com/IdKj7jb.png)

Wywołujemy naprawiony `Find(x)` na powyższym zbiorze i otrzymujemy naprawioną tablicę `repr`, gdzie elementy $X,D,C,B$ i $A$ mają teraz na swoich pozycjach w tablicy `repr` wartość $Z$:
![](https://i.imgur.com/O9R925X.png)


#### Implementacja
```cpp=
int Find(int x){
    if(repr[x]==x)
        return x;
    repr[x] = Find(repr[x])
    return repr[x];
}
```

### Union (lepiej) - rangi
Nasz ulepszony Find 'przepina' elementy zbioru bezpośrednio do reprezentanta zbioru. Mając do wyboru połączenie dwóch zbiorów, chcemy połączyć mniejszy do większego, a nie na odwrót - w ten sposób będziemy mieli mniej do 'przepinania'. By wiedzieć, który zbiór jest większy, dodamy do naszego programu tablicę Rank, utrzymującą wielkości zbiorów, których reprezentantami są dane elementy.

> Po wykonaniu funkcji makeSet, Rank każdego elementu jest równy zero. Gdy łączymy dwa zbiory o równych 'rangach', reprezentantowi złączonych zbiorów zwiększami rangę.

#### Implementacja

```cpp=
#include <algorithm> //do funkcji max
// mamy tablicę rank, posiadającą informację o 'wielkości' zbioru
//...

void Union(int x, int y){ 
    int rx = Find(x);
    int ry = Find(y);
    
    //jeśli zbiory połączone to kończymy
    if (rx == ry)
        return;
    
    if(Rank[rx] < Rank[ry]){
        repr[rx] = ry;
    }
    else{ 
        repr[ry] = rx;
        //można tak
        Rank[rx] = max(Rank[rx], Rank[ry]+1);
        // lub tak
        // if(Rank[rx] == Rank[ry])
        //     Rank[rx]++;
    }
}

```