[关闭]
@myecho 2019-03-24T21:42:44.000000Z 字数 7320 阅读 719

链表

数据结构


题目链接:http://blog.csdn.net/u011412619/article/details/44541143

链表的创建

通常head就是第一个具有value的节点,而tail->next指向一个NULL节点。
链表的插入有头插法和尾插法两种实现方式,通常取决于题目所要求的元素顺序。
头插法:
初始head = NULL
new -> next = head;
head = new;
尾插法:
初始tail = node1
tail->next = new
tail = new

求单链表中结点的个数

  1. int GetListLength(ListNode * pHead)
  2. {
  3. if(pHead == NULL)
  4. return 0;
  5. unsigned int nLength = 0;
  6. ListNode * pCurrent = pHead;
  7. while(pCurrent != NULL)
  8. {
  9. nLength++;
  10. pCurrent = pCurrent -> Next;
  11. }
  12. return nLength;
  13. }

反转链表

  1. ListNode * ReverseList(ListNode * pHead)
  2. {
  3. if(pHead == NULL || pHead->Next == NULL)
  4. return pHead;
  5. ListNode * pReversedHead = NULL;
  6. ListNode * pCurrent = pHead;
  7. while(pCurrent != NULL)
  8. {
  9. ListNode * pTemp = pCurrent; //取下一个节点
  10. pCurrent = pCurrent->Next;
  11. pTemp->Next = pReversedHead; //头插法得到的链表正好是逆序
  12. pReversedHead = pTemp;
  13. }
  14. return pReversedHead;
  15. }

查找单链表中的倒数第K个结点(k > 0)

  1. ListNode * RGetKthNode(ListNode * pHead, int k) //函数名前面的R代表反向
  2. {
  3. if(k == 0 || pHead == NULL)
  4. return NULL;
  5. ListNode * pAhead = pHead;
  6. ListNode * pBehind = pHead;
  7. while(k > 1 && pAhead != NULL) //前面的指针先走到正向第k个结点
  8. {
  9. pAhead = pAhead -> Next;
  10. k--;
  11. }
  12. if(k > 1 || pAhead == NULL) // 结点个数小于k,返回NULL
  13. return NULL;
  14. while(pAhead -> Next != NULL) //前后两个指针一起向前走,直到前面的指针指向最后一个结点
  15. {
  16. pBehind = pBehind -> Next;
  17. pAhead = pAhead -> Next;
  18. }
  19. return pBehind; // 后面的指针所指结点就是倒数第k个结点
  20. }

查找单链表的中间结点

  1. ListNode * GetMiddleNode(ListNode * pHead)
  2. {
  3. if(pHead == NULL || pHead -> Next == NULL)
  4. return pHead;
  5. ListNode * pAhead = pHead;
  6. ListNode * pBehind = pHead;
  7. while(pAhead->Next != NULL) //前面指针每次走两步,直到指向最后一个结点,后面指针每次走一步
  8. {
  9. pAhead = pAhead->Next;
  10. pBehind = pBehind->Next;
  11. if(pAhead->Next != NULL)
  12. pAhead = pAhead->Next;
  13. }
  14. return pBehind; // 后面的指针所指结点即为中间结点
  15. }

从尾到头打印单链表

  1. //使用栈
  2. void RPrintList(ListNode * pHead)
  3. {
  4. std::stack<ListNode *> s;
  5. ListNode * pNode = pHead;
  6. while(pNode != NULL)
  7. {
  8. s.push(pNode);
  9. pNode = pNode->Next;
  10. }
  11. while(!s.empty())
  12. {
  13. pNode = s.top();
  14. printf("%d\t", pNode->value);
  15. s.pop();
  16. }
  17. }
  18. //使用系统栈,递归函数
  19. // 从尾到头打印链表,使用递归
  20. void RPrintList(ListNode * pHead)
  21. {
  22. if(pHead == NULL)
  23. {
  24. return;
  25. }
  26. else
  27. {
  28. RPrintList(pHead->Next);
  29. printf("%d\t", pHead->value);
  30. }
  31. }

