2014-03-18 02:41
题目:给定一个带有环的单链表,找出环的入口节点。
解法1:用hash来检测重复节点肯定是容易想而且效率也高的好办法。
代码:
1 // 2.6 You have a circular Linked List: a->b->c->d->e->c. Find where the cycle starts. 2 #include <cstdio> 3 #include <unordered_set> 4 using namespace std; 5 6 struct ListNode { 7 int val; 8 ListNode *next; 9 ListNode(int x): val(x), next(nullptr) {}; 10 }; 11 12 class Solution { 13 public: 14 ListNode* firstFirstNodeInCycle(ListNode *head) { 15 if (head == nullptr) { 16 return head; 17 } 18 19 // hash the pointers. 20 unordered_set<ListNode *> us; 21 ListNode *ptr; 22 23 ptr = head; 24 while (ptr != nullptr) { 25 if (us.find(ptr) != us.end()) { 26 // the first node of the cycle is found. 27 return ptr; 28 } else { 29 us.insert(ptr); 30 ptr = ptr->next; 31 } 32 } 33 34 // the list has no cycle. 35 return nullptr; 36 } 37 }; 38 39 int main() 40 { 41 int i; 42 int n, k; 43 int val; 44 struct ListNode *head, *tail, *ptr; 45 Solution sol; 46 47 while (scanf("%d", &n) == 1 && n > 0) { 48 // create a linked list 49 ptr = head = tail = nullptr; 50 for (i = 0; i < n; ++i) { 51 scanf("%d", &val); 52 if (head == nullptr) { 53 head = ptr = new ListNode(val); 54 } else { 55 ptr->next = new ListNode(val); 56 ptr = ptr->next; 57 } 58 } 59 tail = ptr; 60 61 // create a cycle in the list 62 scanf("%d", &k); 63 if (k >= 1 && k <= n) { 64 ptr = head; 65 for (i = 1; i < k; ++i) { 66 ptr = ptr->next; 67 } 68 tail->next = ptr; 69 } 70 71 // find the first node in the cycle. 72 ListNode *first_node = sol.firstFirstNodeInCycle(head); 73 if (first_node != nullptr) { 74 printf("%d\n", first_node->val); 75 } else { 76 printf("no cycle\n"); 77 } 78 79 /* 80 // print the list 81 ptr = head; 82 for (i = 0; i < n; ++i) { 83 printf("%d->", ptr->val); 84 ptr = ptr->next; 85 } 86 printf("\n"); 87 */ 88 89 // delete the list 90 for (i = 0; i < n; ++i) { 91 ptr = head->next; 92 delete head; 93 head = ptr; 94 } 95 } 96 97 return 0; 98 }
解法2:如果你非不让我用hash,就只有观察一下几个特殊节点了。既然这个环有“第一个”点,那肯定也有“最后一个”点。我特地打了引号,是因为最后一个点恰好指向第一个。如果你从链表的头开始向后遍历,必然是先到达“第一个”,然后才能到达“最后一个的”。只有对于“最后一个”点,才会出现先到达ptr->next,后到达ptr的情况,所以如果存在符合这个情况的节点ptr,ptr->next就是我们要找的环的入口。这种方法效率不高而且不容易想,除了面试之外基本没有别的用处了。但为了面试淘汰一些人,这点用处也很重要了。
代码:
1 // 2.6 You have a circular Linked List: a->b->c->d->e->c. Find where the cycle starts. 2 #include <cstdio> 3 #include <unordered_set> 4 using namespace std; 5 6 struct ListNode { 7 int val; 8 ListNode *next; 9 ListNode(int x): val(x), next(nullptr) {}; 10 }; 11 12 class Solution { 13 public: 14 ListNode* firstFirstNodeInCycle(ListNode *head) { 15 if (head == nullptr) { 16 return head; 17 } 18 19 ListNode *p1, *p2; 20 21 p1 = head; 22 while (p1 != nullptr) { 23 p2 = head; 24 while (p2 != p1->next && p2 != nullptr) { 25 if (p2 == p1) { 26 break; 27 } else { 28 p2 = p2->next; 29 } 30 } 31 if (p2 == p1->next) { 32 return p2; 33 } else { 34 p1 = p1->next; 35 } 36 } 37 38 return nullptr; 39 } 40 }; 41 42 int main() 43 { 44 int i; 45 int n, k; 46 int val; 47 struct ListNode *head, *tail, *ptr; 48 Solution sol; 49 50 while (scanf("%d", &n) == 1 && n > 0) { 51 // create a linked list 52 ptr = head = tail = nullptr; 53 for (i = 0; i < n; ++i) { 54 scanf("%d", &val); 55 if (head == nullptr) { 56 head = ptr = new ListNode(val); 57 } else { 58 ptr->next = new ListNode(val); 59 ptr = ptr->next; 60 } 61 } 62 tail = ptr; 63 64 // create a cycle in the list 65 scanf("%d", &k); 66 if (k >= 1 && k <= n) { 67 ptr = head; 68 for (i = 1; i < k; ++i) { 69 ptr = ptr->next; 70 } 71 tail->next = ptr; 72 } 73 74 // find the first node in the cycle. 75 ListNode *first_node = sol.firstFirstNodeInCycle(head); 76 if (first_node != nullptr) { 77 printf("%d\n", first_node->val); 78 } else { 79 printf("no cycle\n"); 80 } 81 82 /* 83 // print the list 84 ptr = head; 85 for (i = 0; i < n; ++i) { 86 printf("%d->", ptr->val); 87 ptr = ptr->next; 88 } 89 printf("\n"); 90 */ 91 92 // delete the list 93 for (i = 0; i < n; ++i) { 94 ptr = head->next; 95 delete head; 96 head = ptr; 97 } 98 } 99 100 return 0; 101 }