Macierz sąsiedztwa

Plusy:

  • dodanie / usuwanie / sprawdzenie istnienia krawędzi w O(1)
    Wady:
  • złożoność pamięciowa O(n
  • przetworzenie wszystkich krawędzi incydentnych z wierzchołkiem w O(n)

Lista Sąsiedztwa

Plusy:

  • złożoność pamięciowa O(m) a nie O(n
  • Przetwarzanie krawędzi incydentnych z wierzchołkiem w O(d)
    Wady:
  • Dostęp do krawędzi (sprawdzenie) pesymistycznie w O(d)

DFS

function DFS(G, v){
	visited[v] = true;
	foreach(var u in N(v)){
		if(!visited[u]){
			DFS(G, u)
		}
	}
}

BFS

function BFS(v){
	q = new Queue<int>();
	q.add(v);
	visited[v] = true;
	while(q.count > 0){
		int w = q.pop();
		foreach(var u in N(w)){
			if(!visited[u]){
				q.add(u);
				visited[u] = true;
			}
		}
	}
}

jest w kolejce <=> jest oznaczony visited

ZAD 1

a) Lista sąsiedztwa zapewni zarówno przejrzenie krawędzi wychodzących z v w czasie O(deg(v)) jak i złożoność pamięciową O(m). Natomiast operacje na pojedynczej krawędzi w O(log(n)) musi przyspieszyć dodatkowa struktura, np. lista sąsiedztwa w postaci drzewa zrównoważonego, np. AVL. Można trzymać drzewo zamiast listy;
b) Lista sąsiedztwa trzymana w postaci listy łączonej, insert na początku w O(1), przejście przez linked listę w O(deg(v)), złożoność pamięciowa O(m).
c) Jako że nie ma wymogów pamięciowych poza macierzą sąsiedztwa można obok trzymać listę sąsiedztwa, oba warunki spełnione
d) Potrzebne są dwie struktury:

  • lista sąsiedztwa -> rozwiązuje przejrzenie wszystkich sąsiadów w O(d), pamięciowo O(m)
  • tablica hashująca -> trzyma zapisane tuple (u,v), które reprezentują istnienie krawędzi w grafie, poza tym ma funkcję hashującą. Optymistycznie insert + checkw O(1), jeśli nie ma kolizji hashy. W węźle, w którym przetrzymywana jest ta tupla znajduje się wskaźnik na odpowiednik w liście sąsiedztwa, co pozwala na delete w O(1).

ZAD 2

function nonRecDFS(v){
	var s = new Stack<int>();
	s.add(v);
	
	while(s.count > 0){
		var w = s.pop();
		if(visited[w]) continue;
		visited[w] = true;
		foreach(var u in N(w)){
			if(!visited[u]){
				s.add(u);
			}
		}
	}
}

tutaj markowanie na visited musi być poza pętla dla sąsiadów, wierzchołek ma prawo być wielokrotnie w stacku ale musi zostać sprawdzony

ZAD 3

function BFS(v){
	visited[v] = true;
	var q = new queue<int>();
	q.push(v);
	while (q.count > 0){
		var w = q.pop();
		foreach(var u in N(w)){
			if(!visited[u]){
				q.add(u);
				visited[u] = true;
			}
		}
	}
}

ZAD 4

Rozwiązanie to odpalenie BFS z każdego wierzchołka. Odpalając z każdego nowego wierzchołka dodajemy jedną spójną składową (jeśli BFS się zakończył a wierzchołek nie był odwiedzony, to oznacza że niemożliwe było dotarcie do niego z poprzedniego wierzchołka startowego => nowy wierzchołek znajduje się w innej spójnej składowej).

function ZnajdzIloscSpojnychSkladowych(G){
	visited = new bool[n];
	int count = 0;
	for(int i = 0; i < n; i++){
		if(!visited[i]){
			count++;
			BFS(i);
		}
	}
	return count;
}
 
function BFS(v){
	var q = new queue<int>();
	q.push(v);
	visited[v] = true;
	while(q.count > 0){
		var w = q.pop();
		foreach(var u in N(w)){
			if(!visited[u]){
				visited[u] = true;
				q.add(u);
			}
		}
	}
}

Każdy wierzchołek jest dokładany do kolejki dokładnie raz. Dlatego że BFSY dzielą tabelę visited, pokrywają każdą składową dokładnie raz. O(m);

ZAD 5

Graf jest dwudzielny < = > Graf jest dwukolorowalny

function ZnajdzCzyDwudzielny(G){
	c = new int[n];
	foreach(int i = 0; i < n; i++){
		if(c[i] == 0){
			if(DFSColor(i, 1) == false)
				return false;
		}
	}
	return true;
}
 
 
function DFSColor(v, color){
	int other = color == 1 ? 2 : 1;
	
	c[v] = color;
	foreach(var u in N(v)){
		if(c[u] == 0){
			if(DFSColor(u,other) == false) 
				return false;
		}
		else if(c[u] == color)
			return false;
	}
	return true;
}
 

Każdy wierzchołek koloruję dokładnie raz, ilość krawędzi odwiedzonych pomnożona przez stałą. O(n + m);

ZAD 6

Na moje to jest po prostu DFS z post-order insertem

function TopologicalSort(G){
	order = new list<int>
	visited = new int[n];
	for(int i = 0; i < n; i++){
		if(!visited[i]){
			DFS(i);
		}
	}
	return order.reverse();
}
 
function DFS(v){
	visited[v] = true;
	foreach(var u in N(v)){
		if(!visited[u]){
			DFS(u);
		}
	}
	order.add(v);
}

ZAD 7

 
function findEulerCycle(G){
	var order = new list<int>();
	start = -1;
	for(int i = 0; i < n; i++){
		if(g.degree(i) > 0)
			start = i;
	}
	int used = 0;
	int m = g.Edgecount;
	
	if(start == -1)
		return null;
 
	DFS(start);
	
	if(used == m){
		return order;
	}
	else 
		return null;
}
 
function DFS(v){
	visited[v] = true;
	foreach(var e in N(v)){
		var u = e.To;
		G.remove(e);
		used++;
		DFS(u);
	}
	order.add(v);
}