Από το L06 ξέρουμε τι είναι ένα γράφημα και πώς το αποθηκεύουμε (λίστα γειτνίασης). Ξέρουμε όμως μόνο να το περιγράφουμε — όχι να απαντάμε ερωτήσεις πάνω του:
s-t συνεκτικότητα: υπάρχει διαδρομή από την κορυφή s στην κορυφή t;
s-t απόσταση: ποιο είναι το μήκος της συντομότερης διαδρομής από την s στην t;
Αυτές οι δύο ερωτήσεις κρύβονται παντού: λύσιμο λαβυρίνθου, «έξι βαθμοί του Kevin Bacon», ελάχιστος αριθμός ενδιάμεσων κόμβων σε ένα δίκτυο. Για να τις απαντήσουμε χρειαζόμαστε διάσχιση — έναν συστηματικό τρόπο να επισκεφθούμε κάθε κορυφή. Θα δούμε τις δύο θεμελιώδεις τεχνικές:
DFS (Depth-First Search) — αναζήτηση κατά βάθος. Πας όσο πιο μακριά μπορείς, μετά γυρνάς πίσω και δοκιμάζεις άλλο δρόμο.
BFS (Breadth-First Search) — αναζήτηση κατά πλάτος. Εξερευνείς επίπεδα: πρώτα όλους τους γείτονες, μετά τους γείτονες των γειτόνων, κ.ο.κ.
Και οι δύο τρέχουν σε χρόνο O(n+m) με λίστα γειτνίασης. Λύνουν την s-t συνεκτικότητα· επιπλέον ο BFS λύνει και την s-t απόσταση σε γραφήματα χωρίς βάρη.
Σε όλη τη διάλεξη δουλεύουμε πάνω στο ίδιο γράφημα — αυτό από το L06:
Το γράφημα-παράδειγμα: 8 κορυφές, 11 ακμές. Θα το διασχίσουμε ξανά και ξανά, ξεκινώντας πάντα από την κορυφή 1.
DFS — Αναζήτηση κατά βάθος
Η ιδέα
Φαντάσου ότι στέκεσαι στην κορυφή s ενός λαβύρινθου. Διαλέγεις μία κατεύθυνση και προχωράς. Φτάνεις σε νέα κορυφή, διαλέγεις πάλι μία κατεύθυνση που δεν έχεις δοκιμάσει, προχωράς. Συνεχίζεις μέχρι να βρεθείς σε αδιέξοδο — μια κορυφή χωρίς νέους δρόμους. Τότε γυρνάς πίσω (backtrack) στην προηγούμενη κορυφή και δοκιμάζεις άλλη κατεύθυνση.
Ο αλγόριθμος
ΑλγόριθμοςDFS — Αναζήτηση κατά βάθος
O(n + m)
Βυθίσου όσο πιο βαθιά μπορείς· όταν κολλήσεις, κάνε backtrack.
Είσοδος:
γράφημα G (λίστα γειτνίασης), αφετηρία s
Έξοδος:
όλες οι κορυφές που φτάνεις από την s — η συνεκτική της συνιστώσα
Με λόγια. Κρατάμε για κάθε κορυφή μια ένδειξη «την έχω εξερευνήσει;». Ξεκινώντας από την s:
Σημαδεύουμε την τρέχουσα κορυφή ως εξερευνημένη.
Διαλέγουμε έναν μη-εξερευνημένο γείτονα και κατεβαίνουμε — κάνουμε αναδρομική κλήση από εκεί.
Όταν επιστρέψει η αναδρομή, συνεχίζουμε με κάποιον άλλο μη-εξερευνημένο γείτονα.
Όταν δεν μένει κανείς, η κλήση τελειώνει και ο έλεγχος γυρνά στην κορυφή που μας κάλεσε (backtrack).
Αυτή η περιγραφή — «βυθίσου, κι όταν κολλήσεις γύρνα πίσω» — είναι αρκετή για να λύσεις άσκηση. Ο ψευδοκώδικας απλώς την κάνει ακριβή:
Μια πιο γενική ματιά. Στις σημειώσεις, ο DFS εμφανίζεται πρώτα σε μια ακόμη πιο απλή, γενική μορφή — τον λεγόμενο R-set algorithm. Κρατάμε ένα σύνολο R από κορυφές που έχουμε «πιάσει», και όσο υπάρχει ακμή που βγαίνει έξω από το R, τραβάμε τη νέα κορυφή μέσα:
Τώρα γίνε εσύ ο αλγόριθμος. Σε κάθε βήμα διάλεξε όποιον μη-εξερευνημένο γείτονα θέλεις — ο DFS δουλεύει με κάθε επιλογή. Η στοίβα αναδρομής ενημερώνεται ζωντανά και κάνει backtrack μόνη της στα αδιέξοδα. (Με «Παρακολούθησε» τρέχει μόνο του.)
Γίνε ο αλγόριθμος — DFS
Στοίβα αναδρομήςΔεξιά η κορυφή όπου βρίσκεσαι· σε αδιέξοδο η στοίβα μικραίνει (backtrack).
(άδεια)
Κάνε κλικ στην αφετηρία s = 1 για να ξεκινήσεις.
Βήμα 0 / 8
Το «κρυφό» αποτέλεσμα — το DFS-δέντρο
Όσο τρέχει η αναδρομή, σχηματίζει σιωπηλά ένα δέντρο. Κάθε φορά που κατεβαίνουμε από την u σε νέα κορυφή v, η ακμή {u,v} γίνεται ακμή του δέντρου. Οι υπόλοιπες ακμές του γραφήματος ονομάζονται οπίσθιες ακμές — και (στο μη-κατευθυνόμενο DFS) η καθεμιά τους κλείνει κύκλο πίσω σε πρόγονο της τρέχουσας κορυφής στο δέντρο.
Πάτα βήμα-βήμα κι άσε το DFS να ζωγραφίσει μόνο του τις δύο πλευρές — αριστερά η σάρωση πάνω στο G, δεξιά το δέντρο που γεννιέται· και δίπλα τους η ζωντανή στοίβα αναδρομής.
Το «κρυφό» αποτέλεσμα του DFS — το DFS-δέντρο
Πορεία από κορυφή 1, γείτονες σε αύξουσα σειρά
Γράφημα G
DFS-δέντρο T
Στοίβα αναδρομήςδεξιά = top
1
Ξεκινάμε από την κορυφή 1 — αυτή είναι η ρίζα του DFS-δέντρου. Πάτα «Βήμα» ή ▶.
Συμβάν 0 / 19
Γιατί μας ενδιαφέρει αυτή η διάκριση «δεντρική ακμή vs οπίσθια ακμή»; Στο L08 το ίδιο εργαλείο γίνεται ορισμός των strongly connected components σε κατευθυνόμενα γραφήματα, και στο L09 πιάνει τα ελάχιστα συνδετικά δέντρα. Μην κουραστείς να την αποστηθίζεις τώρα — απλώς πρόσεξε πως ο DFS γεννά δομή, δεν επισκέπτεται μόνο.
Κλείδωσέ το στο μυαλό σου
Πρώτα η συμπύκνωση — δεν αποστηθίζεις σελίδα, αποστηθίζεις μια κάρτα:
2Πήγαινε σε έναν μη-εξερευνημένο γείτονα — όποιον — με αναδρομική κλήση.
3Δεν υπάρχει; Κάνε backtrack στην κορυφή απ’ όπου ήρθες.
4Τελείωσες όταν γυρίσεις στην s χωρίς νέους γείτονες.
Πολυπλοκότητα
O(n + m)
Κλασική παγίδα
Ξεχνάς να σημαδέψεις την κορυφή πριν την αναδρομή → ο DFS ξαναμπαίνει σ’ αυτήν και κολλάει σε ατέρμονη αναδρομή πάνω στους κύκλους.
Τώρα η ανάκληση. Μην προχωρήσεις αν δεν τα κάνεις χωρίς να κοιτάς πίσω:
Συμπλήρωσε τα κενά
Συμπλήρωσε τα κενά του αναδρομικού DFS.
DFS(G, u):
Εξερευνημένη(u) =
για κάθε γείτονα v της u:
εάν Εξερευνημένη(v) = :
DFS(G, )
Βάλε τα βήματα στη σειρά
Βάλε τα βήματα του DFS στη σωστή σειρά.
Σύρε τις γραμμές για αναδιάταξη — ή χρησιμοποίησε τα βελάκια.
1.Αν δεν υπάρχει μη-εξερευνημένος γείτονας, κάνε backtrack.
2.Σταμάτα όταν επιστρέψεις στην s και δεν μένει κανείς.
3.Διάλεξε έναν μη-εξερευνημένο γείτονα της τρέχουσας κορυφής.
4.Κατέβα σ’ αυτόν με αναδρομική κλήση και επανέλαβε από εκεί.
5.Ξεκίνα στην αφετηρία s και σημάδεψέ την «εξερευνημένη».
Πότε σκέφτεσαι DFS;
BFS — Αναζήτηση κατά πλάτος
Η ιδέα
Φαντάσου τώρα ότι ρίχνεις μια πέτρα στην κορυφή s μιας λίμνης. Ένα κύμα εξαπλώνεται προς όλες τις κατευθύνσεις ταυτόχρονα. Σε χρόνο 1 το κύμα φτάνει στους άμεσους γείτονες της s. Σε χρόνο 2 στους γείτονες των γειτόνων. Και ούτω καθεξής.
Ο αλγόριθμος
Επίσημα, από την s φτιάχνουμε επίπεδα:
L0={s} — μόνο η αφετηρία.
L1 = όλοι οι γείτονες του L0.
Li+1 = όλες οι κορυφές που δεν ανήκουν σε προηγούμενο επίπεδο και έχουν ακμή προς κάποια κορυφή του Li.
Πριν πάρεις το θεώρημα στα πιστά, διάλεξε εσύ μια κορυφή-στόχο και δες το να συμβαίνει. Στα αριστερά το BFS-«κύμα» απλώνεται επίπεδο-ανά-επίπεδο· στα δεξιά υπολογίζουμε αυτόνομα τη συντομότερη διαδρομή προς τον στόχο σου. Όταν το κύμα τον αγκαλιάσει, οι δύο αριθμοί συμπίπτουν: το επίπεδο εμφάνισης = η απόσταση. Δοκίμασε διαφορετικούς στόχους — η εξίσωση κρατάει.
Θεώρημα — Li = όλες οι κορυφές σε απόσταση i
Διάλεξε στόχο t, παρακολούθησε το BFS-«κύμα» και σύγκρινε
Στόχος t =
BFS-«κύμα» — επίπεδα L₀, L₁, L₂, L₃
Συντομότερη διαδρομή 1 → t
1→2→5→6
Μήκος (πλήθος ακμών) = 3 — αυτή είναι η απόσταση d(1, 6).
Το κύμα δεν έχει φτάσει ακόμα την t = 6. Είμαστε στο βήμα 0 / 4. Συνέχισε με «Επόμενο επίπεδο» — η στόχος-κορυφή έχει απόσταση 3, άρα θα ενεργοποιηθεί στο βήμα 3.
«Γιατί δεν εμφανίζεται νωρίτερα;» Κάθε κορυφή απόστασης d έχει την πιο σύντομη διαδρομή της με d ακμές. Το BFS-κύμα προχωράει μία ακμή ανά βήμα — άρα στο βήμα i < d δεν υπάρχει τρόπος να την έχει «αγγίξει» ακόμα.
«Γιατί δεν αργεί;» Στο βήμα d−1 το κύμα έχει αγγίξει ΟΛΟΥΣ τους γείτονες της t που απέχουν d−1. Στο επόμενο βήμα το κύμα τους σαρώνει — η t εντοπίζεται τότε. Άρα όχι αργότερα από το d.
Επίπεδο 0 / 4
Δες τα ίδια επίπεδα ως ενιαία εικόνα στο γράφημά μας — αυτή τη φορά με τις 7 ακμές του BFS-δέντρου ξεχωρισμένες:
Το BFS-δέντρο με ρίζα την 1. Συμπαγείς ακμές = ακμές του δέντρου· διακεκομμένες = ακμές εκτός δέντρου. Παρατήρησε: καμία ακμή δεν πηδά επίπεδο.
ΑλγόριθμοςBFS — Αναζήτηση κατά πλάτος
O(n + m)
Εξερεύνησε επίπεδο-επίπεδο, σαν κύμα που απλώνεται από την s.
Είσοδος:
γράφημα G (λίστα γειτνίασης), αφετηρία s
Έξοδος:
για κάθε κορυφή: η απόσταση από την s και ο γονέας π[v] στο BFS-δέντρο
Με λόγια. Χτίζουμε τα επίπεδα ένα-ένα. Έχοντας το επίπεδο Li, σαρώνουμε κάθε κορυφή του και κοιτάμε τους γείτονές της: όποιον δεν έχουμε ξαναδεί τον σημαδεύουμε και τον ρίχνουμε στο επόμενο επίπεδο Li+1. Σταματάμε όταν ένα επίπεδο βγει άδειο. Παράλληλα κρατάμε για κάθε κορυφή v τον γονέα της π[v] — έτσι μπορούμε ν' ανακατασκευάσουμε τη συντομότερη διαδρομή.
Η εκδοχή με ουρά. Αντί για πίνακα από λίστες, ο BFS γράφεται πιο φυσικά με μια ουρά FIFO (first-in, first-out). Είναι μαθηματικά ισοδύναμο — και είναι ο τρόπος που τον σκεφτόμαστε:
Γίνε ο αλγόριθμος. Σε κάθε βήμα κάνε κλικ στην κορυφή που βρίσκεται στην αρχή της ουράς — η ουρά ενημερώνεται ζωντανά. (Σύμβαση: τους νέους γείτονες τους βάζουμε στην ουρά με αύξουσα σειρά, ώστε η σειρά να είναι προβλέψιμη.)
Γίνε ο αλγόριθμος — BFS
Ουρά (FIFO)Η επόμενη κορυφή βγαίνει από τα αριστερά· οι νέες μπαίνουν δεξιά.
1
Κάνε κλικ στην αφετηρία s = 1 για να ξεκινήσεις.
Βήμα 0 / 8
Από το παράδειγμα προκύπτουν τα επίπεδα L0={1}, L1={2,3}, L2={4,5,7,8}, L3={6} — άρα οι αποστάσεις από την 1:
Κορυφή
1
2
3
4
5
6
7
8
Απόσταση
0
1
1
2
2
3
2
2
Δοκίμασέ τη με τα ίδια σου τα χέρια. Στην πρώτη καρτέλα σαρώνεις και τις 11 ακμές μία-μία — η μετρητής Δlevel ανεβαίνει σε δύο στήλες (Δ=1 για δεντρικές, Δ=0 για ίδιο-επίπεδο) και ΠΟΤΕ στη στήλη Δ≥2. Στη δεύτερη καρτέλα είσαι ο «αντίπαλος»: διαλέγεις δύο κορυφές και βλέπεις γιατί καμιά πραγματική ακμή δεν μπορεί να έχει Δ≥2 — αν υπήρχε, το BFS θα είχε χτυπήσει την πιο μακρινή κορυφή ένα επίπεδο νωρίτερα.
Καμία ακμή του G δεν πηδά επίπεδο στο BFS-δέντρο
Δ = 1
0
δέντρο
Δ = 0
0
ίδιο επίπεδο
Δ ≥ 2
0
πήδημα
Πάτα «Επόμενη ακμή» για να ξεκινήσει η σάρωση.
0 / 11 ακμές
Κλείδωσέ το στο μυαλό σου
Κάρτα μνήμης — BFS
Λέξεις-κλειδιά
ουρά (FIFO)επίπεδα L₀, L₁, …απόστασηBFS-δέντροσημάδεψε στην εισαγωγή
Τα βήματα στο μυαλό σου
1Βάλε την αφετηρία s στην ουρά και σημάδεψέ την.
2Βγάλε την κορυφή από την αρχή της ουράς (FIFO).
3Σημάδεψε τους νέους γείτονές της και βάλ’ τους στο τέλος της ουράς.
4Επανέλαβε ώσπου η ουρά να αδειάσει.
Πολυπλοκότητα
O(n + m)
Κλασική παγίδα
Σημαδεύεις μια κορυφή όταν τη βγάζεις από την ουρά αντί όταν τη βάζεις → η ίδια κορυφή μπαίνει πολλές φορές, η σειρά μπερδεύεται και χάνεται το O(n+m).
Συμπλήρωσε τα κενά
Συμπλήρωσε τα κενά του BFS με ουρά. Πρόσεξε ποιο άκρο της ουράς είναι ποιο.
BFS(G, s):
ουρά Q ← { }· Αναγνωσμένη(s) = true
όσο η Q δεν είναι :
u ← βγάλε από την της Q
για κάθε γείτονα v του u με Αναγνωσμένη(v) = false:
Αναγνωσμένη(v) =
βάλε το v στο της Q
Βάλε τα βήματα στη σειρά
Βάλε τα βήματα του BFS στη σωστή σειρά.
Σύρε τις γραμμές για αναδιάταξη — ή χρησιμοποίησε τα βελάκια.
1.Βγάλε την κορυφή u από την αρχή της ουράς.
2.Βρες τους γείτονες του u που δεν έχουν σημαδευτεί.
3.Επανέλαβε όσο η ουρά δεν είναι άδεια.
4.Σημάδεψέ τους και βάλ’ τους στο τέλος της ουράς.
5.Βάλε την αφετηρία s στην ουρά και σημάδεψέ την.
Πότε σκέφτεσαι BFS;
BFS vs DFS — δύο όψεις του ίδιου αλγορίθμου
BFS
DFS
Στρατηγική
επίπεδο-επίπεδο
βάθος πρώτα
Δομή δεδομένων
ουρά (FIFO)
στοίβα (LIFO) ή αναδρομή
Λύνει
s-t απόσταση (χωρίς βάρη)
s-t συνεκτικότητα
Πολυπλοκότητα
O(n+m)
O(n+m)
Δέντρο που παράγει
BFS-δέντρο: ίδιο επίπεδο = ίδια απόσταση
DFS-δέντρο: μακριές αλυσίδες
Τυπικές εφαρμογές
shortest paths (unweighted), διμερότητα, web crawler
τοπολογική ταξινόμηση, SCC, εύρεση κύκλων
Ο πίνακας δείχνει τις διαφορές. Αλλά το πιο σημαντικό είναι η ομοιότητα:
Δες το με τα μάτια σου: τρέξε τον ίδιο αλγόριθμο στο ίδιο γράφημα, και άλλαξε μόνο τη δομή δεδομένων. Στην FIFO καρτέλα η σειρά ανακάλυψης είναι 1,2,3,4,5,7,8,6 (BFS). Στη LIFO γίνεται 1,2,3,5,4,6,7,8 (DFS) — μακρύ μονοπάτι με backtracks. Στην «Ελεύθερη» καρτέλα εσύ διαλέγεις ποια κορυφή από το frontier θα τραβήξεις, σε όποια σειρά θες· κι όμως, στο τέλος, η ίδια οκτάδα κορυφών είναι μέσα.
Γενική αναζήτηση — η δομή δεδομένων ορίζει τον αλγόριθμο
Επόμενη έξοδος ← αριστερά. Νέοι γείτονες μπαίνουν δεξιά (πίσω).
ΟυράΟυρά (FIFO) → BFS
→ έξοδος
Σειρά ανακάλυψης
1
Αρχή: Q = [1] — η αφετηρία περιμένει στην ουρά. Πάτα «Βήμα» ή ▶ για να βγει η πρώτη κορυφή από την αρχή της ουράς.
Βήμα 0 / 8
Πολυπλοκότητα — γιατί O(n + m), όχι O(n²)
Και ο BFS και ο DFS τρέχουν σε χρόνο O(n+m) με λίστα γειτνίασης — όταν μετράμε σωστά. Με τον λάθος τρόπο μέτρησης βγαίνει O(n2), που είναι μεν σωστός φραγμός, αλλά για αραιά γραφήματα είναι τάξη μεγέθους χειρότερος από την πραγματικότητα. Αξίζει να δούμε γιατί.
Πόση δουλειά κάνουμε ανά κορυφή; Ο αλγόριθμος, στον κύκλο του, βγάζει την κορυφή u από τη δομή (FIFO ή LIFO) και σαρώνει τους γείτονές της για να διακρίνει ποιους έχει ήδη δει. Άρα η δουλειά για κάθε u είναι:
βγαˊλε το u1+(οˊσοι γειˊτονες εˊχει η u).
Πόσοι είναι «οι γείτονες»; — δύο απαντήσεις:
Χαλαρά: «το πολύ n−1» (δεν μπορεί να έχει περισσότερους από όλο το V). Συνολικά ∑u(1+n)=n+n2=O(n2). Σωστό φραγμός — αλλά κάθε κορυφή σπάνια ακουμπά σχεδόν όλο το γράφημα.
Σφιχτά: οι γείτονες της u είναι ακριβώς deg(u), ούτε μία παραπάνω. Συνολικά ∑u(1+deg(u))=n+∑udeg(u).
Και τώρα η κίνηση που σε γλιτώνει: το handshaking lemma του L06 λέει ∑udeg(u)=2m. Άρα η συνολική δουλειά είναι n+2m.
Σύρε τον κέρσορα στο πεδίο m και δες τους δύο φραγμούς να τρέχουν παράλληλα. Για αραιό γράφημα το «αφελές» bar είναι ψηλό σαν τοίχος ενώ το «σφιχτό» μένει χαμηλό· για πυκνό σχεδόν συμπίπτουν. Η εμπειρία του «×20 καλύτερο» είναι το κίνητρο της σφιχτής ανάλυσης.
Αφελής O(n²) ή σφιχτός O(n + m); — δες την απόσταση
n κορυφές, m ακμές, n + n² vs n + 2m
n =
m =31/ 120
Αφελής / Σφιχτός
3.5×
Αφελής: n + n² = 16 + 256 = 272ανεξάρτητο από m
272
Σφιχτός: n + 2m = 16 + 62 = 78αυξάνεται γραμμικά με m
78
Σύμφωνα με την αφελή ανάλυση: «1 + n» ανά κορυφή
Σύνολο = Σ(1 + n) = n + n² = 272 — το ίδιο «μπλοκ» n+1 παντού, ανεξάρτητα από το πραγματικό deg(u).
Σφιχτή ανάλυση: «1 + deg(u)» ανά κορυφή
Σύνολο = n + Σ deg(u) = n + 2m = 78 (handshaking λήμμα).
Μέτρια πυκνότητα. Η σφιχτή ανάλυση δίνει 3.5× καλύτερο φραγμό — αξίζει τον κόπο.
Γρήγορα προεπιλογές m:
Πού κουμπώνει αυτή η διάλεξη
Με BFS και DFS στο χέρι, οι ερωτήσεις του L06 έχουν πλέον αλγοριθμικές απαντήσεις:
Πρόβλημα
Λύση
Πολυπλοκότητα
s-t συνεκτικότητα
DFS ή BFS — δες αν το t ανήκει στο σύνολο που φτάσαμε
O(n+m)
s-t απόσταση (χωρίς βάρη)
BFS — απόσταση = το επίπεδο όπου εμφανίζεται το t
O(n+m)
Όλες οι συνεκτικές συνιστώσες
Επανέλαβε DFS/BFS από κάθε ανεξερεύνητη κορυφή
O(n+m)
Και αυτά είναι μόνο η αρχή. Στο L08 ο DFS γίνεται τοπολογική ταξινόμηση και SCC, ενώ η ιδιότητα του BFS-δέντρου γίνεται έλεγχος διμερότητας. Στο L09 προσθέτουμε βάρη στις ακμές και το BFS εξελίσσεται σε Dijkstra.
Τι μάθαμε
Τι κρατάμε από αυτή τη διάλεξη
DFS — βυθίζεσαι όσο μπορείς, μετά κάνεις backtrack. Στοίβα/αναδρομή. Λύνει s-t συνεκτικότητα.
DFS-δέντρο. Η αναδρομή αφήνει πίσω της δέντρο με n−1 ακμές· οι υπόλοιπες είναι οπίσθιες, όλες κλείνουν κύκλο σε πρόγονο. Θεμέλιο για L08 (SCC) και L09 (MST).
BFS — εξερευνάς επίπεδα με μια ουρά FIFO. L0={s}, και κάθε Li+1 είναι οι νέοι γείτονες του Li.
Θεώρημα BFS: το Li είναι όλες οι κορυφές σε απόσταση i — άρα ο BFS λύνει την s-t απόσταση χωρίς βάρη.
Ιδιότητα BFS-δέντρου: καμία ακμή δεν πηδά επίπεδο. Κλειδί για τη διμερότητα στο L08.
Ένας αλγόριθμος, δύο πρόσωπα: ο R-set λέει «τράβα μέσα μια νέα κορυφή όσο υπάρχει ακμή προς τα έξω». Με ουρά FIFO → BFS· με στοίβα LIFO → DFS. Το τελικό σύνολο είναι το ίδιο.
Πολυπλοκότητα: μην αρκείσαι στο O(n2) — με το handshaking lemma «∑u(1+deg(u))=n+2m» παίρνεις O(n+m), που για αραιά γραφήματα είναι ασύγκριτα καλύτερο.