链表OJ题讲解1

博主个人主页:不是笨小孩
⏩专栏分类:数据结构与算法
代码仓库:笨小孩的代码库
⏩社区:不是笨小孩
欢迎大家三连关注,一起学习,一起进步!!

链表OJ

  • 删除链表元素
    • 方法一
    • 方法二
  • 反向链表
    • 方法一
    • 方法二
  • 链表的中间结点
  • 链表中倒数第k个结点
  • 合并两个排序列表

在这里插入图片描述

删除链表元素

链表OJ题讲解1_第1张图片
链表OJ题讲解1_第2张图片
链表OJ题讲解1_第3张图片

方法一

我们可以遍历一遍链表,然后将等于val的值的节点删除了,然后在将剩余的链接起来即可。我们就需要两个指针,一个遍历链表另一个记录上一个节点的位置,如果相等就删除链接,等到链表遍历结束,就可以得到新的链表,但是这种放大有一个弊端,就是如果头结点就是val我们无法处理,所以我们需要现将头结点是val的情况处理了,头结点是val,我们只需要头删即可。

代码如下:

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* prev = NULL,*cur = head;
    while(cur)
    {
        if(cur->val==val)
        {
            //相等就要删除
            if(cur==head)
            {
                //如果头结点是就相等,就要头删
                head = cur->next;
                free(cur);
                cur = head;
            }
            else
            {
                //正常删除
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else
        {
            // 不相等prev记录上一个节点,cur往下走
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
}

方法二

我们可以依旧是遍历原链表,但是我们这一次需要一个新的头,然后与val不相等的我们尾插到新的头中就可以了,这个方法比较省事,直接尾插就可以,但是需要注意新的头为空的情况,为了方便尾插,我们需要一个尾指针来记录尾节点的位置。

代码如下:

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur = head,*tail=NULL,*newhead=NULL;
    while(cur)
    {
        if(cur->val==val)
        {
            //相等就删除
            struct ListNode* next = cur->next;
            free(cur);
            cur = next;
        }
        else
        {
            //不相等就尾插
            if(newhead==NULL)
            {
                //第一次尾插
                newhead = tail = cur;
                cur= cur->next;
            }
            else
            {
                //正常尾插
                tail->next= cur;
                cur = cur->next;
                tail = tail->next;
            }
        }
    }
    //尾差的最有后一个节点的next要置NULL,不然可能会非法访问
    if(tail!=NULL)
    //tail等于空说明链表为空,或者链表中的元素都val
        tail->next = NULL;
    return newhead;
}

反向链表

链表OJ题讲解1_第4张图片
链表OJ题讲解1_第5张图片

方法一

我们定义一个空指针,然后翻转链表的指向,但是直接翻转的话我们会丢失下一个节点,所以我们还需要一个指针来记录下一个节点的位置。

代码如下:

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newhead = NULL,*cur = head,*next = NULL;
    while(cur)
    {
        //next指向下一个节点
        next = cur->next;
        //翻转指向
        cur->next = newhead;
        //更新新的头结点
        newhead = cur;
        //更新cur
        cur = next;        
    }
    return newhead;
}

方法二

头插法,我们遍历原链表,然后把节点都头插到新的头处,即可翻转链表。

代码如下:

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newhead = NULL,*cur = head;
    while(cur)
    {
        //保存下一个节点
        struct ListNode* next = cur->next;
        //头插
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;
}

链表的中间结点

链表OJ题讲解1_第6张图片
链表OJ题讲解1_第7张图片

这个题使用快慢指针就可以解决。我们让慢指针一次走一步,快指针一次走两步,我们就可以知道当快指针为空时,或者当快指针的next为空时此时的慢指针就是中间节点。

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow = head,*fast = head;
    while(fast&&fast->next)
    {
    	//慢指针走一步
        slow=slow->next;
        //快指针走两步
        fast = fast->next->next;
    }
    return slow;
}

链表中倒数第k个结点

链表OJ题讲解1_第8张图片

这个题同样使用快慢指针,我们可以让快指针先走k步,然后两个指针一起走,当快指针为空时,慢指针就是倒数第k个节点,如果先让快指针走k-1步的话,当快指针的next为空时,慢指针就是倒数第k个节点。这两种方法都可以。我们实现走k步的

代码如下:

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast = pListHead,*slow = pListHead;
    while(k--)
    {
        // fast=NULL前K个就为空了(判断k是不是过大,以及链表是不是空链表)
        if(fast==NULL) return NULL;
        fast = fast->next;
    }

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

合并两个排序列表

链表OJ题讲解1_第9张图片

这个题我们可以同时遍历两个链表,然后拿小的下来尾插,思想差不多就是这样,我们需要一个头,但是这个头可以是哨兵位,也可以是空,我们这里写两个版本的。

带哨兵位的
代码如下:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
	//如果一个链表为空直接返回另一个链表
    if(list1==NULL) return list2;
    if(list2==NULL) return list1;
    //创建哨兵位
    struct ListNode* newhead = NULL,*tail = NULL;
    newhead = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
    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==NULL) tail->next = list2;
    if(list2==NULL) tail->next = list1;
    //保返回的头
    struct ListNode* next = newhead->next;
    //释放哨兵位
    free(newhead);
    return next;
}

不带哨兵位
代码如下:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    //如果一个链表为空直接返回另一个链表
    if(list1==NULL) return list2;
    if(list2==NULL) return list1;
    struct ListNode* newhead = NULL,*tail = NULL;
    while(list1&&list2)
    {
        if(list1->val<list2->val)
        {
            //判断头结点是否为空
            if(newhead==NULL)
            {
                newhead = tail = list1;
                list1 = list1->next;
            }
            else
            {
                tail->next = list1;
                tail = tail->next;
                list1= list1->next;
            }
        }
        else
        {
            //判断头结点是否为空
            if(newhead==NULL)
            {
                newhead = tail = list2;
                list2 = list2->next;
            }
            else
            {
                tail->next = list2;
                tail = tail->next;
                list2= list2->next;
            }
            
        }
    }
    //如果一个链表为空,直接将另一个链表链接过来
    if(list1==NULL) tail->next = list2;
    if(list2==NULL) tail->next = list1;
    return newhead;
}

今天的分享就到这里了,感谢大家的支持和关注。

你可能感兴趣的:(刷题专栏,数据结构与算法,链表,数据结构,c语言)