400-028-6601
建站资讯

网站建设资讯

为你提供网站建设行业资讯、网站优化知识、主机域名邮箱、网站开发常见问题等。

五个解决办法教你C++中检测链表中的循环

给定一个链表,检查链表是否有循环。下图显示了带有循环的链表。

专注于为中小企业提供网站建设、网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业宁德免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了千余家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

以下是执行此操作的不同方法 

解决方案1:散列方法:

遍历该列表,并将节点地址始终放在哈希表中。在任何时候,如果达到NULL,则返回false,如果当前节点的下一个指向Hash中先前存储的任何节点,则返回true。

 
 
 
  1. #include  
  2. using namespace std; 
  3. struct Node { 
  4.     int data; 
  5.     struct Node* next; 
  6. }; 
  7.   
  8. void push(struct Node** head_ref, int new_data) 
  9. { 
  10.     struct Node* new_node = new Node; 
  11.     new_node->data = new_data; 
  12.     new_node->next = (*head_ref); 
  13.     (*head_ref) = new_node; 
  14. } 
  15. bool detectLoop(struct Node* h) 
  16. { 
  17.     unordered_set s; 
  18.     while (h != NULL) { 
  19.         if (s.find(h) != s.end()) 
  20.             return true; 
  21.         s.insert(h); 
  22.   
  23.         h = h->next; 
  24.     } 
  25.   
  26.     return false; 
  27. } 
  28. int main() 
  29. { 
  30.     struct Node* head = NULL; 
  31.   
  32.     push(&head, 20); 
  33.     push(&head, 4); 
  34.     push(&head, 15); 
  35.     push(&head, 10); 
  36.     head->next->next->next->next = head; 
  37.   
  38.     if (detectLoop(head)) 
  39.         cout << "Loop found"; 
  40.     else 
  41.         cout << "No Loop"; 
  42.   
  43.     return 0; 
  44. } 

复杂度分析:

时间复杂度: O(n)。
只需循环一次即可。

辅助空间: O(n)。
n是将值存储在哈希图中所需的空间。

解决方案2:通过修改链表数据结构,无需哈希图即可解决此问题。
方法:此解决方案需要修改基本链表数据结构。

  • 每个节点都有一个访问标志。
  • 遍历链接列表并继续标记访问的节点。
  • 如果您再次看到一个访问过的节点,那么就会有一个循环。该解决方案适用于O(n),但每个节点都需要其他信息。
  • 此解决方案的一种变体不需要修改基本数据结构,可以使用哈希来实现,只需将访问的节点的地址存储在哈希中,如果您看到哈希中已经存在的地址,则存在一个循环。

C++:

 
 
 
  1. #include  
  2. using namespace std; 
  3. struct Node { 
  4.     int data; 
  5.     struct Node* next; 
  6.     int flag; 
  7. }; 
  8.   
  9. void push(struct Node** head_ref, int new_data) 
  10. { 
  11.     struct Node* new_node = new Node; 
  12.     new_node->data = new_data; 
  13.   
  14.     new_node->flag = 0; 
  15.     new_node->next = (*head_ref); 
  16.     (*head_ref) = new_node; 
  17. } 
  18. bool detectLoop(struct Node* h) 
  19. { 
  20.     while (h != NULL) { 
  21.         if (h->flag == 1) 
  22.             return true; 
  23.         h->flag = 1; 
  24.   
  25.         h = h->next; 
  26.     } 
  27.   
  28.     return false; 
  29. } 
  30. int main() 
  31. { 
  32.     struct Node* head = NULL; 
  33.   
  34.     push(&head, 20); 
  35.     push(&head, 4); 
  36.     push(&head, 15); 
  37.     push(&head, 10); 
  38.     head->next->next->next->next = head; 
  39.   
  40.     if (detectLoop(head)) 
  41.         cout << "Loop found"; 
  42.     else 
  43.         cout << "No Loop"; 
  44.   
  45.     return 0; 
  46. } 

复杂度分析:

时间复杂度: O(n)。
只需循环一次即可。

辅助空间: O(1)。
不需要额外的空间。

解决方案3:Floyd的循环查找算法
方法:这是最快的方法,下面进行了介绍:

  • 使用两个指针遍历链表。
  • 将一个指针(slow_p)移动一个,将另一个指针(fast_p)移动两个。
  • 如果这些指针在同一节点相遇,则存在循环。如果指针不符合要求,则链接列表没有循环。

Floyd的循环查找算法的实现:

 
 
 
  1. #include  
  2. using namespace std; 
  3. class Node { 
  4. public: 
  5.     int data; 
  6.     Node* next; 
  7. }; 
  8.   
  9. void push(Node** head_ref, int new_data) 
  10. { 
  11.     Node* new_node = new Node(); 
  12.     new_node->data = new_data; 
  13.     new_node->next = (*head_ref); 
  14.     (*head_ref) = new_node; 
  15. } 
  16.   
  17. int detectLoop(Node* list) 
  18. { 
  19.     Node *slow_p = list, *fast_p = list; 
  20.   
  21.     while (slow_p && fast_p && fast_p->next) { 
  22.         slow_p = slow_p->next; 
  23.         fast_p = fast_p->next->next; 
  24.         if (slow_p == fast_p) { 
  25.             return 1; 
  26.         } 
  27.     } 
  28.     return 0; 
  29. } 
  30. int main() 
  31. { 
  32.     Node* head = NULL; 
  33.   
  34.     push(&head, 20); 
  35.     push(&head, 4); 
  36.     push(&head, 15); 
  37.     push(&head, 10); 
  38.     head->next->next->next->next = head; 
  39.     if (detectLoop(head)) 
  40.         cout << "Loop found"; 
  41.     else 
  42.         cout << "No Loop"; 
  43.     return 0; 
  44. } 

