常见的链表OJ题(简单篇)

本篇博客整理了一些常见的链表oj题(以下所有oj题均出自leetcode平台),用于交流和学习。下面将通过C实现这些oj题。

🌏删除链表指定节点

  给定一个链表的首节点head和一个整数val,删除链表中所有满足node.val == val的节点,并返回新的首节点。示例如下:



基本思路:创建一个哨兵位头节点,遍历原链表所有节点,若节点值不等于val,则将该节点尾插进新链表;若节点值等于val,free该节点后跳向下一个节点。

按照该思路,大致作图如下:

代码实现

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* newHead = malloc(sizeof (struct ListNode));
    struct ListNode* tail = newHead;//维护新链表尾节点 方便尾插
    struct ListNode* cur = head;
    while (cur)
    {
    	//先存一份next避免节点free后无法继续遍历
        struct ListNode* next = cur->next;
        if (cur->val == val)
        {
            free(cur);
        }
        else
        {
            tail->next = cur;
            tail = tail->next;
        }
        cur = next;
    }
    tail->next = NULL;//注意这里要置空
    struct ListNode* next = newHead->next;
    free(newHead);
    return next;
}

注意:若链表为空或链表最后一个节点的val等于指定的val时,新链表的尾节点链向的是一个野指针,因此在最后别忘了将尾节点的next置为NULL。


🌏逆序链表

  给定一个链表的首节点head,逆序该链表,并返回逆序后链表的首节点。示例如下:



基本思路:创建一个空的新链表,遍历原链表,将原链表的节点依次头插进新链表。

由于此题思路并不复杂,所以这里没有选择作图。


代码实现

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* newHead = NULL;
    struct ListNode* cur = head;
    while (cur)
    {
    	//先存一份next避免当前节点头插进新链表后无法继续遍历
        struct ListNode* next = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = next;
    }
    return newHead;
}

🌏链表的中间节点

  给定一个首节点为head的非空单链表,返回链表的中间节点。如果有两个中间节点,则返回第二个中间节点。示例如下:

示例1:

输入:[1,2,3,4,5]
输出:[3,4,5]

示例2:

输入:[1,2,3,4,5,6]
输出:[4,5,6]
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。


思路一:先遍历一遍链表统计节点个数,节点数除以2就得到了中间节点的索引,再从链表首节点开始走索引步后,该节点即为中间节点。

代码实现

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* cur = head;
    int count = 0;
    while (cur)
    {
        count++;
        cur = cur->next;
    }
    count /= 2;//获取中间节点的索引
    cur = head;
    while (count)//从首节点开始走索引步
    {
        cur = cur->next;
        count--;
    }
    return cur;
}

思路二:使用快慢指针,慢指针一次走一步,快指针一次走两步,当快指针为NULL或快指针的next为NULL时,慢指针指向的节点即为中间节点。

按照该思路,大致作图如下:

代码实现

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow, *fast;
    slow = fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

🌏合并两个有序链表

  将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的(即新链表的节点为两个原链表的节点)。示例如下:



基本思路:创建一个哨兵位头节点,先同时遍历两个链表,将两个链表中val较小的节点尾插到新链表后跳向该链表较小节点的下一个节点,继续比较两个链表的节点直到其中一个链表走到NULL。因为每次比较后只是将其中一个链表的节点尾插到新链表,所以两个链表不可能同时走到NULL,此时其中一个链表还留有节点,需要将该链表剩下的所有节点尾插到新链表。

按照该思路,大致作图如下:

代码实现

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if (list1 == NULL && list2 == NULL)//保证至少一个链表不为空
        return NULL;
    struct ListNode* head = malloc(sizeof (struct ListNode));
    struct ListNode* tail = head;
    //两个链表同时遍历直到一个链表走到NULL
    while (list1 && list2)
    {
        if (list1->val < list2->val)
        {
            tail->next = list1;
            tail = tail->next;
            list1 = list1->next;
        }
        else
        {
            tail->next = list2;
            tail = tail->next;
            list2 = list2->next;
        }
    }
    //将其中一个链表剩下的节点尾插到新链表
    if (list1)
        tail->next = list1;
    else
        tail->next = list2;
    struct ListNode* next = head->next;
    free(head);
    return next;
}

  

🌏相交链表

  给定两个单链表的首节点headA和headB,找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回NULL。

示例1:

相交的起始节点为‘8’

示例2:

相交的起始节点为‘2’

示例3:

两个链表不相交,返回NULL

注意:当两个链表在某个节点相交后,不会像下图中左边那样,相交后分散;而是会像下图中右边那样,相交后汇集到两个链表共同的节点。这是因为链表的节点只能“多链一”而不能“一链多”,相交的起始节点不可能再同时向后链向两个节点,所以两个链表在相交后不会分散。由此我们能够得出两个链表相交的条件就是“两个链表的尾节点相同”。


基本思路:先判断两个链表尾节点是否相同来确定链表是否相交,不相交则直接返回NULL。然后分别遍历两个链表统计各自的节点数,两个链表的节点数相减得到差值。再让节点数较多的链表先走差值步,之后两个链表同时向后走,直到两个指针指向的节点地址相同,该节点即为相交的起始节点。

按照该思路,大致作图如下:

代码实现

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL)
        return NULL;
    struct ListNode* curA = headA, *curB = headB;
    int countA = 0, countB = 0;
    //因为最后是用两个链表节点数的差值 所以不计入尾节点也一样
    while (curA->next)
    {
        countA++;
        curA = curA->next;
    }
    while (curB->next)
    {
        countB++;
        curB = curB->next;
    }
    if (curA != curB)//判断两个链表尾节点地址是否相同
        return NULL;
    //到这里两个链表必然相交
    struct ListNode* longList = headA, *shortList = headB;
    if (countA < countB)
    {
        longList = headB;
        shortList = headA;
    }
    int gap = abs(countA - countB);
    //长链表先走差值步
    while (gap)
    {
        longList = longList->next;
        gap--;
    }
    //找到地址相同的节点即为相交的起始节点
    while (longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return longList;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值