-
Notifications
You must be signed in to change notification settings - Fork 0
/
graph.clj
101 lines (87 loc) · 3.26 KB
/
graph.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
(defn create-graph
[& {nodes :nodes edges :edges}]
{:nodes (set nodes) :edges edges})
(defn add-edge
([graph from to weight]
(-> (assoc-in graph [:edges from to] weight)
(update :nodes #(conj % from))
(update :nodes #(conj % to))))
([graph from to]
(add-edge graph from to nil)))
(defn add-double-edge
([graph from to]
(add-double-edge graph from to nil))
([graph from to weight]
(-> (add-edge graph from to weight)
(add-edge to from weight))))
(defn get-nodes
[graph]
(get graph :nodes))
(defn get-neighbors
([graph]
(get graph :edges))
([graph node]
(-> (get-neighbors graph)
(get node)
(keys))))
(assert (= '(:a) (-> (create-graph)
(add-double-edge :a :b 0)
(get-neighbors :b))))
(defn get-distance
[graph from to]
(get-in graph [:edges from to]))
(defn dijkstra
([graph source destination]
(let [[_ previous] (dijkstra graph source)]
(loop [path '()
v destination]
(if (nil? v)
path
(recur
(conj path v)
(get previous v))))))
([graph source]
(let [min-distance-in-queue (fn [queue distance]
(apply (partial min-key #(get distance %)) queue))
dijkstra-inner (fn [graph u distance previous]
(loop [neighbors (get-neighbors graph u)
distance distance
previous previous]
(if (empty? neighbors)
[distance previous]
(let [v (first neighbors)
alt (+ (get distance u) (get-distance graph u v))]
(if (< alt (get distance v))
; Faster to go through u
(recur
(rest neighbors)
(assoc distance v alt)
(assoc previous v u))
; Not faster, continoue
(recur
(rest neighbors)
distance
previous))))))]
(loop [distance (-> (into {} (map (fn [v] [v Integer/MAX_VALUE]) (get-nodes graph)))
(assoc source 0))
previous (into {} (map (fn [v] [v nil]) (get-nodes graph)))
q (into (set {}) (get-nodes graph))]
(if (empty? q)
[distance
previous]
(let [u (min-distance-in-queue q distance)
[distance previous] (dijkstra-inner graph u distance previous)]
(recur distance previous (disj q u))))))))
(let [g (-> (create-graph :nodes [1 2 3 4 5 6])
(add-double-edge 1 2 7)
(add-double-edge 1 3 9)
(add-double-edge 1 6 14)
(add-double-edge 2 3 10)
(add-double-edge 2 4 15)
(add-double-edge 3 6 2)
(add-double-edge 3 4 11)
(add-double-edge 4 5 6)
(add-double-edge 5 6 9))
[distance previous] (dijkstra g 1)]
(assert (= 9 (get distance 3)))
(assert (= 11 (get distance 6))))