合并两个有序列表

  1. ListNode * MergeSortedList(ListNode * pHead1, ListNode * pHead2)
  2. {
  3. if(pHead1 == NULL)
  4. return pHead2;
  5. if(pHead2 == NULL)
  6. return pHead1;
  7. ListNode * pHeadMerged = NULL;
  8. if(pHead1->m_nKey < pHead2->m_nKey)
  9. {
  10. pHeadMerged = pHead1;
  11. pHeadMerged->Next = NULL;
  12. pHead1 = pHead1->Next;
  13. }
  14. else
  15. {
  16. pHeadMerged = pHead2;
  17. pHeadMerged->Next = NULL;
  18. pHead2 = pHead2->Next;
  19. }
  20. ListNode * pTemp = pHeadMerged;//以上首先初始化了头部,因为要使用尾插法构造链表,不能使用头部插入法
  21. while(pHead1 != NULL && pHead2 != NULL)
  22. {
  23. if(pHead1->m_nKey < pHead2->m_nKey)
  24. {
  25. pTemp->Next = pHead1;
  26. pHead1 = pHead1->Next;
  27. pTemp = pTemp->Next;
  28. pTemp->Next = NULL;
  29. }
  30. else
  31. {
  32. pTemp->Next = pHead2;
  33. pHead2 = pHead2->Next;
  34. pTemp = pTemp->Next;
  35. pTemp->Next = NULL;
  36. }
  37. }
  38. if(pHead1 != NULL)
  39. pTemp->Next = pHead1;
  40. else if(pHead2 != NULL)
  41. pTemp->Next = pHead2;
  42. return pHeadMerged;
  43. }
  44. //递归版本
  45. ListNode * MergeSortedList(ListNode * pHead1, ListNode * pHead2)
  46. {
  47. if(pHead1 == NULL)
  48. return pHead2;
  49. if(pHead2 == NULL)
  50. return pHead1;
  51. ListNode * pHeadMerged = NULL;
  52. if(pHead1->m_nKey < pHead2->m_nKey)
  53. {
  54. pHeadMerged = pHead1;
  55. pHeadMerged->Next = MergeSortedList(pHead1->Next, pHead2);
  56. }
  57. else
  58. {
  59. pHeadMerged = pHead2;
  60. pHeadMerged->Next = MergeSortedList(pHead1, pHead2->Next);
  61. }
  62. return pHeadMerged;
  63. }

扩展延伸---合并k个链表
思路:
1. 用最小堆去维护,取走一个,再把该元素下一个放入最小堆,使用时直接调用STL的priority_queue即可
2. 分治法 Divide and Conquer Approach,不停的两两合并

ListNode *mergeKLists(vector<ListNode *> &lists) {
        if (lists.size() == 0) return NULL;
        int n = lists.size();
        while (n > 1) {
            int k = (n + 1) / 2;
            for (int i = 0; i < n / 2; ++i) {
                lists[i] = mergeTwoLists(lists[i], lists[i + k]);
            }
            n = k;
        }
        return lists[0];
    } //类似于非递归实现的归并排序

判断一个单链表中是否有环

  1. bool HasCircle(ListNode * pHead)
  2. {
  3. ListNode * pFast = pHead; // 快指针每次前进两步
  4. ListNode * pSlow = pHead; // 慢指针每次前进一步
  5. while(pFast != NULL && pFast->Next != NULL)
  6. {
  7. pFast = pFast->Next->Next;
  8. pSlow = pSlow->Next;
  9. if(pSlow == pFast) // 相遇,存在环
  10. return true;
  11. }
  12. return false;
  13. }
  14. //如果跑的快的走了两圈的话,那么一定跑的慢的一定只走了一圈,距离差两倍

求进入环的第一个节点

