链表OJ题讲解1

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

删除链表元素

bd47ab1bbb7a48d3a3305e816d867da9.png299b506da6f4490da79da58945ff9068.png2445683c5bc84483a2689823e7f1fe58.png

方法一

我们可以遍历一遍链表,然后将等于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;
}

反向链表a3cacfe05fbf4b628aac8461ae9deea4.png0ed69c5646324130ac2b87ab8e07615d.png

方法一

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


代码如下:

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;
}

链表的中间结点

7796048d043541c093dd5454e7a37e78.png

11d75aa95c7b4179b82061151458b838.png

这个题使用快慢指针就可以解决。我们让慢指针一次走一步,快指针一次走两步,我们就可以知道当快指针为空时,或者当快指针的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个结点6f84e76279bd4fab9a52b34ff4435c6a.png

这个题同样使用快慢指针,我们可以让快指针先走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;
}

合并两个排序列表


d3b9a4cdebaa44d4890eae110ae567bd.png

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


带哨兵位的

代码如下:

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;
}

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

相关文章
|
1月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
25 7
|
4月前
【数据结构OJ题】环形链表
力扣题目——环形链表
37 3
【数据结构OJ题】环形链表
|
4月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
51 1
【数据结构OJ题】复制带随机指针的链表
|
4月前
【数据结构OJ题】环形链表II
力扣题目——环形链表II
30 1
【数据结构OJ题】环形链表II
|
4月前
【数据结构OJ题】相交链表
力扣题目——相交链表
32 1
【数据结构OJ题】相交链表
|
4月前
【数据结构OJ题】合并两个有序链表
力扣题目——合并两个有序链表
41 8
【数据结构OJ题】合并两个有序链表
|
4月前
【数据结构OJ题】移除链表元素
力扣题目——移除链表元素
43 2
【数据结构OJ题】移除链表元素
|
4月前
【数据结构OJ题】链表中倒数第k个结点
牛客题目——链表中倒数第k个结点
36 1
【数据结构OJ题】链表中倒数第k个结点
|
4月前
【数据结构OJ题】链表分割
牛客题目——链表分割
32 0
【数据结构OJ题】链表分割
|
4月前
【数据结构OJ题】链表的回文结构
牛客题目——链表的回文结构
39 0
【数据结构OJ题】链表的回文结构