【数据结构】链表OJ - 精选精讲


前言

本文选取了一些经典的来自力扣和牛客的链表OJ题,并提供优质,值得借鉴的思路供读者积累与掌握。均配有步骤图解


一、注意事项

我们在学习数据结构时刷题很重要,刷题的时候画图很重要,学会作图才能理解其中的核心思想,代码的实现反而是其次的,要画图,然后看图写代码。本文在讲解时也遵循先解析思路,配图,再进行代码实现的先后顺序。

二、OJ精讲

  1.移除链表元素

  • 题目描述
    在这里插入图片描述
  • 思路讲解

整体:从头开始遍历链表,把不是val值的节点取下来尾插进新链表。
拆分:

  1. 准备工作,初始化指针
  2. 分是val和不是val两种情况,其中是val时要格外讨论是否为第一次插入新表。

在这里插入图片描述
在这里插入图片描述

  • 代码实现
struct ListNode* removeElements(struct ListNode* head, int val)
 {
    struct ListNode* tail = NULL;
    struct ListNode* newhead = NULL;
    struct ListNode* cur = head;


    while (cur)
    {
        if (cur->val != val)
        {
            if (newhead == NULL)
            {
                newhead = cur;
                tail = newhead;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else
        {
            struct ListNode* tmp = cur;
            cur = cur->next;
            free(tmp);
        }
    }
     if(tail)
        tail->next = NULL;
    return newhead;
}


  2. 反转单链表

  • 题目描述
    在这里插入图片描述
  • 思路讲解

从头开始依次反转节点指向,并更新头结点

在这里插入图片描述

  • 代码实现
struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
        return NULL;
      struct ListNode* cur = head;
      struct ListNode* next = cur->next;
      struct ListNode* newhead = NULL;

      while(cur)
      {
          cur->next = newhead;
          newhead = cur;
          cur = next;
          if(next)
            next = next->next;
      }
      return newhead;
}


  3. 链表的中间节点

  • 题目描述
    在这里插入图片描述
  • 思路讲解

快慢指针法
让fast指针一次走两步,slow一次走一步,当fast走到尾节点或NULL时,slow位置即为中间节点。对于本题,节点个数为奇数和偶数在代码实现上是一样的,不必分开讨论。

在这里插入图片描述
在这里插入图片描述

  • 代码实现
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    
    return slow;
}


  4. 链表中倒数第K个节点

  • 题目描述
    在这里插入图片描述
  • 思路讲解

快慢指针法
fast 先走K步,再同时走,当fast为空时,slow位置为倒数第K个节点。
类似于上题,读者可以尝试自行使用快慢指针法画图求解

  • 代码实现
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast = pListHead;
    struct ListNode* slow = pListHead;

    while(k--)
    {
        if(fast == NULL)
            return NULL;
        fast = fast->next;
        
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

  5.合并两个有序链表

  • 题目描述
    在这里插入图片描述
  • 思路讲解

分别从头遍历给定的两个链表,进行节点值的比较,将较小的先尾插到一个新链表。

  • 代码实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {

	  if(list1 == NULL)
	    return list2;
	  if(list2 == NULL)
	    return list1;
	  if(list1==NULL && list2==NULL)
	    return NULL;

     struct ListNode* cur1 = list1;
     struct ListNode* cur2 = list2;
     struct ListNode* newhead = NULL;
     struct ListNode* tail;

     while(cur1 && cur2)
     {
       if(cur1->val <= cur2->val)
       {
            if(newhead == NULL)
                tail = newhead = cur1;
            else
            {
              tail->next = cur1;
              tail = tail->next;
            }
            cur1 = cur1->next;
       }
       else
       {
            if(newhead == NULL)
                tail = newhead = cur2;
            else
            {
              tail->next = cur2;
              tail = tail->next;
            }
         cur2 = cur2->next;
       }
     }

     if(cur1)
     {
       tail->next = cur1;
     }
     if(cur2)
     {
         tail->next = cur2;
     }
     
     return newhead;
}

  6.链表的回文结构

  • 题目描述
    在这里插入图片描述
  • 思路讲解

1.找到中间节点
2.反转链表后半部,使原链表拆分为两个链表
3.两个新链表从头开始依次比较节点的val值,均相同则为回文结构,否则不为回文结构。

在这里插入图片描述

  • 代码实现
class PalindromeList {
    struct ListNode* reverseList(struct ListNode* head) {
        if (head == NULL)
            return NULL;
        struct ListNode* cur = head;
        struct ListNode* next = cur->next;
        struct ListNode* newhead = NULL;

        while (cur) {
            cur->next = newhead;
            newhead = cur;
            cur = next;
            if (next)
                next = next->next;
        }
        return newhead;
    }

    struct ListNode* middleNode(struct ListNode* head) {
        struct ListNode* fast = head;
        struct ListNode* slow = head;

        while (fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
        }

        return slow;
    }
  public:
    bool chkPalindrome(ListNode* head) {

        ListNode* mid = middleNode(head);
        ListNode* newhead = reverseList(mid);
        while (head && newhead) 
        {
            if(head->val != newhead->val)
                return false;

            head = head->next;
            newhead = newhead->next;
        }
        return true;
    }
};


  7. 相交链表

  • 题目描述
    在这里插入图片描述
  • 思路讲解

1.判断是否相交
  分别找尾,尾节点相同则相交。
2.找交点
  快慢指针法。计算出两表长度,长表先从头走差距步,再同时走,直到节点地址相同时停下,即为交点。

  • 代码实现
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //1.判断是否相交,顺便计算A,B长度
    struct ListNode * tailA = headA;
    struct ListNode * tailB = headB;
    int lenA = 1;
    int lenB = 1;

    while(tailA->next)
    {
        tailA = tailA->next;
        lenA++;
    }
    while(tailB->next)
    {
        tailB = tailB->next;
        lenB++;
    }
    if(tailA != tailB)
        return NULL;
    
    //2.找交点
    //假设法精简代码
    struct ListNode * longlist = headA;
    struct ListNode * shortlist = headB;
    //lenA与lenB差值的绝对值
    int n = abs(lenA-lenB);
    if(lenA < lenB)
    {
        longlist = headB;
        shortlist = headA;
    }
    while(n--)
    {
        longlist = longlist->next;
    }
    while(longlist != shortlist)
    {
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    return longlist;

}


总结

本文精选了链表经典OJ题,有些思路对初学者来说可能是第一次见,我们要学会站在巨人的肩膀上,将它们消化吸收,在往后的实践中运用能达到很好的效果。如果对你有所帮助,还望点赞收藏支持博主。
文章中有什么不对的丶可改正的丶可优化的地方,欢迎各位来评论区指点交流,博主看到后会一一回复。

  • 19
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值