Chu-Liu-Edmondsův algoritmus (Chu-Liu-Edmond's algorithm), byl nezávisle objeven v roce 1965 (Chu a Liu), 1967 (Edmonds) a 1971 (Bock). Tento algoritmus slouží k nalezení minimální (maximální) kostry orientovaného grafu, což je výrazně složitější problém, než je hledání kostry neorientovaného grafu, pro které existuje řada jednoduchých a efektivních algoritmů - například Borůvkův, Jarník-Primův nebo Kruskalův algoritmus.

Princip

Chu-Liu-Edmondsův se dá rozdělit na dvě fáze. V té první dochází k eliminaci prokazatelně přebytečných hran a postupné kontrakci cyklů do pseudouzlů reprezentujících cykly a přehodnocení hran vedoucích do těchto pseudouzlů. V druhé fázi jsou cykly postupně zpětně rozevírány a jsou vybrány pouze hrany, které patří do minimální kostry.

Tarjanova implementace

Tento článek se dále bude zabývat pouze Tarjanovou implementací Edmondsova algoritmu (pro minimální kostru) se zahrnutými Cameriniho připomínkami. Původní verze algoritmu uveřejněná v roce 1977 Robertem Tarjanem obsahovala chybu, díky které nezaručoval optimalitu nalezené kostry. Tyto nedostatky byly opraveny v roce 1979 úpravou druhé fáze Tarjanova algoritmu.

Datové struktury

Tarjanova implementace využívá několika datových struktur, jejichž účel zde bude pro přehlednost popsán.

  • roots - Obsahuje komponenty, do kterých dosud nevede žádná hrana.
  • queues[i] - Obsahuje prioritní fronty hran vedoucích do komponenty i.
  • enter[i] - Pro každou komponentu i obsahuje hranu, která do ní aktuálně vede.
  • h - Obsahuje hrany, které mohou být v kostře (přecházejí do druhé fáze).
  • rset - Obsahuje kořenové komponenty výsledného lesa.
  • s - Obsahuje reprezentanty silných komponent (všechny uzly z dané silné komponenty mají společného reprezentanta).
  • w - Obsahuje reprezentanty slabých komponent (všechny uzly z dané slabé komponenty mají společného reprezentanta).
  • min - Označuje uzly, které mohou být kořeny výsledného minimálního lesa (tzn. min[rset[i]] je kořenem v minimálním lese).
  • fRoots - Kořeny lesa F (Cameriniho připomínky). V lese F je uzlem každá hrana z h. Pokud hrana v vede do kontrahovaného cyklu c, pak jsou všechny hrany z c potomky hrany v v lese F.
  • lambda[i] - Listy lesa F (tj. hrany, které nevedou do žádného cyklu). Platí, že lambda[i] obsahuje hranu v(k, i).
  • cycles[i] - Obsahuje hrany cyklu silné komponenty reprezentované uzlem i

Disjoint sety s a w mají tu vlastnost, že slučují vždy do prvního reprezentanta. Prioritní fronty queues řadí hrany dle jejich váhy vzestupně. Pokud ve frontě již není žádná hrana, tak vrátí dummy hranu.

První fáze algoritmu

Na začátku jsou všechny uzly v roots, protože do nich nevede ještě žádná hrana. Prvním krokem algoritmu je smyčka - dokud roots obsahuje uzly, tak algoritmus vybere náhodně jeden uzel k z roots a vybere pro něj z odpovídající fronty nejkratší hranu v(i, k). Pokud je tato hrana dummy, pak je daný uzel zřejmě root komponentou optimálního lesa, protože do něj nevedou žádné hrany.

Pokud tato hrana není dummy, tak mohou nastat následující situace. Buď jsou oba uzly ve stejné silné komponentě a byla tedy nalezena hrana v rámci silné komponenty - je třeba proto k vrátit do roots a pokračovat smyčkou v prvním kroku. Nebo se tak nestalo, a v tom případě musí algoritmus tuto hranu zpracovat.

Pokud jsou uzly i a k v různých slabých komponentách, pak dojde ke spojení těchto slabých komponent algoritmus se vrací na výběr uzlu z roots. Pokud jsou ve stejné slabé komponentě, tak vznikla silná komponenta (cyklus), protože k bylo v roots (neobsahovalo žádnou vstupní hranu).

V rámci nově vzniklé komponenty, reprezentované uzlem k, je zapotřebí učinit následující úkony. Nejprve je zapotřebí snížit všem vstupním hranám uzlů un daného cyklu prioritu o currWeight - maxWeight, kde currWeight je váha hrany, která vstupuje do uzlu un v rámci cyklu a maxWeight je váha maximální hrany cyklu. Tímto se docílí penalizace hran, které by v případě svého ponechání v kostře poškodily optimalitu. Po přehodnocení těchto hran je třeba sloučit haldy hran všech uzlů v cyklu do haldy uzlu k. Protože k nyní vystupuje v roli pseudouzlu, do nějž zatím nevstupují žádné hrany, tak se k přidá do roots. Algoritmus se opět vrací na výběr uzlu z roots.

Druhá fáze algoritmu

Nyní již seznam h obsahuje pouze hrany, které mohou být v kostře. V originální Tarjanově implementaci se provedl průchod do hloubky z jednotlivých kořenů minimálního lesa, avšak tento postup nefunguje, protože druhé z Tarjanových lemmat bylo vyvráceno (o jednoznačnosti cesty mezi vrcholem a uzlem komponenty). Z tohoto důvodu je zapotřebí ziskat minimální les jiným způsobem.

Camerini ve svém článku navrhuje následující postup.

Nechť R je množina kořenových uzlů minimálního lesa, čili platí R = min[rset(i)]. Nechť B je prázná množina hran minimálního lesa. Pak opakujme následující algoritmus, dokud množina R a množina kořenových uzlů lesa F (fRoots) nejsou prázdné.

  • Pokud je množina R neprázdná, pak odstraň jeden z jejích prvků (v). V opačném případě odstraň jeden z vrcholů lesa F a přidej jej do množiny B.
  • Identifikuj cestu mezi listem (u, v) = \\lambda[v] a příslušným kořenem lesa F.
  • Odstraň z lesa F všechny uzly (a hrany z nich vedoucí) na cestě identifikované v předchozím kroku. Tato operace mění obsah množiny fRoots.

Časová složitost

Tarjanova implementace má časovou složitost v nejhorším případě O(m \\cdot \\log{n}) a v průměrném O(n \\cdot (\\log n)^{2} + m) za předpokladu, že dílčí operace mají následující složitost.

Prioritní fronta se inicializuje alespoň v čase O(n), operace min - O(m), spojení dvou front - O(n), operace add - O(n). Čili celkem všechny operace proritních front zaberou celkem O(m\\cdot \\log {n}) jednotek času.

Požadované složitosti operací disjoint-setů jsou: find - O(m), union - O(n).

Modifikace algoritmu

Modifikace pro husté grafy

Zatímco v řídkých grafech tento algoritmus funguje se složitostí O(m \\cdot \\log{n}), tak pro husté grafy lze dosáhnout složitosti O(n^{2}). Modifikace spočívá v nahrazení prioritních front běžnými seznamy. Seznam pro každou silnou komponentu S obsahuje pouze jednu minimální hranu (i, j) takovou, že i \\not\\in S,\\; j \\in S (pozn. z implementačního hlediska jsou uzly i a j obvykle reprezentanty silných komponent). Tento seznam je řazen dle i.

Modifikace pro kořenové kostry

Kořenovou kostru lze získat jednoduchou úpravou vstupního grafu - stačí vymazat hrany, které vstupují do požadovaného kořenu.

Maximální kostra

Pro získání maximální orientované kostry daného grafu stačí v algoritmu změnit prioritu front a pozměnit část algoritmu, která kontrahuje uzly do pseudouzlů (otočit znaménka, nalézt hranu s minimální vahou a upravit příslušným způsobem vzorec).

Kód

001./**
002. * Tarjanova implementace Edmondsova algoritmu se zapracovanymi Cameriniho
003. * pripominkami. Hleda minimalni orientovanou kostru
004. * Jedna se o implementaci pro ridke grafy, jejiz operace nejsou optimalni
005. * ve smyslu clanku Roberta Tarjana "Finding optimum branchings", ale
006. * k optimalite nemaji daleko.
007. * @author Pavel Micka
008. */
009.public class EdmondsAlgorithm {
010.    private Queue<Integer> roots; //silne komponenty, ktere jsou koreny v aktualnim grafu (nevede do nich zadny uzel)
011.    private BinomialHeap[] queues; //vstupni hrany do danych uzlu (razene dle priority)
012.    private Edge[] enter; //vstupni hrany do silnych komponent
013.    private List<Edge>[] h; //hrany, ktere mohou byt v kostre
014.    private List<Integer> rset; //rooti vyslednych stromu Tarjanovy implementace
015.    private DisjointSet s; //silne komponenty (reprezentanti)
016.    private DisjointSet w; //slabe komponenty (reprezentanti)
017.    private int[] min; //urcuje finalni korenove uzly orientovanych koster
018.    private int nodeCount; //pocet uzlu zadaneho grafu
019.    private List<Edge> fRoots; //koreny lesa F
020.    private List<Edge>[] cycles; //nalezene cykly
021.    private Edge[] lambda; //listy lesa F
022. 
023.    public EdmondsAlgorithm(int nodeCount) {
024.        roots = new LinkedList<Integer>();
025.        queues = new BinomialHeap[nodeCount];
026.        enter = new Edge[nodeCount]; //vstupni hrany silnych komponent
027.        h = new ArrayList[nodeCount]; //mnozina hran (optimalni kostra)
028.        rset = new ArrayList<Integer>(); //finalni koreny
029.        w = new DisjointSet(nodeCount);
030.        s = new DisjointSet(nodeCount);
031.        min = new int[nodeCount];
032. 
033.        this.fRoots = new ArrayList<Edge>();
034.        cycles = new ArrayList[nodeCount];
035.        lambda = new Edge[nodeCount];
036. 
037.        for (int i = 0; i < nodeCount; i++) {
038.            roots.add(i);
039.            queues[i] = new BinomialHeap(1400000); //kapacita, ktera by mela stacit pro dane pripady
040.            h[i] = new ArrayList<Edge>();
041.            enter[i] = new Edge(0, 0, Integer.MIN_VALUE);
042.            min[i] = i;
043.        }
044.    }
045. 
046.    /**
047.     * Demonstrace Edmondsova algorimu
048.     *
049.     * Na vstupu (standard in) jsou data ve tvaru
050.     *
051.     * 6
052.     * 0 1 50
053.     * 0 2 60
054.     * 1 3 50
055.     * 2 3 1
056.     * 3 5 1000
057.     * 0 4 2
058.     * 3 0 1
059.     * 0 3 30
060.     * 0 3 300
061.     * 0 0 0
062.     *
063.     * Kde prvni radek je pocet uzlu, nasledujici radky reprezentuji hrany
064.     * ve tvaru (z do vaha) a posledni radek (0 0 0) znaci konec vstupu
065.     *
066.     * Algoritmus vypise koren a hrany minimalni kostry
067.     * @param args the command line arguments
068.     */
069.    public static void main(String[] args) throws FileNotFoundException, IOException {
070.        BufferedReader reader = new BufferedReader(new FileReader(new File("edmondsData/soubor.txt")), 9192);
071. 
072.        String line = reader.readLine();
073.        int nodeCount = Integer.valueOf(line);
074. 
075.        EdmondsAlgorithm o = new EdmondsAlgorithm(nodeCount);
076.        o.readEdges(reader);
077.        ResultWrapper r = o.edmondsAlgorithm();
078. 
079.        for(Integer i : r.getRoots()){
080.            System.out.println(i);
081.        }
082.        for(Edge e : r.getEdges()){
083.            System.out.println(e);
084.        }
085.    }
086. 
087.    /**
088.     * Precte hrany ve tvaru
089.     * E1 E1 WEIGHT
090.     * ze zadaneho readeru
091.     * @param reader
092.     * @param nodeCount pocet uzlu
093.     * @throws IOException
094.     */
095.    private void readEdges(BufferedReader reader) throws IOException {
096.        String line = null;
097.        while ((line = reader.readLine()) != null) {
098.            StringTokenizer tokenizer = new StringTokenizer(line, " ");
099.            int from = Integer.valueOf(tokenizer.nextToken());
100.            int to = Integer.valueOf(tokenizer.nextToken());
101.            int weight = Integer.valueOf(tokenizer.nextToken());
102. 
103.            if (from == to) continue;
104. 
105.            Edge e = new Edge(from, to, weight);
106.            queues[to].insert(e);
107.        }
108.    }
109.    /**
110.     * Tarjanova implementace Edmonsova algoritmu
111.     * @return minimalni kostra orientovaneho grafu
112.     */
113.    private ResultWrapper edmondsAlgorithm() {
114.        while (roots.size() != 0) {
115.            int k = roots.poll();
116.            Edge edge = min(k);
117.            if (!(edge.getFrom() == 0 && edge.getTo() == 0)) {
118.                if (cycles[s.find(k)] == null || cycles[s.find(k)].size() == 1) {
119.                    lambda[k] = edge;
120.                } else if (s.find(edge.getFrom()) != s.find(edge.getTo())) {
121.                    for (Edge e : cycles[s.find(k)]) {
122.                        fRoots.remove(e); //je v cyklu, nemuze byt rootem
123.                        edge.getfChildren().add(e);
124.                        e.setfParent(edge);
125.                    }
126.                }
127.            }
128.            if (edge.getFrom() == 0 && edge.getTo() == 0) { //pokud je to dummy hrana
129.                rset.add(k); //pak uzel nema vstupni hranu (je root)
130.            } else if (s.find(edge.getFrom()) == k) { //nalezli jsme hranu v ramci silne komponenty
131.                roots.add(k); //uzel vratimem do fronty
132.            } else {
133.                h[edge.getFrom()].add(edge); //tato hrana je adeptem na hranu MST
134.                fRoots.add(edge); //tato hrana je korenem v lese F
135.                if (w.find(edge.getFrom()) != w.find(edge.getTo())) { //pocatecni a koncovy uzel jsou v ruznych slabych komponentach
136.                    w.union(w.find(edge.getFrom()), w.find(edge.getTo()));
137.                    enter[k] = edge; //edge je vstupni hranou do k
138.                } else { //pocatecni a koncovy uzel jsou ve stejne slabe komponente
139.                    Edge cycleEdge = edge;
140.                    int maxVal = Integer.MIN_VALUE;
141.                    int vertex = Integer.MIN_VALUE;
142. 
143.                    List<Edge> cycle = new ArrayList<Edge>();
144.                    while (!(cycleEdge.getFrom() == 0 && cycleEdge.getTo() == 0)) { //hledame hranu v cyklu s maximalni vahou (ktera nejvice poskodi cyklus)
145.                        cycle.add(cycleEdge);
146.                        if (cycleEdge.getWeight() > maxVal) {
147.                            maxVal = cycleEdge.getWeight();
148.                            vertex = s.find(cycleEdge.getTo());
149.                        }
150.                        cycleEdge = enter[s.find(cycleEdge.getFrom())];
151.                    }
152.                    cycles[s.find(k)] = cycle;
153.                    queues[k].decreaseAllKeys(edge.getWeight() - maxVal);
154.                    min[k] = min[vertex];
155. 
156.                    //collapse cycle
157.                    cycleEdge = enter[s.find(edge.getFrom())];
158.                    while (!(cycleEdge.getFrom() == 0 && cycleEdge.getTo() == 0)) {
159.                        queues[s.find(cycleEdge.getTo())].decreaseAllKeys(cycleEdge.getWeight() - maxVal); //pridame prichozim hranam hodnotu, ktera znaci, o kolik se vypustenim dane cyklove hrany a pridanim dane necyklove zhorsi reseni
160.                        queues[k].mergeHeaps(queues[s.find(cycleEdge.getTo())]);
161.                        s.union(k, s.find(cycleEdge.getTo()));
162.                        cycleEdge = enter[s.find(cycleEdge.getFrom())];
163.                    }
164.                    roots.add(k);
165.                }
166.            }
167.        }
168.        return leaf();
169.    }
170. 
171.    /**
172.     * Slouzi k identifikaci minimalni kostry dle Cameriniho pripominek
173.     * @return minimalni kostra grafu
174.     */
175.    private ResultWrapper leaf() {
176.        List<Edge> b = new ArrayList<Edge>();
177.        List<Integer> r = new ArrayList<Integer>();
178.        int fRootIndex = 0;
179.        OUTER:
180.        while (!rset.isEmpty() || fRootIndex < fRoots.size()) {
181.            int v = 0;
182.            if (!rset.isEmpty()) {
183.                v = min[rset.remove(0)];
184.                r.add(v);
185.            } else {
186.                Edge e = fRoots.get(fRootIndex);
187.                if (e.isDeleted()) {
188.                    fRootIndex++;
189.                    continue OUTER;
190.                }
191.                v = e.getTo();
192.                b.add(e);
193.                fRootIndex++;
194.            }
195.            Edge curr = lambda[v];
196.            while (curr != null && curr.isDeleted() == false) {
197.                curr.setDeleted(true);
198.                fRoots.addAll(curr.getfChildren());
199.                curr = curr.getfParent();
200.            }
201.        }
202.        return new ResultWrapper(r, b);
203.    }
204. 
205.    /**
206.     * Odstrani a vrati minimalni hranu vstupujici do daneho uzlu
207.     * @param k cislo uzlu
208.     * @return minimalni vstupni hrana, pokud hrana neexistuje - dummy hrana
209.     * z uzlu 0 do uzlu 0 s vahou Integer.MIN_VALUE
210.     */
211.    private Edge min(int k) {
212.        Edge e = queues[k].returnTop();
213.        if (e == null) e = new Edge(0, 0, Integer.MIN_VALUE);
214.        return e;
215.    }
216.}
217. 
218./**
219. * Binomialni halda
220. * Radi prvky dle priority (mensi == vyssi priorita)
221. * @author Pavel Micka
222. */
223.class BinomialHeap {
224.    private Node<Edge>[] nodes;
225.    private Node<Edge> min;
226.    private int offset = 0;
227. 
228.    public BinomialHeap(double capacity) {
229.        min = null;
230.        nodes = new Node[((int) (Math.log(capacity) / Math.log(2))) + 2];
231.    }
232. 
233.    public void decreaseAllKeys(int ammout) {
234.        this.offset -= ammout;
235.    }
236. 
237.    /**
238.     * Vlozi prvek do haldy, pokud je mensi nez aktualni minimalni, tak
239.     * jej ulozi jako nejmensi prvek
240.     * @param e
241.     */
242.    public void insert(Edge e) {
243.        Node<Edge> n = new Node<Edge>(e);
244.        if (nodes[0] != null) merge(n, nodes[0]);
245.        else nodes[0] = n;
246. 
247.        if (min == null) min = n;
248.        else if (min.value.compareTo(n.value) == 1) min = n;
249.    }
250. 
251.    /**
252.     * Smaze a vrati uzel s nejvyssi prioritou
253.     * @return
254.     */
255.    public Edge returnTop() {
256.        if (min == null) return null;
257. 
258.        Edge tmp = (Edge) min.value;
259.        tmp.setWeight(tmp.getWeight() + this.offset);
260.        nodes[min.order] = null; //strom vyjmeme
261.        for (Node n : min.children) { //a z potomku udelame nove stromu
262.            n.parent = null;
263.            if (nodes[n.order] != null) merge(n, nodes[n.order]);
264.            else nodes[n.order] = n;
265.        }
266.        min.children.clear();
267.        Edge minVal = null;
268.        Node minNode = null;
269.        for (int i = 0; i < nodes.length; i++) {
270.            if (nodes[i] != null) {
271.                if (minVal == null) {
272.                    minVal = nodes[i].value;
273.                    minNode = nodes[i];
274.                } else if (minVal.compareTo(nodes[i].value) == 1) {
275.                    minVal = nodes[i].value;
276.                    minNode = nodes[i];
277.                }
278.            }
279.        }
280.        min = minNode;
281.        return (Edge) tmp;
282.    }
283. 
284.    /**
285.     * Provede slouceni binomialnich stromu, je-li nutno provede slucovani
286.     * do vyssich radu
287.     * alespon jedna z hald musi byt bezpodminecne jiz soucasti haldy, obe haldy
288.     * musi byt stejneho radu
289.     * @param a halda 1
290.     * @param b halda 2
291.     * @return sloucena halda
292.     */
293.    private void merge(Node a, Node b) {
294.        if (a.order != b.order)
295.            throw new IllegalArgumentException("Haldy nejsou stejneho radu");
296.        int tmpOrder = a.order;
297.        nodes[tmpOrder] = null;
298.        Node newRoot = null;
299.        if (a.value.compareTo(b.value) < 0) {
300.            b.parent = a;
301.            a.children.add(b);
302.            a.order++;
303.            newRoot = a;
304.        } else {
305.            a.parent = b;
306.            b.children.add(a);
307.            b.order++;
308.            newRoot = b;
309.        }
310.        if (nodes[tmpOrder + 1] == null) nodes[tmpOrder + 1] = newRoot;
311.        else merge(newRoot, nodes[tmpOrder + 1]);
312.    }
313. 
314.    /**
315.     * Slouci haldy, pokud mergovana halda obsahuje mensi prvek, nez halda, na
316.     * ktere je volana operace, pak je tento prvek minimalni i v nove halde
317.     * (Toto lze napsat lepe)
318.     * @param heap
319.     */
320.    public void mergeHeaps(BinomialHeap heap) {
321.        for (int i = 0; i < heap.nodes.length; i++) {
322.            if (heap.nodes[i] != null) {
323.                decreaseWeights(this.offset - heap.offset, heap.nodes[i]);
324.                if (nodes[i] == null) nodes[i] = heap.nodes[i];
325.                else merge(nodes[i], heap.nodes[i]);
326.            }
327.        }
328.        if (this.min == null) this.min = heap.min;
329.        else if (this.min != null && heap.min != null && heap.min.value.getWeight() < min.value.getWeight())
330.            min = heap.min;
331.    }
332. 
333.    @Override
334.    public String toString() {
335.        StringBuilder builder = new StringBuilder();
336.        for (int i = 0; i < nodes.length; i++) {
337.            if (nodes[i] == null) builder.append(i + ": null\\n");
338.            else builder.append(i + ":\\n" + nodes[i].toString() + "\\n");
339.        }
340.        return builder.toString();
341.    }
342. 
343.    private void decreaseWeights(int ammount, Node<Edge> node) {
344.        node.value.setWeight(node.value.getWeight() - ammount);
345.        for (Node n : node.children) {
346.            decreaseWeights(ammount, n);
347.        }
348.    }
349. 
350.    /**
351.     * Reprezentuje uzel binomialni haldy
352.     * @param <ENTITY>
353.     */
354.    private class Node<ENTITY extends Comparable> {
355. 
356.        Node<ENTITY> parent;
357.        ENTITY value;
358.        List<Node> children;
359.        int order; //rad binomialni haldy
360. 
361.        public Node(ENTITY value) {
362.            this.value = value;
363.            children = new ArrayList<Node>();
364.            order = 0;
365.        }
366. 
367.        @Override
368.        public String toString() {
369.            String s = "Node value: " + value + ", order: " + order + "\\n";
370.            for (Node n : children) {
371.                s += n.toString();
372.            }
373.            return s;
374.        }
375.    }
376.}
377. 
378./**
379. * Hrana orientovaneho grafu
380. * @author malejpavouk
381. */
382.class Edge implements Comparable<Edge> {
383.    private Edge fParent; //rodic ve stromu F
384.    private List<Edge> fChildren; //deti ve stromu F
385.    private int from; //uzel z
386.    private int to; //uzel do
387.    private int weight; //vaha
388.    private int originalWeight; //puvodni vaha hrany
389.    private boolean deleted; //priznak smazani hrany
390. 
391.    public Edge(int from, int to, int weight) {
392.        this.from = from;
393.        this.to = to;
394.        this.weight = weight;
395.        this.originalWeight = weight;
396.        this.deleted = false;
397. 
398.        this.fChildren = new ArrayList<Edge>();
399.    }
400. 
401.    public int compareTo(Edge o) {
402.        if (this.getWeight() < o.getWeight()) return -1;
403.        else if (this.getWeight() > o.getWeight()) return 1;
404.        return 0;
405.    }
406. 
407.    @Override
408.    public String toString() {
409.        return getFrom() + " " + getTo() + " " + getWeight();
410.    }
411. 
412.    /**
413.     * @return the fParent
414.     */
415.    public Edge getfParent() {
416.        return fParent;
417.    }
418. 
419.    /**
420.     * @param fParent the fParent to set
421.     */
422.    public void setfParent(Edge fParent) {
423.        this.fParent = fParent;
424.    }
425. 
426.    /**
427.     * @return the fChildren
428.     */
429.    public List<Edge> getfChildren() {
430.        return fChildren;
431.    }
432. 
433.    /**
434.     * @param fChildren the fChildren to set
435.     */
436.    public void setfChildren(List<Edge> fChildren) {
437.        this.fChildren = fChildren;
438.    }
439. 
440.    /**
441.     * @return the from
442.     */
443.    public int getFrom() {
444.        return from;
445.    }
446. 
447.    /**
448.     * @param from the from to set
449.     */
450.    public void setFrom(int from) {
451.        this.from = from;
452.    }
453. 
454.    /**
455.     * @return the to
456.     */
457.    public int getTo() {
458.        return to;
459.    }
460. 
461.    /**
462.     * @param to the to to set
463.     */
464.    public void setTo(int to) {
465.        this.to = to;
466.    }
467. 
468.    /**
469.     * @return the weight
470.     */
471.    public int getWeight() {
472.        return weight;
473.    }
474. 
475.    /**
476.     * @param weight the weight to set
477.     */
478.    public void setWeight(int weight) {
479.        this.weight = weight;
480.    }
481. 
482.    /**
483.     * @return the originalWeight
484.     */
485.    public int getOriginalWeight() {
486.        return originalWeight;
487.    }
488. 
489.    /**
490.     * @param originalWeight the originalWeight to set
491.     */
492.    public void setOriginalWeight(int originalWeight) {
493.        this.originalWeight = originalWeight;
494.    }
495. 
496.    /**
497.     * @return the deleted
498.     */
499.    public boolean isDeleted() {
500.        return deleted;
501.    }
502. 
503.    /**
504.     * @param deleted the deleted to set
505.     */
506.    public void setDeleted(boolean deleted) {
507.        this.deleted = deleted;
508.    }
509.}
510. 
511./**
512. * Implementace Union-Find problemu pro Edmondsuv algoritmus
513. * @author malejpavouk
514. */
515.class DisjointSet {
516.    private Node[] nodes;
517. 
518.    public DisjointSet(int nodeCount) {
519.        nodes = new Node[nodeCount];
520.        for (int i = 0; i < nodeCount; i++) {
521.            nodes[i] = new Node();
522.            nodes[i].id = i;
523.        }
524.    }
525. 
526.    /**
527.     * Provede sjednoceni komponent, ve kterych jsou uzly "a" a "b"
528.     * Union provadi vzdy do A
529.     * @param a cislo uzlu a
530.     * @param b cislo uzlu b
531.     * @return cislo reprezentanta sjednocene komponenty
532.     */
533.    public int union(int a, int b) {
534.        Node repA = nodes[find(a)];
535.        Node repB = nodes[find(b)];
536. 
537.        repB.parent = repA;
538.        return repA.id;
539.    }
540. 
541.    /**
542.     * Vrati reprezentanta zadaneho uzlu
543.     * @param a cislo uzlu, jehoz reprezentanta hledame
544.     * @return cislo reprezentanta uzlu
545.     */
546.    public int find(int a) {
547.        Node n = nodes[a];
548.        int jumps = 0;
549.        while (n.parent != null) {
550.            n = n.parent;
551.            jumps++;
552.        }
553.        if(jumps > 1) repair(a, n.id);
554.        return n.id;
555.    }
556.    /**
557.     * Provede kontrakci cesty do korene
558.     * @param a list, ze ktereho se zacina kontrahovat
559.     * @param rootId id rootu
560.     */
561.    private void repair(int a, int rootId) {
562.        Node curr = nodes[a];
563.        while(curr.id != rootId){
564.            Node tmp = curr.parent;
565.            curr.parent = nodes[rootId];
566.            curr = tmp;
567.        }
568.    }
569.    @Override
570.    public String toString() {
571.        StringBuilder builder = new StringBuilder();
572.        for (int i = 0; i < nodes.length; i++) {
573.            builder.append(find(i) + " ");
574.        }
575.        return builder.toString();
576.    }
577. 
578.    /**
579.     * Uzel n-arniho stromu
580.     */
581.    private static class Node {
582.        /**
583.         * Rodic
584.         */
585.        Node parent;
586.        /**
587.         * Identifikator uzlu
588.         */
589.        int id;
590.    }
591.}
592. 
593./**
594. * Prepravka na vysledek Edmondsova algoritmu
595. * @author malejpavouk
596. */
597.class ResultWrapper {
598.    private List<Integer> roots; //hrany minimalniho lesa
599.    private List<Edge> edges; //hrany minimalniho lesa
600. 
601.    public ResultWrapper(List<Integer> roots, List<Edge> edges) {
602.        this.roots = roots;
603.        this.edges = edges;
604.    }
605.    /**
606.     * @return the roots
607.     */
608.    public List<Integer> getRoots() {
609.        return roots;
610.    }
611. 
612.    /**
613.     * @return the edges
614.     */
615.    public List<Edge> getEdges() {
616.        return edges;
617.    }
618.}

Literatura

  • TARJAN, Robert. Finding Optimum Branchings. In Networks : Vol. 7. [s.l.] : [s.n.], 1977. s. 25-35.
  • CAMERINI, P. M., FRATTA, L., MAFFIOLI, F. A Note on Finding Optimum Branchings. In Networks : Vol. 9. [s.l.] : [s.n.], 1979. s. 309-312.
  • TOFIGH, Ali. Optimum Branchings and Spanning Aborescences. [s.l.] : [s.n.], 2009. 11 s.

SEO od společnosti Digital Pylon


Online casino s algoritmem

České casino online online slot-vegas.cz

Hrajte nejlepší hry jako je GoodGame Empire.





Zajímavé články: Jak najít práci snů? Zvolte kariéru v IT!, Češi mají rádi hrací automaty online, Jak funguje algoritmické obchodování Casino, Online výuka Algoritmus a online marketing mají svá pravidla, Automaty, Matematický vliv, Ratings, Jak fungují algoritmy hazardních her online: více znalostí, více peněz, SYPWAI - nástroj pro vědecký vývoj, Vynikají na globálním trhu: Nejlepší vývojáři softwaru pro online výherní automaty, Jak si vybrat nejlepší české online casino, Proč byste měli hrát online casino VPN revoluce, Kde najdeme algoritmy v každodenním životě?, Čeká vás pracovní pohovor mimo město? Podívejte se, jak dokonale zvládnout včasný příchod, 5 úžasných technologií ze světa hazardních her, Mirror and access to Mostbet, Svou kancelář můžete mít stále po ruce, Jaké výhody má digitalizovaná firma oproti off-line konkurenci?, Jaký systém vybrat pro snadné řízení výroby?, Nahradí umělá inteligence ajťáky?, Důvody, proč používat SnapTik ke stahování videí TikTok, Dokonalý den na pláži: Co si vzít s sebou, aby byl výlet zábavný a bezpečný?, Jak přežít dlouhý let?, Go pay GoodGame Empire, Blockchain, Rozhovor, Umělá inteligence, Ochranná známka pre softvér: Prečo ju registrovať?, Role kryptografických algoritmů v zabezpečení online kasin, Jaké jsou náklady na nákup 3D tiskárny?, Jak algoritmy vylepšují online zážitky v roce 2025, Epilace laserem a péče o pokožku před a po ní, Byty k pronájmu Sokolov - výhody a rizika pronájmu bytu bez realitky, Filmy a seriály plné hádanek: kryptografie jako hlavní téma


Doporučujeme

Internet pro vaši firmu na míru

https://www.algoritmy.net