Количество провинций

Средняя

Описание задачи #

Есть n городов, некоторые из которых соединены между собой.

Если город a напрямую соединен c городом b, а город b напрямую соединен с городом c, тогда города a и c косвенно соединены между собой.

Провинция — это группа напрямую или косвенно связанных между собой городов.

Вам дана матрица isConnected размером n x n, где isConnected[i][j] = 1, если i-й город и j-й город напрямую соединены, и isConnected[i][j] = 0 в противном случае.

Верните общее количество провинций.


Ограничения #


Примеры #

  • Входные данные

    isConnected = [[1,1,0],[1,1,0],[0,0,1]]
    

    Ответ: 2

  • Входные данные

    isConnected = [[1,0,0],[0,1,0],[0,0,1]]
    

    Ответ: 3


Решение #

У этой задачи достаточно много решений и все они достаточно сложные. Но мы можем легко решить задачу, если знать как работает структура данных DisjointSet (также известная как UnionFind).

Подробно про реализацию структуры данных DisjointSet можно посмотреть в этом посте.

Как только мы имеем реализованную структуру данных, задача становится крайне легкой.

Алгоритм решения:

В итоге после перебора элементов матрицы переменная numberOfProvinces будет показывать количество получившихся провинций.

Реализация #

  • package number_of_provinces
    
    type DisjointSet struct {
    	nodes []int
    }
    
    func NewDisjointSet(size int) DisjointSet {
    	nodes := make([]int, size)
    
    	for i := 0; i < size; i++ {
    		nodes[i] = i
    	}
    
    	return DisjointSet{
    		nodes: nodes,
    	}
    }
    
    func (uf *DisjointSet) find(node int) int {
    	return uf.nodes[node]
    }
    
    func (uf *DisjointSet) union(x int, y int) {
    	rootX := uf.find(x)
    	rootY := uf.find(y)
    
    	if rootY != rootX {
    		for i := range uf.nodes {
    			if uf.nodes[i] == rootY {
    				uf.nodes[i] = rootX
    			}
    		}
    	}
    }
    
    func findCircleNum(isConnected [][]int) int {
    	n := len(isConnected)
    	disjointSet := NewDisjointSet(n)
    
    	numberOfProvinces := n
    
    	for i := 0; i < n; i++ {
    		for j := i + 1; j < n; j++ {
    			if isConnected[i][j] == 1 && disjointSet.find(i) != disjointSet.find(j) {
    				numberOfProvinces -= 1
    				disjointSet.union(i, j)
    			}
    		}
    	}
    
    	return numberOfProvinces
    }

  • class DisjointSet {
        readonly nodes: number[];
    
        constructor(size: number) {
            this.nodes = new Array(size);
    
            for (let i = 0; i < size; i++) {
                this.nodes[i] = i;
            }
        }
    
        find(node: number): number {
            return this.nodes[node];
        }
    
        union(x: number, y: number): void {
            const rootX = this.find(x);
            const rootY = this.find(y);
    
            if (rootX !== rootY) {
                for (let i = 0; i < this.nodes.length; i++) {
                    if (this.nodes[i] === rootY) {
                        this.nodes[i] = rootX;
                    }
                }
            }
        }
    }
    
    export const findCircleNum = (isConnected: number[][]): number => {
        const n = isConnected.length;
        const disjointSet = new DisjointSet(n);
    
        let numberOfProvinces = n;
    
        for (let i = 0; i < n; i++) {
            for (let j = i + 1; j < n; j++) {
                if (isConnected[i][j] == 1 && disjointSet.find(i) != disjointSet.find(j)) {
                    numberOfProvinces -= 1;
                    disjointSet.union(i, j);
                }
            }
        }
    
        return numberOfProvinces
    }

Оценка сложности #

По времени

Общая сложность по памяти O(n2).

По памяти

Сложность O(n) для хранения состояния структуры DisjointSet.

Оригинал задачи на Leetcode
< Предыдущая задача