例题

单链表翻转 LeetCode 206

这个问题可以使用递归和非递归两种方法解决。

递归算法实现:

  1. ListNode* reverseList(ListNode* head)
  2. {
  3. if(NULL == head || NULL == head->next)
  4. return head;
  5. ListNode * p = reverseList(head->next);
  6. head->next->next = head;
  7. head->next = NULL;
  8. return p;
  9. }

非递归算法实现:

  1. ListNode* reverseList(ListNode* head) {
  2. ListNode *curr = head;
  3. if (curr == NULL) {
  4. return NULL;
  5. }
  6. ListNode *prev = NULL, *temp = NULL;
  7. while (curr != NULL) {
  8. temp = curr->next;
  9. curr->next = prev;
  10. prev = curr;
  11. curr = temp;
  12. }
  13. return prev;
  14. }

单链表判断是否有环 LeetCode 141

最容易想到的思路是存一个所有 Node 地址的 Hash 表,从头开始遍历,将 Node 存到 Hash 表中,如果出现了重复,则说明链表有环。

一个经典的方法是双指针(也叫快慢指针),使用两个指针遍历链表,一个指针一次走一步,另一个一次走两步,如果链表有环,两个指针必然相遇。

双指针算法实现:

  1. bool hasCycle(ListNode *head) {
  2. if (head == nullptr) {
  3. return false;
  4. }
  5. ListNode *fast,*slow;
  6. slow = head;
  7. fast = head->next;
  8. while (fast && fast->next) {
  9. slow = slow->next;
  10. fast = fast->next->next;
  11. if (slow == fast) {
  12. return true;
  13. }
  14. }
  15. return false;
  16. }

单链表找环入口 LeetCode 141

作为上一题的扩展,为了找到环所在的位置,在快慢指针相遇的时候,此时慢指针没有遍历完链表,再设置一个指针从链表头部开始遍历,这两个指针相遇的点,就是链表环的入口。

算法实现:

  1. ListNode *detectCycle(ListNode *head) {
  2. if (head == nullptr) {
  3. return nullptr;
  4. }
  5. ListNode *fast,*slow;
  6. slow = head;
  7. fast = head;
  8. while (fast && fast->next) {
  9. slow = slow->next;
  10. fast = fast->next->next;
  11. if (slow == fast) {
  12. ListNode *slow2 = head;
  13. while (slow2 != slow) {
  14. slow = slow->next;
  15. slow2 = slow2->next;
  16. }
  17. return slow2;
  18. }
  19. }
  20. return nullptr;
  21. }

单链表找交点 LeetCode 160

和找环的方法类似,同样可以使用 Hash 表存储所有节点,发现重复的节点即交点。

一个容易想到的方法是,先得到两个链表的长度,然后得到长度的差值 distance,两个指针分别从两个链表头部遍历,其中较长链表指针先走 distance 步,然后同时向后走,当两个指针相遇的时候,即链表的交点:

  1. int getListLength(ListNode *head) {
  2. if (head == nullptr) {
  3. return 0;
  4. }
  5. int length = 0;
  6. ListNode *p = head;
  7. while (p!=nullptr) {
  8. p = p->next;
  9. length ++;
  10. }
  11. return length;
  12. }
  13. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  14. int lengthA = getListLength(headA);
  15. int lengthB = getListLength(headB);
  16. if (lengthA > lengthB) {
  17. std::swap(headA, headB);
  18. };
  19. int distance = abs(lengthB - lengthA);
  20. ListNode *p1 = headA;
  21. ListNode *p2 = headB;
  22. while(distance--) {
  23. p2 = p2->next;
  24. }
  25. while (p1 != nullptr && p2 != nullptr) {
  26. if (p1 == p2)
  27. return p1;
  28. p1 = p1->next;
  29. p2 = p2->next;
  30. }
  31. return NULL;
  32. }

另一个较快的方法时,两个指针 pa,pb 分别从 headA,headB开始遍历,当 pa 遍历到尾部的时候,指向 headB,当 pb 遍历到尾部的时候,转向 headA。当两个指针再次相遇的时候,如果两个链表有交点,则指向交点,如果没有则指向 NULL:

  1. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  2. ListNode *pa = headA;
  3. ListNode *pb = headB;
  4. while (pa != pb) {
  5. pa = pa != nullptr ? pa->next : headB;
  6. pb = pb != nullptr ? pb->next : headA;
  7. }
  8. return pa;
  9. }

单链表找中间节点 LeetCode 876

用快慢指针法,当快指针走到链表结尾时,慢指针刚好走到链表的中间:

  1. ListNode* middleNode(ListNode* head) {
  2. ListNode *slow = head;
  3. ListNode *fast = head;
  4. while (fast && fast->next) {
  5. slow = slow->next;
  6. fast = fast->next->next;
  7. }
  8. return slow;
  9. }

单链表合并 LeetCode 21

两个链表本身都是排序过的,把两个链表从头节点开始,逐个节点开始进行比较,最后剩下的节点接到尾部:

  1. ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
  2. if (l1 == nullptr) {
  3. return l2;
  4. }
  5. if (l2 == nullptr) {
  6. return l1;
  7. }
  8. ListNode dummy(-1);
  9. ListNode *p = &dummy;
  10. for (; l1 && l2; p = p->next) {
  11. if (l1->val < l2->val) {
  12. p->next = l1;
  13. l1 = l1->next;
  14. } else {
  15. p->next = l2;
  16. l2 = l2->next;
  17. }
  18. }
  19. p->next = l1 != nullptr ? l1 : l2;
  20. return dummy.next;
  21. }