• 链表相关OJ及方法总结


    目录​​​​​​​

    第一类:改变链接关系 

    第二类:快慢指针 


    第一类:改变链接关系 

    1. 删除链表中等于给定值 val 的所有结点。

    (1)原地删除

    1. struct ListNode* removeElements(struct ListNode* head, int val){
    2. if(head==NULL)
    3. {
    4. return head;
    5. }
    6. struct ListNode* cur=head,* pre=NULL,* ret;
    7. while(cur)
    8. {
    9. //需要删除cur
    10. if(cur->val==val)
    11. {
    12. //cur是首节点,需要改变头结点
    13. if(pre==NULL)
    14. {
    15. ret=cur;
    16. head=cur->next;
    17. cur=cur->next;
    18. }
    19. //直接删除即可
    20. else
    21. {
    22. ret=cur;
    23. pre->next=cur->next;
    24. cur=cur->next;
    25. }
    26. free(ret);
    27. }
    28. //不用删除,
    29. else
    30. {
    31. pre=cur;
    32. cur=cur->next;
    33. }
    34. }
    35. return head;
    36. }

    (2)建立一个新链表,将不用删除的节点依次尾插

    1. if(head==NULL)
    2. {
    3. return head;
    4. }
    5. struct ListNode* cur=head,* pre=NULL,*newhead=NULL,*newtail=NULL;
    6. while(cur)
    7. {
    8. //需要尾插cur
    9. if(cur->val!=val)
    10. {
    11. //cur是首节点,需要改变newhead
    12. if(!newhead)
    13. {
    14. newhead=newtail=cur;
    15. }
    16. //直接尾插即可
    17. else
    18. {
    19. newtail->next=cur;
    20. newtail=newtail->next;
    21. }
    22. }
    23. pre=cur;
    24. cur=cur->next;
    25. }
    26. if(newtail)
    27. newtail->next=NULL;
    28. return newhead;

    (3)在2的基础上使用带头节点链表

    1. struct ListNode* removeElements(struct ListNode* head, int val){
    2. if(head==NULL)
    3. {
    4. return head;
    5. }
    6. struct ListNode* cur=head,* pre=NULL;
    7. struct ListNode* newhead=(struct ListNode* )malloc(sizeof(struct ListNode)),*newtail=newhead;
    8. while(cur)
    9. {
    10. //需要尾插cur
    11. if(cur->val!=val)
    12. {
    13. newtail->next=cur;
    14. newtail=newtail->next;
    15. }
    16. pre=cur;
    17. cur=cur->next;
    18. }
    19. newtail->next=NULL;
    20. return newhead->next;
    21. }

    (4)递归

    1. struct ListNode* removeElements(struct ListNode* head, int val){
    2. if(head==NULL)
    3. {
    4. return head;
    5. }
    6. if(head->val==val)
    7. {
    8. return removeElements(head->next, val);
    9. }
    10. else
    11. {
    12. head->next=removeElements(head->next, val);
    13. return head;
    14. }
    15. }

    2. 反转一个单链表

     (1)原地逆转

    1. struct ListNode* reverseList(struct ListNode* head){
    2. struct ListNode* ret=NULL,*cur=head,*pre=NULL;
    3. while(cur)
    4. {
    5. ret=cur->next;
    6. cur->next=pre;
    7. pre=cur;
    8. cur=ret;
    9. }
    10. return pre;
    11. }

    (2)建立一个新链表,将节点依次头插(带头结点链表)

    1. struct ListNode* reverseList(struct ListNode* head){
    2. struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
    3. struct ListNode*cur=head,*ret;
    4. newhead->next=NULL;
    5. while(cur)
    6. {
    7. ret=cur->next;
    8. cur->next=newhead->next;
    9. newhead->next=cur;
    10. cur=ret;
    11. }
    12. return newhead->next;
    13. }

    (3)递归

    1. struct ListNode* reverseList(struct ListNode* head){
    2. if(head==NULL||head->next==NULL)
    3. {
    4. return head;
    5. }
    6. else
    7. {
    8. struct ListNode* newhead=reverseList(head->next);
    9. head->next->next=head;
    10. head->next=NULL;
    11. return newhead;
    12. }
    13. }

    第二类:快慢指针 

     3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则 返回第二个中间结点。

    (1)快慢指针

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

    (2).求出size再遍历

    1. struct ListNode* middleNode(struct ListNode* head){
    2. struct ListNode* cur = head;
    3. int size=0;
    4. while (cur) {
    5. size++;
    6. cur=cur->next;
    7. }
    8. cur = head;
    9. size/=2;
    10. while(size--)
    11. {
    12. cur=cur->next;
    13. }
    14. return cur;
    15. }

    (3)先遍历一遍将数据存入数组再处理

    1. struct ListNode* middleNode(struct ListNode* head){
    2. struct ListNode* cur = head;
    3. struct ListNode* a[180];
    4. int i=0;
    5. while (cur) {
    6. a[i++]=cur;
    7. cur=cur->next;
    8. }
    9. return a[i/2];
    10. }

    4. 输入一个链表,输出该链表中倒数第k个结点。

    (1)快慢指针

    1. struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    2. if(pListHead==NULL||k==0)
    3. return NULL;
    4. struct ListNode* fast,*slow;
    5. fast=slow=pListHead;
    6. while(k&&fast)
    7. {
    8. fast=fast->next;
    9. k--;
    10. }
    11. if(k)
    12. {
    13. return NULL;
    14. }
    15. while(fast)
    16. {
    17. fast=fast->next;
    18. slow=slow->next;
    19. }
    20. return slow;
    21. }

    (2).求出size再遍历size-k次

    1. struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    2. struct ListNode* cur = pListHead;
    3. int size=0;
    4. while (cur) {
    5. size++;
    6. cur=cur->next;
    7. }
    8. cur = pListHead;
    9. if(k>size)
    10. return NULL;
    11. size-=k;
    12. while(size--)
    13. {
    14. cur=cur->next;
    15. }
    16. return cur;
    17. }

    5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有 结点组成的。

     (1)直接迭代改变链接关系(使用带头链表)

    1. struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    2. struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
    3. struct ListNode* tail;
    4. if(newhead!=NULL)
    5. {
    6. tail=newhead;
    7. }
    8. struct ListNode* p1=list1,*p2=list2;
    9. tail->next=NULL;
    10. while(p1&&p2)
    11. {
    12. if(p1->val<p2->val)
    13. {
    14. tail->next=p1;
    15. tail=tail->next;
    16. p1=p1->next;
    17. }
    18. else
    19. {
    20. tail->next=p2;
    21. tail=tail->next;
    22. p2=p2->next;
    23. }
    24. }
    25. if(p1)
    26. {
    27. tail->next=p1;
    28. }
    29. if(p2)
    30. {
    31. tail->next=p2;
    32. }
    33. return newhead->next;
    34. }

    (2)递归

    1. struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    2. if(list1==NULL)
    3. {
    4. return list2;
    5. }
    6. if(list2==NULL)
    7. {
    8. return list1;
    9. }
    10. if(list1->val<list2->val)
    11. {
    12. list1->next=mergeTwoLists(list1->next,list2);
    13. return list1;
    14. }
    15. else
    16. {
    17. list2->next=mergeTwoLists(list2->next,list1);
    18. return list2;
    19. }
    20. return NULL;
    21. }

    6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结 点之前 。

    创建两个链表,分别链接小于x的节点和大于x的节点: 

    1. ListNode* partition(ListNode* pHead, int x) {
    2. ListNode* newhead1=(ListNode*)malloc(sizeof(ListNode));
    3. ListNode* newhead2=(ListNode*)malloc(sizeof(ListNode));
    4. newhead1->next=NULL;
    5. newhead2->next=NULL;
    6. ListNode* tail1=newhead1;
    7. ListNode* tail2=newhead2;
    8. ListNode* cur=pHead;
    9. while(cur)
    10. {
    11. if(cur->val < x)
    12. {
    13. tail1->next=cur;
    14. tail1=tail1->next;
    15. }
    16. else
    17. {
    18. tail2->next=cur;
    19. tail2=tail2->next;
    20. }
    21. cur=cur->next;
    22. }
    23. tail1->next=newhead2->next;
    24. tail2->next=NULL;
    25. return newhead1->next;
    26. }

    7. 链表的回文结构。

     快慢指针加逆序链表

    1. struct ListNode* reverseList(struct ListNode* head){
    2. struct ListNode* ret=NULL,*cur=head,*pre=NULL;
    3. while(cur)
    4. {
    5. ret=cur->next;
    6. cur->next=pre;
    7. pre=cur;
    8. cur=ret;
    9. }
    10. return pre;
    11. }
    12. bool chkPalindrome(ListNode* A) {
    13. ListNode* slow=A,*fast=A;
    14. while(fast&&fast->next)
    15. {
    16. fast=fast->next->next;
    17. slow=slow->next;
    18. }
    19. slow=reverseList(slow);
    20. while(slow&&A!=slow)
    21. {
    22. if(slow->val!=A->val)
    23. {
    24. return false;
    25. }
    26. slow=slow->next;
    27. A=A->next;
    28. }
    29. return true;
    30. }

    8. 输入两个链表,找出它们的第一个公共结点。

    (1)先遍历两个链表,得出长度差,使得长链表先走长度差的步数,然后一起比较。 

    1. struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    2. int difsize=0;
    3. struct ListNode *p1=headA,*p2=headB;
    4. struct ListNode*longL=headA,*shortL=headB;
    5. while(p1||p2)
    6. {
    7. if(p1)
    8. {
    9. difsize++;
    10. p1=p1->next;
    11. }
    12. if(p2)
    13. {
    14. difsize--;
    15. p2=p2->next;
    16. }
    17. }
    18. if(difsize<0)
    19. {
    20. longL=headB,shortL=headA;
    21. }
    22. difsize=abs(difsize);
    23. while(difsize--)
    24. {
    25. longL=longL->next;
    26. }
    27. while(longL)
    28. {
    29. if(longL==shortL)
    30. return longL;
    31. else
    32. {
    33. longL=longL->next;
    34. shortL=shortL->next;
    35. }
    36. }
    37. return NULL;
    38. }

    (2)不记录长度一直遍历达到两个指针指向长度一样的目的。

    创建两个指针 初始时分别指向两个链表的头节点 ,然后将两个指针依次遍历两个链表的每个节点。

    如果指针 pA不为空,则将指针 pA 移到下一个节点;pB同理。

    如果指针 pA为空,则将指针 pA 移到链表 headB的头节点;pB同理。

    当指针 pA,pB 指向同一个节点退出。

    1. if(headA==NULL||headB==NULL)
    2. {
    3. return NULL;
    4. }
    5. struct ListNode * pA = headA, *pB = headB;
    6. while (pA != pB) {
    7. if(!pA)
    8. pA =headB;
    9. if(!pB)
    10. pB =headA;
    11. if(pA == pB)
    12. break;
    13. pA=pA->next,pB=pB->next;
    14. }
    15. return pA;
    16. }

    (3)哈希表查找

    1. struct HashTable {
    2. struct ListNode *key;
    3. UT_hash_handle hh;
    4. };
    5. struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    6. struct HashTable *hashTable = NULL;
    7. struct ListNode *temp = headA;
    8. while (temp != NULL) {
    9. struct HashTable *tmp;
    10. HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
    11. if (tmp == NULL) {
    12. tmp = malloc(sizeof(struct HashTable));
    13. tmp->key = temp;
    14. HASH_ADD(hh, hashTable, key, sizeof(struct HashTable *), tmp);
    15. }
    16. temp = temp->next;
    17. }
    18. temp = headB;
    19. while (temp != NULL) {
    20. struct HashTable *tmp;
    21. HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
    22. if (tmp != NULL) {
    23. return temp;
    24. }
    25. temp = temp->next;
    26. }
    27. return NULL;
    28. }

    9. 给定一个链表,判断链表中是否有环。

     (1)快慢指针

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

     (2)哈希表

    1. struct hashTable {
    2. struct ListNode* key;
    3. UT_hash_handle hh;
    4. };
    5. struct hashTable* hashtable;
    6. struct hashTable* find(struct ListNode* ikey) {
    7. struct hashTable* tmp;
    8. HASH_FIND_PTR(hashtable, &ikey, tmp);
    9. return tmp;
    10. }
    11. void insert(struct ListNode* ikey) {
    12. struct hashTable* tmp = malloc(sizeof(struct hashTable));
    13. tmp->key = ikey;
    14. HASH_ADD_PTR(hashtable, key, tmp);
    15. }
    16. bool hasCycle(struct ListNode* head) {
    17. hashtable = NULL;
    18. while (head != NULL) {
    19. if (find(head) != NULL) {
    20. return true;
    21. }
    22. insert(head);
    23. head = head->next;
    24. }
    25. return false;
    26. }

    10. 给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL 

    利用前一题的快慢指针。一指针从相遇点开始走,一指针从头开始,两者一定会在入环点相遇。 

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

     11. 给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点 或空结点。 要求返回这个链表的深度拷贝。

    方法一:遍历两遍,第一遍创建新节点将next链接好,同时记录源节点与新节点对应关系,第二遍完成random的链接。

     

    1. struct Node* copyRandomList(struct Node* head)
    2. {
    3. if(head==NULL)
    4. return NULL;
    5. struct Node* arr1[1001];
    6. struct Node* arr2[1001];
    7. struct Node*cur=head;
    8. struct Node* newguard=(struct Node*)malloc(sizeof(struct Node));
    9. struct Node*ptail=newguard;
    10. assert(newguard);
    11. int index=0;
    12. while(cur)
    13. {
    14. struct Node* ret=(struct Node*)malloc(sizeof(struct Node));
    15. ret->val=cur->val;
    16. ptail->next=ret;
    17. ptail=ret;
    18. arr1[index]=cur;
    19. arr2[index]=ret;
    20. cur=cur->next;
    21. index++;
    22. }
    23. cur=head;
    24. struct Node*newcur=newguard->next;
    25. while(cur)
    26. {
    27. struct Node* obj=cur->random;
    28. if(obj!=NULL)
    29. {
    30. for(int i=0;i<index;i++)
    31. {
    32. if(obj==arr1[i])
    33. {
    34. newcur->random=arr2[i];
    35. break;
    36. }
    37. }
    38. }
    39. else
    40. {
    41. newcur->random=NULL;
    42. }
    43. cur=cur->next,newcur=newcur->next;
    44. }
    45. ptail->next=NULL;
    46. return newguard->next;
    47. }

    方法二:新节点先链接进入原链表然后将新节点的random链接好,此时原来节点random指向的源节点后面就可以直接找到新节点应该指向的位置。

    分为三步:

        //创建新节点

        //改变random指向

        //将新节点剥离

     

    1. struct Node* copyRandomList(struct Node* head)
    2. {
    3. if(head==NULL)
    4. return NULL;
    5. struct Node*cur=head;
    6. //创建新节点
    7. while(cur)
    8. {
    9. struct Node* ret=(struct Node*)malloc(sizeof(struct Node));
    10. ret->val=cur->val;
    11. ret->next=cur->next;
    12. cur->next=ret;
    13. cur=ret->next;
    14. }
    15. cur=head;
    16. //改变random指向
    17. while(cur)
    18. {
    19. struct Node* obj=cur->next;
    20. if(cur->random==NULL)
    21. {
    22. obj->random=NULL;
    23. }
    24. else
    25. {
    26. obj->random=cur->random->next;
    27. }
    28. cur=obj->next;
    29. }
    30. cur=head->next;
    31. //将新节点剥离
    32. while(cur->next)
    33. {
    34. cur->next=cur->next->next;
    35. cur=cur->next;
    36. }
    37. return head->next;
    38. }

    (3)

    (4)

  • 相关阅读:
    Cesium加载地图服务
    职能篇—自动驾驶产品经理
    【R语言】概率密度图
    5.华为交换机局域网vlan网段隔离配置
    工商银行新一代银行移动开发平台建设研究
    Kudu-1.16编译中下载Gradle依赖失败的解决办法
    几种ubuntu下deb打包技术:checkinstall打包成deb,sh打包成可执行文件,以及添加依赖库,到打包到deb
    干货 | 便捷、可靠的数据传输方法
    基于协同过滤算法的旅游推荐系统
    大厂必问的几个面试题(面试含泪总结)
  • 原文地址:https://blog.csdn.net/m0_62918577/article/details/127696447