首先判断是否存在环,若不存在结束。在环中的一个节点处断开,这样就形成了两个相交的单链表,求进入环中的第一个节点也就转换成了求两个单链表相交的第一个节点。
见leetcode Linked List Cycle,方法二: 通过证明,得到了起始点的更简单的思路。首先得到环的长度,然后设置两个指针一个前一个后即可~ 长度(在相遇点,让一个指针再跑一圈,下次相遇时走过的长度即为环的长度)
http://www.cnblogs.com/x1957/p/3406448.html
注意这是一个链表不是两个。

  1. ListNode* GetFirstNodeInCircle(ListNode * pHead)
  2. {
  3. if(pHead == NULL || pHead->Next == NULL)
  4. return NULL;
  5. ListNode * pFast = pHead;
  6. ListNode * pSlow = pHead;
  7. while(pFast != NULL && pFast->Next != NULL) //判断是否有环
  8. {
  9. pSlow = pSlow->Next;
  10. pFast = pFast->Next->Next;
  11. if(pSlow == pFast)
  12. break;
  13. }
  14. if(pFast == NULL || pFast->Next == NULL)
  15. return NULL;
  16. // 将环中的此节点作为假设的尾节点,将它变成两个单链表相交问题 //这里最好画个图看一下两个链表的形状是如何的
  17. ListNode * pAssumedTail = pSlow;
  18. ListNode * pHead1 = pHead;
  19. ListNode * pHead2 = pAssumedTail->Next;
  20. ListNode * pNode1, * pNode2;
  21. int len1 = 1;
  22. ListNode * pNode1 = pHead1;
  23. while(pNode1 != pAssumedTail)
  24. {
  25. pNode1 = pNode1->Next;
  26. len1++;
  27. }
  28. int len2 = 1;
  29. ListNode * pNode2 = pHead2;
  30. while(pNode2 != pAssumedTail)
  31. {
  32. pNode2 = pNode2->Next;
  33. len2++;
  34. }
  35. pNode1 = pHead1;
  36. pNode2 = pHead2;
  37. if(len1 > len2)
  38. {
  39. int k = len1 - len2;
  40. while(k--)
  41. pNode1 = pNode1->Next;
  42. }
  43. else
  44. {
  45. int k = len2 - len1;
  46. while(k--)
  47. pNode2 = pNode2->Next;
  48. }
  49. while(pNode1 != pNode2)
  50. {
  51. pNode1 = pNode1->Next;
  52. pNode2 = pNode2->Next;
  53. }
  54. return pNode1;
  55. }

判断两个单链表是否相交

两个链表相交意味着他们有着一段公共的串。

  1. bool IsIntersected(ListNode * pHead1, ListNode * pHead2)
  2. {
  3. if(pHead1 == NULL || pHead2 == NULL)
  4. return false;
  5. ListNode * pTail1 = pHead1;
  6. while(pTail1->Next != NULL)
  7. pTail1 = pTail1->Next;
  8. ListNode * pTail2 = pHead2;
  9. while(pTail2->Next != NULL)
  10. pTail2 = pTail2->Next;
  11. return pTail1 == pTail2;
  12. }

求两个单链表相交的第一个节点