解决方案4:在不修改链接列表数据结构的情况下标记访问的节点
在此方法中,将创建一个临时节点。使遍历的每个节点的下一个指针指向该临时节点。这样,我们将节点的下一个指针用作标志来指示该节点是否已遍历。检查每个节点以查看下一个节点是否指向临时节点。在循环的第一个节点的情况下,第二次遍历该条件将成立,因此我们发现该循环存在。如果遇到一个指向null的节点,则循环不存在。
下面是上述方法的实现:

 
 
 
  1. #include  
  2. using namespace std; 
  3.   
  4. struct Node { 
  5.     int key; 
  6.     struct Node* next; 
  7. }; 
  8.   
  9. Node* newNode(int key) 
  10. { 
  11.     Node* temp = new Node; 
  12.     temp->key = key; 
  13.     temp->next = NULL; 
  14.     return temp; 
  15. } 
  16. void printList(Node* head) 
  17. { 
  18.     while (head != NULL) { 
  19.         cout << head->key << " "; 
  20.         head = head->next; 
  21.     } 
  22.     cout << endl; 
  23. } 
  24. bool detectLoop(Node* head) 
  25. { 
  26.     Node* temp = new Node; 
  27.     while (head != NULL) { 
  28.         if (head->next == NULL) { 
  29.             return false; 
  30.         } 
  31.         if (head->next == temp) { 
  32.             return true; 
  33.         } 
  34.         Node* nex = head->next; 
  35.         head->next = temp; 
  36.         head = nex; 
  37.     } 
  38.   
  39.     return false; 
  40. } 
  41. int main() 
  42. { 
  43.     Node* head = newNode(1); 
  44.     head->next = newNode(2); 
  45.     head->next->next = newNode(3); 
  46.     head->next->next->next = newNode(4); 
  47.     head->next->next->next->next = newNode(5); 
  48.     head->next->next->next->next->next = head->next->next; 
  49.   
  50.     bool found = detectLoop(head); 
  51.     if (found) 
  52.         cout << "Loop Found"; 
  53.     else 
  54.         cout << "No Loop"; 
  55.   
  56.     return 0; 
  57. } 

复杂度分析:

时间复杂度: O(n)。
只需循环一次即可。

辅助空间: O(1)。
不需要空间。

解决方案5:存放长度

在此方法中,将创建两个指针,第一个(始终指向头)和最后一个指针。每次最后一个指针移动时,我们都会计算第一个和最后一个之间的节点数,并检查当前节点数是否大于先前的节点数,如果是,我们通过移动最后一个指针进行操作,否则就意味着我们已经到达循环的终点,因此我们相应地返回输出。

 
 
 
  1. #include  
  2. using namespace std; 
  3.   
  4. struct Node { 
  5.     int key; 
  6.     struct Node* next; 
  7. }; 
  8.   
  9. Node* newNode(int key) 
  10. { 
  11.     Node* temp = new Node; 
  12.     temp->key = key; 
  13.     temp->next = NULL; 
  14.     return temp; 
  15. } 
  16. void printList(Node* head) 
  17. { 
  18.     while (head != NULL) { 
  19.         cout << head->key << " "; 
  20.         head = head->next; 
  21.     } 
  22.     cout << endl; 
  23. } 
  24. int distance(Node* first, Node* last) 
  25. { 
  26.     int counter = 0; 
  27.   
  28.     Node* curr; 
  29.     curr = first; 
  30.   
  31.     while (curr != last) { 
  32.         counter += 1; 
  33.         curr = curr->next; 
  34.     } 
  35.   
  36.     return counter + 1; 
  37. } 
  38. bool detectLoop(Node* head) 
  39.     Node* temp = new Node; 
  40.   
  41.     Node *first, *last; 
  42.     first = head; 
  43.     last = head; 
  44.     int current_length = 0; 
  45.     int prev_length = -1; 
  46.   
  47.     while (current_length > prev_length && last != NULL) { 
  48.           prev_length = current_length; 
  49.         current_length = distance(first, last); 
  50.         last = last->next; 
  51.     } 
  52.       
  53.     if (last == NULL) { 
  54.         return false; 
  55.     } 
  56.     else {  
  57.         return true; 
  58.     } 
  59. } 
  60. int main() 
  61. { 
  62.     Node* head = newNode(1); 
  63.     head->next = newNode(2); 
  64.     head->next->next = newNode(3); 
  65.     head->next->next->next = newNode(4); 
  66.     head->next->next->next->next = newNode(5); 
  67.     head->next->next->next->next->next = head->next->next; 
  68.   
  69.     bool found = detectLoop(head); 
  70.     if (found) 
  71.         cout << "Loop Found"; 
  72.     else 
  73.         cout << "No Loop Found"; 
  74.   
  75.     return 0; 
  76. } 

}


当前题目:五个解决办法教你C++中检测链表中的循环
标题链接:http://www.hnjierui.cn/article/dpdoepe.html

其他资讯