logo

האלגוריתם של הירולצר לגרף מכוון

בהינתן גרף אוילר מכוון המשימה היא להדפיס מעגל אוילר . מעגל אוילר הוא נתיב שחוצה כל קצה של גרף פעם אחת בדיוק והנתיב מסתיים בקודקוד ההתחלה.

פֶּתֶק: הגרף הנתון מכיל מעגל אוילר.

דוּגמָה:



קלט: גרף מכוון

האלגוריתם של הירולצר לגרף מכוון' title=

תְפוּקָה: 0 3 4 0 2 1 0

דרישות קדם:

  • דנו ב בעיה של לגלות אם גרף נתון הוא אולריאן או לא עבור גרף לא מכוון
  • תנאים למעגל אולריאנית ב-Grpag מכוון: (1) כל הקודקודים שייכים לרכיב אחד מחובר חזק. (2) לכל הקודקודים יש אותה מעלה ודרגה החוצה. שימו לב שעבור גרף לא מכוון התנאי שונה (לכל הקודקודים יש מידה שווה)

גִישָׁה:

  1. בחרו כל קודקוד התחלתי v ועקבו אחרי שובל של קצוות מאותו קודקוד עד החזרה ל-v. לא ניתן להיתקע בשום קודקוד פרט ל-v כי דרגת דרגת קצה של כל קודקוד חייבת להיות זהה כאשר השביל נכנס לקודקוד אחר w חייב להיות קצה לא בשימוש שיוצא מ-w. הסיור שנוצר בדרך זו הוא סיור סגור אך עשוי שלא לכסות את כל הקודקודים והקצוות של הגרף הראשוני.
  2. כל עוד קיים קודקוד u ששייך לסיור הנוכחי אך יש לו קצוות סמוכים שאינם חלק מהסיור התחילו מסלול נוסף מ-u בעקבות קצוות לא בשימוש עד החזרה אל u והצטרפו לסיור שנוצר בצורה זו לסיור הקודם.

אִיוּר:

ניקח דוגמה לתרשים שלמעלה עם 5 צמתים: adj = {{2 3} {0} {1} {4} {0}}.

  1. התחל בקודקוד 0 :
    • נתיב נוכחי: [0]
    • מעגל: []
  2. קודקוד 0 → 3 :
    • נתיב נוכחי: [0 3]
    • מעגל: []
  3. קודקוד 3 → 4 :
    • נתיב נוכחי: [0 3 4]
    • מעגל: []
  4. קודקוד 4 → 0 :
    • נתיב נוכחי: [0 3 4 0]
    • מעגל: []
  5. קודקוד 0 → 2 :
    • נתיב נוכחי: [0 3 4 0 2]
    • מעגל: []
  6. קודקוד 2 → 1 :
    • נתיב נוכחי: [0 3 4 0 2 1]
    • מעגל: []
  7. קודקוד 1 → 0 :
    • נתיב נוכחי: [0 3 4 0 2 1 0]
    • מעגל: []
  8. חזרה לקודקוד 0 : הוסף 0 למעגל.
    • נתיב נוכחי: [0 3 4 0 2 1]
    • מעגל: [0]
  9. חזרה לקודקוד 1 : הוסף 1 למעגל.
    • נתיב נוכחי: [0 3 4 0 2]
    • מעגל: [0 1]
  10. חזרה לקודקוד 2 : הוסף 2 למעגל.
    • נתיב נוכחי: [0 3 4 0]
    • מעגל: [0 1 2]
  11. חזרה לקודקוד 0 : הוסף 0 למעגל.
    • נתיב נוכחי: [0 3 4]
    • מעגל: [0 1 2 0]
  12. חזרה לקודקוד 4 : הוסף 4 למעגל.
    • נתיב נוכחי: [0 3]
    • מעגל: [0 1 2 0 4]
  13. חזרה לקודקוד 3 : הוסף 3 למעגל.
    • נתיב נוכחי: [0]
    • מעגל: [0 1 2 0 4 3]
  14. חזרה לקודקוד 0 : הוסף 0 למעגל.
    • נתיב נוכחי: []
    • מעגל: [0 1 2 0 4 3 0]

