boruvka.py: A few simplifications and f-strings
diff --git a/DIRECTORY.md b/DIRECTORY.md
index adc9bb9e4699..41485f6f0ca4 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -97,6 +97,7 @@
   * [Peak Signal To Noise Ratio](https://github.com/TheAlgorithms/Python/blob/master/compression/peak_signal_to_noise_ratio.py)
 
 ## Computer Vision
+  * [Cnn Classification](https://github.com/TheAlgorithms/Python/blob/master/computer_vision/cnn_classification.py)
   * [Harris Corner](https://github.com/TheAlgorithms/Python/blob/master/computer_vision/harris_corner.py)
   * [Mean Threshold](https://github.com/TheAlgorithms/Python/blob/master/computer_vision/mean_threshold.py)
 
@@ -300,6 +301,7 @@
   * [Bfs Zero One Shortest Path](https://github.com/TheAlgorithms/Python/blob/master/graphs/bfs_zero_one_shortest_path.py)
   * [Bidirectional A Star](https://github.com/TheAlgorithms/Python/blob/master/graphs/bidirectional_a_star.py)
   * [Bidirectional Breadth First Search](https://github.com/TheAlgorithms/Python/blob/master/graphs/bidirectional_breadth_first_search.py)
+  * [Boruvka](https://github.com/TheAlgorithms/Python/blob/master/graphs/boruvka.py)
   * [Breadth First Search](https://github.com/TheAlgorithms/Python/blob/master/graphs/breadth_first_search.py)
   * [Breadth First Search 2](https://github.com/TheAlgorithms/Python/blob/master/graphs/breadth_first_search_2.py)
   * [Breadth First Search Shortest Path](https://github.com/TheAlgorithms/Python/blob/master/graphs/breadth_first_search_shortest_path.py)
@@ -349,6 +351,7 @@
   * [Djb2](https://github.com/TheAlgorithms/Python/blob/master/hashes/djb2.py)
   * [Enigma Machine](https://github.com/TheAlgorithms/Python/blob/master/hashes/enigma_machine.py)
   * [Hamming Code](https://github.com/TheAlgorithms/Python/blob/master/hashes/hamming_code.py)
+  * [Luhn](https://github.com/TheAlgorithms/Python/blob/master/hashes/luhn.py)
   * [Md5](https://github.com/TheAlgorithms/Python/blob/master/hashes/md5.py)
   * [Sdbm](https://github.com/TheAlgorithms/Python/blob/master/hashes/sdbm.py)
   * [Sha1](https://github.com/TheAlgorithms/Python/blob/master/hashes/sha1.py)
@@ -421,10 +424,12 @@
   * [Binomial Distribution](https://github.com/TheAlgorithms/Python/blob/master/maths/binomial_distribution.py)
   * [Bisection](https://github.com/TheAlgorithms/Python/blob/master/maths/bisection.py)
   * [Ceil](https://github.com/TheAlgorithms/Python/blob/master/maths/ceil.py)
+  * [Check Valid Ip Address](https://github.com/TheAlgorithms/Python/blob/master/maths/check_valid_ip_address.py)
   * [Chudnovsky Algorithm](https://github.com/TheAlgorithms/Python/blob/master/maths/chudnovsky_algorithm.py)
   * [Collatz Sequence](https://github.com/TheAlgorithms/Python/blob/master/maths/collatz_sequence.py)
   * [Combinations](https://github.com/TheAlgorithms/Python/blob/master/maths/combinations.py)
   * [Decimal Isolate](https://github.com/TheAlgorithms/Python/blob/master/maths/decimal_isolate.py)
+  * [Double Factorial Recursive](https://github.com/TheAlgorithms/Python/blob/master/maths/double_factorial_recursive.py)
   * [Entropy](https://github.com/TheAlgorithms/Python/blob/master/maths/entropy.py)
   * [Euclidean Distance](https://github.com/TheAlgorithms/Python/blob/master/maths/euclidean_distance.py)
   * [Euclidean Gcd](https://github.com/TheAlgorithms/Python/blob/master/maths/euclidean_gcd.py)
@@ -539,6 +544,7 @@
 
 ## Other
   * [Activity Selection](https://github.com/TheAlgorithms/Python/blob/master/other/activity_selection.py)
+  * [Date To Weekday](https://github.com/TheAlgorithms/Python/blob/master/other/date_to_weekday.py)
   * [Davis–Putnam–Logemann–Loveland](https://github.com/TheAlgorithms/Python/blob/master/other/davis–putnam–logemann–loveland.py)
   * [Dijkstra Bankers Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/dijkstra_bankers_algorithm.py)
   * [Doomsday](https://github.com/TheAlgorithms/Python/blob/master/other/doomsday.py)
@@ -854,6 +860,7 @@
   * [Counting Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/counting_sort.py)
   * [Cycle Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/cycle_sort.py)
   * [Double Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/double_sort.py)
+  * [Exchange Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/exchange_sort.py)
   * [External Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/external_sort.py)
   * [Gnome Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/gnome_sort.py)
   * [Heap Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/heap_sort.py)
@@ -893,6 +900,7 @@
 
 ## Strings
   * [Aho Corasick](https://github.com/TheAlgorithms/Python/blob/master/strings/aho_corasick.py)
+  * [Alternative String Arrange](https://github.com/TheAlgorithms/Python/blob/master/strings/alternative_string_arrange.py)
   * [Anagrams](https://github.com/TheAlgorithms/Python/blob/master/strings/anagrams.py)
   * [Autocomplete Using Trie](https://github.com/TheAlgorithms/Python/blob/master/strings/autocomplete_using_trie.py)
   * [Boyer Moore Search](https://github.com/TheAlgorithms/Python/blob/master/strings/boyer_moore_search.py)
@@ -902,6 +910,7 @@
   * [Check Pangram](https://github.com/TheAlgorithms/Python/blob/master/strings/check_pangram.py)
   * [Detecting English Programmatically](https://github.com/TheAlgorithms/Python/blob/master/strings/detecting_english_programmatically.py)
   * [Frequency Finder](https://github.com/TheAlgorithms/Python/blob/master/strings/frequency_finder.py)
+  * [Indian Phone Validator](https://github.com/TheAlgorithms/Python/blob/master/strings/indian_phone_validator.py)
   * [Is Palindrome](https://github.com/TheAlgorithms/Python/blob/master/strings/is_palindrome.py)
   * [Jaro Winkler](https://github.com/TheAlgorithms/Python/blob/master/strings/jaro_winkler.py)
   * [Knuth Morris Pratt](https://github.com/TheAlgorithms/Python/blob/master/strings/knuth_morris_pratt.py)
@@ -941,6 +950,7 @@
   * [Instagram Crawler](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_crawler.py)
   * [Instagram Pic](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_pic.py)
   * [Instagram Video](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_video.py)
+  * [Random Anime Character](https://github.com/TheAlgorithms/Python/blob/master/web_programming/random_anime_character.py)
   * [Recaptcha Verification](https://github.com/TheAlgorithms/Python/blob/master/web_programming/recaptcha_verification.py)
   * [Slack Message](https://github.com/TheAlgorithms/Python/blob/master/web_programming/slack_message.py)
   * [Test Fetch Github Info](https://github.com/TheAlgorithms/Python/blob/master/web_programming/test_fetch_github_info.py)
diff --git a/graphs/boruvka.py b/graphs/boruvka.py
index b95bcc39850e..3fa5c6fd2a26 100644
--- a/graphs/boruvka.py
+++ b/graphs/boruvka.py
@@ -1,11 +1,12 @@
 """Borůvka's algorithm.
 
-    Determines the minimum spanning tree(MST) of a graph using the Borůvka's algorithm.
+    Determines the minimum spanning tree (MST) of a graph using the Borůvka's algorithm.
     Borůvka's algorithm is a greedy algorithm for finding a minimum spanning tree in a
-    graph,or a minimum spanning forest in the case of a graph that is not connected.
+    connected graph, or a minimum spanning forest if a graph that is not connected.
 
     The time complexity of this algorithm is O(ELogV), where E represents the number
     of edges, while V represents the number of nodes.
+    O(number_of_edges Log number_of_nodes)
 
     The space complexity of this algorithm is O(V + E), since we have to keep a couple
     of lists whose sizes are equal to the number of nodes, as well as keep all the
@@ -19,7 +20,7 @@
     doesn't need to presort the edges or maintain a priority queue in order to find the
     minimum spanning tree.
     Even though that doesn't help its complexity, since it still passes the edges logE
-    times, it is a bit more simple to code.
+    times, it is a bit simpler to code.
 
     Details: https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm
 """
@@ -31,13 +32,13 @@ def __init__(self, num_of_nodes: int) -> None:
         Arguments:
             num_of_nodes - the number of nodes in the graph
         Attributes:
-            m_v - the number of nodes in the graph.
+            m_num_of_nodes - the number of nodes in the graph.
             m_edges - the list of edges.
             m_component - the dictionary which stores the index of the component which
             a node belongs to.
         """
 
-        self.m_v = num_of_nodes
+        self.m_num_of_nodes = num_of_nodes
         self.m_edges = []
         self.m_component = {}
 
@@ -57,7 +58,7 @@ def set_component(self, u_node: int) -> None:
         """Finds the component index of a given node"""
 
         if self.m_component[u_node] != u_node:
-            for k in self.m_component.keys():
+            for k in self.m_component:
                 self.m_component[k] = self.find_component(k)
 
     def union(self, component_size: list, u_node: int, v_node: int) -> None:
@@ -82,22 +83,18 @@ def boruvka(self) -> None:
         component_size = []
         mst_weight = 0
 
-        minimum_weight_edge = [-1] * self.m_v
+        minimum_weight_edge = [-1] * self.m_num_of_nodes
 
         # A list of components (initialized to all of the nodes)
-        for node in range(self.m_v):
+        for node in range(self.m_num_of_nodes):
             self.m_component.update({node: node})
             component_size.append(1)
 
-        num_of_components = self.m_v
+        num_of_components = self.m_num_of_nodes
 
         while num_of_components > 1:
-            l_edges = len(self.m_edges)
-            for i in range(l_edges):
-
-                u = self.m_edges[i][0]
-                v = self.m_edges[i][1]
-                w = self.m_edges[i][2]
+            for edge in self.m_edges:
+                u, v, w = edge
 
                 u_component = self.m_component[u]
                 v_component = self.m_component[v]
@@ -113,22 +110,16 @@ def boruvka(self) -> None:
                     observing right now, we will assign the value of the edge
                     we're observing to it"""
 
-                    if (
-                        minimum_weight_edge[u_component] == -1
-                        or minimum_weight_edge[u_component][2] > w
-                    ):
-                        minimum_weight_edge[u_component] = [u, v, w]
-                    if (
-                        minimum_weight_edge[v_component] == -1
-                        or minimum_weight_edge[v_component][2] > w
-                    ):
-                        minimum_weight_edge[v_component] = [u, v, w]
-
-            for node in range(self.m_v):
-                if minimum_weight_edge[node] != -1:
-                    u = minimum_weight_edge[node][0]
-                    v = minimum_weight_edge[node][1]
-                    w = minimum_weight_edge[node][2]
+                    for component in (u_component, v_component):
+                        if (
+                            minimum_weight_edge[component] == -1
+                            or minimum_weight_edge[component][2] > w
+                        ):
+                            minimum_weight_edge[component] = [u, v, w]
+
+            for edge in minimum_weight_edge:
+                if edge != -1:
+                    u, v, w = edge
 
                     u_component = self.m_component[u]
                     v_component = self.m_component[v]
@@ -136,36 +127,19 @@ def boruvka(self) -> None:
                     if u_component != v_component:
                         mst_weight += w
                         self.union(component_size, u_component, v_component)
-                        print(
-                            "Added edge ["
-                            + str(u)
-                            + " - "
-                            + str(v)
-                            + "]\n"
-                            + "Added weight: "
-                            + str(w)
-                            + "\n"
-                        )
+                        print(f"Added edge [{u} - {v}]\nAdded weight: {w}\n")
                         num_of_components -= 1
 
-            minimum_weight_edge = [-1] * self.m_v
-        print("The total weight of the minimal spanning tree is: " + str(mst_weight))
+            minimum_weight_edge = [-1] * self.m_num_of_nodes
+        print(f"The total weight of the minimal spanning tree is: {mst_weight}")
 
 
 def test_vector() -> None:
     """
-    >>> g=Graph(8)
-    >>> g.add_edge(0, 1, 10)
-    >>> g.add_edge(0, 2, 6)
-    >>> g.add_edge(0, 3, 5)
-    >>> g.add_edge(1, 3, 15)
-    >>> g.add_edge(2, 3, 4)
-    >>> g.add_edge(3, 4, 8)
-    >>> g.add_edge(4, 5, 10)
-    >>> g.add_edge(4, 6, 6)
-    >>> g.add_edge(4, 7, 5)
-    >>> g.add_edge(5, 7, 15)
-    >>> g.add_edge(6, 7, 4)
+    >>> g = Graph(8)
+    >>> for u_v_w in ((0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4),
+    ...    (3, 4, 8), (4, 5, 10), (4, 6, 6), (4, 7, 5), (5, 7, 15), (6, 7, 4)):
+    ...        g.add_edge(*u_v_w)
     >>> g.boruvka()
     Added edge [0 - 3]
     Added weight: 5

Python f-strings simplify the code and [should speed up execution](https://www.scivision.dev/python-f-string-speed). 

I don’t know graphs… ~Are `components` and `nodes` the same things or different things?
If they are the same thing, then we should only use one name in the code and drop the other.~
I looked it up.

@srkchowdary2000 Your review, please.

### **Describe your change:**



* [ ] Add an algorithm?
* [x] Fix a bug or typo in an existing algorithm?
* [ ] Documentation change?

### **Checklist:**
* [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md).
* [x] This pull request is all my own work -- I have not plagiarized.
* [x] I know that pull requests will not be merged if they fail the automated tests.
* [x] This PR only changes one algorithm file.  To ease review, please open separate PRs for separate algorithms.
* [x] All new Python files are placed inside an existing directory.
* [x] All filenames are in all lowercase characters with no spaces or dashes.
* [x] All functions and variable names follow Python naming conventions.
* [x] All function parameters and return values are annotated with Python [type hints](https://docs.python.org/3/library/typing.html).
* [x] All functions have [doctests](https://docs.python.org/3/library/doctest.html) that pass the automated testing.
* [x] All new algorithms have a URL in its comments that points to Wikipedia or other similar explanation.
* [x] If this pull request resolves one or more open issues then the commit message contains `Fixes: #{$ISSUE_NO}`.
