File tree Expand file tree Collapse file tree 5 files changed +173
-0
lines changed
data-structure-and-algorithms/algorithms/Union-Find Expand file tree Collapse file tree 5 files changed +173
-0
lines changed Original file line number Diff line number Diff line change 1+ // Union - O(n)
2+ // Find - O(1)
3+ // Init - O(n)
4+ export class QuickFindUF {
5+ private id : number [ ] ;
6+ private size : number ;
7+
8+ constructor ( n : number ) {
9+ this . size = n ;
10+ this . id = new Array ( n ) . fill ( 0 ) . map ( ( _ , i ) => i ) ;
11+ }
12+
13+ public connected ( p : number , q : number ) : boolean {
14+ return this . id [ p ] === this . id [ q ] ;
15+ }
16+
17+ public union ( p : number , q : number ) : void {
18+ const newId = this . id [ q ] ;
19+ const previousId = this . id [ p ] ;
20+
21+ for ( let i = 0 ; i < this . size ; i ++ ) {
22+ if ( this . id [ i ] === previousId ) {
23+ this . id [ i ] = newId ;
24+ }
25+ }
26+ }
27+ }
Original file line number Diff line number Diff line change 1+ // Union - O(n)
2+ // Find - O(1)
3+ // Init - O(n)
4+ export class QuickUnionUF {
5+ private id : number [ ] ;
6+ private size : number ;
7+
8+ constructor ( n : number ) {
9+ this . size = n ;
10+ this . id = new Array ( n ) . fill ( 0 ) . map ( ( _ , i ) => i ) ;
11+ }
12+
13+ public connected ( p : number , q : number ) : boolean {
14+ return this . root ( p ) === this . root ( q ) ;
15+ }
16+
17+ public union ( p : number , q : number ) : void {
18+ const newId = this . id [ q ] ;
19+ const previousId = this . id [ p ] ;
20+
21+ for ( let i = 0 ; i < this . size ; i ++ ) {
22+ if ( this . id [ i ] === previousId ) {
23+ this . id [ i ] = newId ;
24+ }
25+ }
26+ }
27+
28+ private root ( node : number ) : number {
29+ // chase parent pointers until reaches root
30+ while ( this . id [ node ] !== node ) node = this . id [ node ] ;
31+ return node ;
32+ }
33+ }
Original file line number Diff line number Diff line change 1+ // Union - O(n)
2+ // Find - O(1)
3+ // Init - O(n)
4+ export class QuickUnionUF {
5+ private id : number [ ] ;
6+ private size : number ;
7+
8+ constructor ( n : number ) {
9+ this . size = n ;
10+ this . id = new Array ( n ) . fill ( 0 ) . map ( ( _ , i ) => i ) ;
11+ }
12+
13+ public connected ( p : number , q : number ) : boolean {
14+ return this . root ( p ) === this . root ( q ) ;
15+ }
16+
17+ public union ( p : number , q : number ) : void {
18+ const newId = this . id [ q ] ;
19+ const previousId = this . id [ p ] ;
20+
21+ for ( let i = 0 ; i < this . size ; i ++ ) {
22+ if ( this . id [ i ] === previousId ) {
23+ this . id [ i ] = newId ;
24+ }
25+ }
26+ }
27+
28+ private root ( node : number ) : number {
29+ // chase parent pointers until reaches root
30+ while ( this . id [ node ] !== node ) {
31+ this . id [ node ] = this . id [ this . id [ node ] ]
32+ node = this . id [ node ] ;
33+ }
34+ return node ;
35+ }
36+ }
Original file line number Diff line number Diff line change 1+ // Running Time
2+ // Find - takes time proportional to depth of p and q - lg N
3+ // Union - takes constant time, given roots, lg N
4+ // Depth of any node is at most lg N where lg = base-2 logarithm
5+ export class QuickUnionUF {
6+ private id : number [ ] ;
7+ private size : number ;
8+ private nodeSize : number [ ] ;
9+
10+ constructor ( n : number ) {
11+ this . size = n ;
12+ this . id = new Array ( n ) . fill ( 0 ) . map ( ( _ , i ) => i ) ;
13+ this . nodeSize = new Array ( n ) . fill ( 0 ) . map ( ( ) => 1 ) ;
14+
15+ }
16+
17+ public connected ( p : number , q : number ) : boolean {
18+ return this . root ( p ) === this . root ( q ) ;
19+ }
20+
21+ public union ( p : number , q : number ) : void {
22+ const i = this . id [ q ] ;
23+ const j = this . id [ p ] ;
24+
25+ for ( let i = 0 ; i < this . size ; i ++ ) {
26+ if ( this . nodeSize [ i ] < this . nodeSize [ j ] ) {
27+ this . id [ i ] = j ;
28+ this . nodeSize [ j ] += this . nodeSize [ i ] ;
29+ } else {
30+ this . id [ j ] = i ;
31+ this . nodeSize [ i ] += this . nodeSize [ j ] ;
32+ }
33+ }
34+ }
35+
36+ private root ( node : number ) : number {
37+ // chase parent pointers until reaches root
38+ while ( this . id [ node ] !== node ) node = this . id [ node ] ;
39+ return node ;
40+ }
41+ }
Original file line number Diff line number Diff line change 1+ # Union-Find
2+
3+ ## Dynamic Connectivity
4+
5+ Given a set of n objects:
6+
7+ - Union command - connect two objects
8+ - Find / connected query - is there a path connecting two nodes?
9+
10+ ## Quick-Find (eager approach)
11+
12+ - Integer array id[ ] of size N
13+ - Interpretation: p and q are connect if they have the same id.
14+
15+ ## Quick Union
16+
17+ - Integer array id[ ] of size N
18+ - Interpretation: id [ i] is parent of i.
19+ - Root of i is id[ id[ id[ ...id[ i] ...]]] ( keep going untill it doesnt change ) - algorithm ensures no cycle.
20+ - Find - Check if p and q have same root.
21+ - Union - To merge components containing p and q, set the id of p's root to the id of q's root.
22+
23+ ## Quick Union Improvement - Weighing Quick-Union
24+
25+ - Modify quick-union to avoid tall trees.
26+ - keep track of size of each tree.
27+ - Balance by linking root of smaller tree to root of larger tree.
28+
29+ ## Quic Union Improvement 2 - Path Compression
30+
31+ - Just after computing the root of p, set the id of each examined node to point to that root.
32+
33+ ## Weighted Quick-Union with path compression
34+
35+ - Starting from an empty data structure, any sequence of M union-find ops on N objects makes =< c(N + Mlog* N) array access.
36+ - Analysis can be improved to N + M @a (M,N).
You can’t perform that action at this time.
0 commit comments