对第一个链表遍历,计算长度len1,同时保存最后一个节点的地址。
对第二个链表遍历,计算长度len2,同时检查最后一个节点是否和第一个链表的最后一个节点相同,若不相同,不相交,结束。
两个链表均从头节点开始,假设len1大于len2,那么将第一个链表先遍历len1-len2个节点,此时两个链表当前节点到第一个相交节点的距离就相等了,然后一起向后遍历,直到两个节点的地址相同。

  1. ListNode* GetFirstCommonNode(ListNode * pHead1, ListNode * pHead2)
  2. {
  3. if(pHead1 == NULL || pHead2 == NULL)
  4. return NULL;
  5. int len1 = 1;
  6. ListNode * pTail1 = pHead1;
  7. while(pTail1->Next != NULL)
  8. {
  9. pTail1 = pTail1->Next;
  10. len1++;
  11. }
  12. int len2 = 1;
  13. ListNode * pTail2 = pHead2;
  14. while(pTail2->Next != NULL)
  15. {
  16. pTail2 = pTail2->Next;
  17. len2++;
  18. }
  19. if(pTail1 != pTail2) // 不相交直接返回NULL
  20. return NULL;
  21. ListNode * pNode1 = pHead1;
  22. ListNode * pNode2 = pHead2;
  23. // 先对齐两个链表的当前结点,使之到尾节点的距离相等
  24. if(len1 > len2)
  25. {
  26. int k = len1 - len2;
  27. while(k--)
  28. pNode1 = pNode1->Next;
  29. }
  30. else
  31. {
  32. int k = len2 - len1;
  33. while(k--)
  34. pNode2 = pNode2->Next;
  35. }
  36. while(pNode1 != pNode2)
  37. {
  38. pNode1 = pNode1->Next;
  39. pNode2 = pNode2->Next;
  40. }
  41. return pNode1;
  42. }

O(1)时间复杂度删除节点

我们可以把该节点的下一个节点的数据复制到该节点,然后删除下一个节点即可。要注意最后一个节点的情况,此时只能按照常规方法删除(记录前一个节点)。

  1. void Delete(ListNode * pHead, ListNode * pToBeDeleted)
  2. {
  3. if(pToBeDeleted == NULL)
  4. return;
  5. if(pToBeDeleted->Next != NULL)
  6. {
  7. pToBeDeleted->m_nKey = pToBeDeleted->Next->m_nKey;
  8. ListNode * temp = pToBeDeleted->Next;
  9. pToBeDeleted->Next = pToBeDeleted->Next->Next;
  10. delete temp;
  11. }
  12. else // 要删除的是最后一个节点
  13. {
  14. if(pHead == pToBeDeleted) // 链表中只有一个节点的情况
  15. {
  16. pHead = NULL;
  17. delete pToBeDeleted;
  18. }
  19. else
  20. {
  21. ListNode * pNode = pHead;
  22. while(pNode->Next != pToBeDeleted) //找到前一个节点
  23. pNode = pNode->Next;
  24. pNode->Next = NULL;
  25. delete pToBeDeleted;
  26. }
  27. }
  28. }

逆转[m,n]区间内的链表

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. using namespace std;
  5. struct Listnode{
  6. int val;
  7. Listnode* next;
  8. Listnode(int x): val(x),next(NULL){}
  9. };
  10. Listnode* RerveseList(Listnode* head, int m, int n) {
  11. if (head == NULL) return NULL;
  12. Listnode * prev = head;
  13. for (int i = 0; i < m - 1; ++i) {
  14. prev = prev -> next;
  15. }
  16. Listnode * cur = prev -> next;
  17. Listnode * phead = NULL;
  18. for (int i = m; i <= n; ++i) {
  19. Listnode * temp = cur;
  20. cur = cur -> next;
  21. temp -> next = phead;
  22. phead = temp;
  23. }
  24. prev -> next -> next = cur;
  25. prev -> next = phead;
  26. return head;
  27. }
  28. void print(Listnode* head) {
  29. Listnode* phead;
  30. while (phead != NULL) {
  31. cout << phead -> val << endl;
  32. phead = phead -> next;
  33. }
  34. return;
  35. }
  36. int main() {
  37. Listnode * head = NULL;
  38. for (int i = 0; i < 5; ++i) {
  39. Listnode * temp = new Listnode(i);
  40. temp -> next = head;
  41. head = temp;
  42. }
  43. head = RerveseList(head, 1, 3);
  44. print(head);
  45. return 0;
  46. }

链表与其他数据结构的结合

https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/description/
感觉是遍历过程中再用个stack保存一下遍历中断的节点?

https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/description/
https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/
https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/discuss/162900/4-progressively-better-clean-concise-c++-solutions.-O(n)-time-O(logn)-space.-With-descriptions

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注