להלן היישום של הגישה לעיל: 

C++
// C++ program to print Eulerian circuit in given // directed graph using Hierholzer algorithm #include    using namespace std; // Function to print Eulerian circuit vector<int> printCircuit(vector<vector<int>> &adj) {  int n = adj.size();  if (n == 0)  return {};  // Maintain a stack to keep vertices  // We can start from any vertex here we start with 0  vector<int> currPath;  currPath.push_back(0);  // list to store final circuit  vector<int> circuit;  while (currPath.size() > 0) {  int currNode = currPath[currPath.size() - 1];  // If there's remaining edge in adjacency list  // of the current vertex  if (adj[currNode].size() > 0) {    // Find and remove the next vertex that is  // adjacent to the current vertex  int nextNode = adj[currNode].back();  adj[currNode].pop_back();  // Push the new vertex to the stack  currPath.push_back(nextNode);  }  // back-track to find remaining circuit  else {  // Remove the current vertex and  // put it in the circuit  circuit.push_back(currPath.back());  currPath.pop_back();  }  }  // reverse the result vector   reverse(circuit.begin() circuit.end());    return circuit; } int main() {  vector<vector<int>> adj = {{2 3} {0} {1} {4} {0}};  vector<int> ans = printCircuit(adj);    for (auto v: ans) cout << v << ' ';  cout << endl;  return 0; } 
Java
// Java program to print Eulerian circuit in given // directed graph using Hierholzer algorithm import java.util.*; class GfG {  // Function to print Eulerian circuit  static List<Integer> printCircuit(List<List<Integer>> adj) {  int n = adj.size();  if (n == 0)  return new ArrayList<>();  // Maintain a stack to keep vertices  // We can start from any vertex here we start with 0  List<Integer> currPath = new ArrayList<>();  currPath.add(0);  // list to store final circuit  List<Integer> circuit = new ArrayList<>();  while (currPath.size() > 0) {  int currNode = currPath.get(currPath.size() - 1);  // If there's remaining edge in adjacency list  // of the current vertex  if (adj.get(currNode).size() > 0) {  // Find and remove the next vertex that is  // adjacent to the current vertex  int nextNode = adj.get(currNode).get(adj.get(currNode).size() - 1);  adj.get(currNode).remove(adj.get(currNode).size() - 1);  // Push the new vertex to the stack  currPath.add(nextNode);  }  // back-track to find remaining circuit  else {  // Remove the current vertex and  // put it in the circuit  circuit.add(currPath.get(currPath.size() - 1));  currPath.remove(currPath.size() - 1);  }  }  // reverse the result vector  Collections.reverse(circuit);  return circuit;  }  public static void main(String[] args) {  List<List<Integer>> adj = new ArrayList<>();  adj.add(new ArrayList<>(Arrays.asList(2 3)));  adj.add(new ArrayList<>(Arrays.asList(0)));   adj.add(new ArrayList<>(Arrays.asList(1)));   adj.add(new ArrayList<>(Arrays.asList(4)));   adj.add(new ArrayList<>(Arrays.asList(0)));   List<Integer> ans = printCircuit(adj);  for (int v : ans) System.out.print(v + ' ');  System.out.println();  } } 
Python
# Python program to print Eulerian circuit in given # directed graph using Hierholzer algorithm # Function to print Eulerian circuit def printCircuit(adj): n = len(adj) if n == 0: return [] # Maintain a stack to keep vertices # We can start from any vertex here we start with 0 currPath = [0] # list to store final circuit circuit = [] while len(currPath) > 0: currNode = currPath[-1] # If there's remaining edge in adjacency list # of the current vertex if len(adj[currNode]) > 0: # Find and remove the next vertex that is # adjacent to the current vertex nextNode = adj[currNode].pop() # Push the new vertex to the stack currPath.append(nextNode) # back-track to find remaining circuit else: # Remove the current vertex and # put it in the circuit circuit.append(currPath.pop()) # reverse the result vector circuit.reverse() return circuit if __name__ == '__main__': adj = [[2 3] [0] [1] [4] [0]] ans = printCircuit(adj) for v in ans: print(v end=' ') print() 
C#
// C# program to print Eulerian circuit in given // directed graph using Hierholzer algorithm using System; using System.Collections.Generic; class GfG {    // Function to print Eulerian circuit  static List<int> printCircuit(List<List<int>> adj) {  int n = adj.Count;  if (n == 0)  return new List<int>();  // Maintain a stack to keep vertices  // We can start from any vertex here we start with 0  List<int> currPath = new List<int> { 0 };  // list to store final circuit  List<int> circuit = new List<int>();  while (currPath.Count > 0) {  int currNode = currPath[currPath.Count - 1];  // If there's remaining edge in adjacency list  // of the current vertex  if (adj[currNode].Count > 0) {  // Find and remove the next vertex that is  // adjacent to the current vertex  int nextNode = adj[currNode][adj[currNode].Count - 1];  adj[currNode].RemoveAt(adj[currNode].Count - 1);  // Push the new vertex to the stack  currPath.Add(nextNode);  }  // back-track to find remaining circuit  else {  // Remove the current vertex and  // put it in the circuit  circuit.Add(currPath[currPath.Count - 1]);  currPath.RemoveAt(currPath.Count - 1);  }  }  // reverse the result vector  circuit.Reverse();  return circuit;  }  static void Main(string[] args) {  List<List<int>> adj = new List<List<int>> {  new List<int> { 2 3 }  new List<int> { 0 }  new List<int> { 1 }  new List<int> { 4 }  new List<int> { 0 }  };  List<int> ans = printCircuit(adj);  foreach (int v in ans) {  Console.Write(v + ' ');  }  Console.WriteLine();  } } 
JavaScript
// JavaScript program to print Eulerian circuit in given // directed graph using Hierholzer algorithm // Function to print Eulerian circuit function printCircuit(adj) {  let n = adj.length;  if (n === 0)  return [];  // Maintain a stack to keep vertices  // We can start from any vertex here we start with 0  let currPath = [0];  // list to store final circuit  let circuit = [];  while (currPath.length > 0) {  let currNode = currPath[currPath.length - 1];  // If there's remaining edge in adjacency list  // of the current vertex  if (adj[currNode].length > 0) {  // Find and remove the next vertex that is  // adjacent to the current vertex  let nextNode = adj[currNode].pop();  // Push the new vertex to the stack  currPath.push(nextNode);  }  // back-track to find remaining circuit  else {  // Remove the current vertex and  // put it in the circuit  circuit.push(currPath.pop());  }  }  // reverse the result vector  circuit.reverse();  return circuit; } let adj = [[2 3] [0] [1] [4] [0]]; let ans = printCircuit(adj); for (let v of ans) {  console.log(v ' '); } 

תְפוּקָה
0 3 4 0 2 1 0 

מורכבות זמן:  O(V + E) כאשר V הוא מספר הקודקודים ו-E הוא מספר הקצוות בגרף. הסיבה לכך היא כי האלגוריתם מבצע חיפוש עומק ראשון (DFS) ומבקר בכל קודקוד ובכל קצה בדיוק פעם אחת. אז לכל קודקוד לוקח זמן O(1) לבקר אותו ולכל קצה לוקח זמן O(1) לחצות אותו.

מורכבות החלל  : O(V + E) כפי שהאלגוריתם משתמש במחסנית לאחסון הנתיב הנוכחי וברשימה לאחסון המעגל הסופי. הגודל המקסימלי של הערימה יכול להיות V + E במקרה הגרוע ולכן מורכבות החלל היא O(V + E).

צור חידון