单链表OJ题解析——C语言

一、指定全删

单链表OJ题解析——C语言_第1张图片

思路:

用一个指针去遍历整个链表,将不是指定数字的节点拿下来链接,找到指定数字的将其删除释放,思路看似简单,但要实现有几个细节非常重要

1.由于遍历链表时要可以会边走边删,因此需要两个指针并肩走,走在前面的用于记录位置,后面的用于操作链表(链接或者删除)

2.由于这里是尾插操作,我们使用带哨兵位的头节点能避免很多问题

3.在链接过后,要注意删掉尾巴(链接位置的next的地址置空,避免下一个位置成为野指针)

4.考虑空链表的情况

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur=head;
    struct ListNode* sen=(struct ListNode*)malloc(sizeof(struct ListNode));//哨兵位
    struct ListNode* ptail=sen;
    struct ListNode* newhead=NULL;
    sen->next=NULL;//若为空链表,则刚好返回NULL
    while(cur)
    {
        struct ListNode*next=cur->next;//记录下一位
        if(cur->val != val)//不需要删除链接上
        {
            ptail->next=cur;
            ptail=ptail->next;
            ptail->next=NULL;//去尾巴
        }
        else//要删除的直接删
        {
            free(cur);
        }
        cur=next;//找到原链表的下一位
    }
    newhead=sen->next;
    free(sen);
    return newhead;
}

二、反转链表

单链表OJ题解析——C语言_第2张图片

 思路一:反转指针方向

将每一个指针的方向进行反转,在返回新的头地址即可,这个操作要三个指针并肩走,同时还有考虑到空链表的问题。

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur=head;
    struct ListNode* cur_prev=NULL;
    if(head == NULL)
        return NULL;
    struct ListNode* cur_next=cur->next;
    while(cur->next)
    {
        cur->next=cur_prev;
        //三指针并肩走
        cur_prev=cur;
        cur=cur_next;
        cur_next=cur->next;
    }
    cur->next=cur_prev;
    return cur;
}

思路二:遍历头插

定义一个新的头指针指向空指针,将原链表逐一头插,每次头插后更新头指针,同样考虑下空链表的情况。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* newhead=NULL;
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* pnext=cur->next;
        cur->next=newhead;
        newhead=cur;
        cur=pnext;
    }
    return newhead;
}

三、找链表的中间节点

单链表OJ题解析——C语言_第3张图片

快慢指针法

用两个指针同时走,快指针走两步,慢指针走一步,分类讨论后可知,当快指针指向空或者快指针下一位为空时,慢指针刚好就是中间位置。

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

四、倒数节点 

单链表OJ题解析——C语言_第4张图片

快慢指针法

先让快指针走k步,然后走快慢指针同时走,结合画图写代码,考虑边界情况

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
{
    struct ListNode* fast=pListHead;
    struct ListNode* slow=pListHead;
    if(pListHead == NULL || k==0)
        return NULL;
    while(k--)
    {
        fast=fast->next;
        if(fast==NULL && k!=0)
            return NULL;
    }
    while(fast)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

五、合并两个有序链表

单链表OJ题解析——C语言_第5张图片

 

遍历取值法

由一对指针分别去遍历这两个链表,开始同时指向头,然后进行比较,谁比较小取谁尾插,由于尾插操作,最好用带哨兵位的链表去尾插,能避免很多麻烦,最后,哪一个先取完,就将另一个整个链接上去再返回头地址即可,如果不用哨兵位的链表,则要额外考虑两个链表中有空链表的情况,还要考虑第一次尾插得先对头地址赋值。

这里展示的代码是不用哨兵位的:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode* newhead=NULL;
    struct ListNode* newtail=NULL;
    struct ListNode* cur1=list1;
    struct ListNode* cur2=list2;
    if(cur1==NULL)
        return cur2;
    if(cur2==NULL)
        return cur1;
    while(1)
    {
        if(cur1->val >= cur2->val)
        {
            if(newhead==NULL)//第一次赋值
            {
                newhead=newtail=cur2;
                cur2=cur2->next;
            }
            else
            {
                newtail->next=cur2;//链接
                newtail=newtail->next;//往前走一步
                cur2=cur2->next;
            }
            if(cur2==NULL)//如果cur2先走到空,将剩下的cur1链接上
            {
                newtail->next=cur1;
                return newhead;
            }

        }
        else
        {
            if(newhead==NULL)
            {
                newhead=newtail=cur1;
                cur1=cur1->next;
            }
            else
            {
                newtail->next=cur1;
                newtail=newtail->next;
                cur1=cur1->next;
            }
            
            if(cur1==NULL)
            {
                newtail->next=cur2;
                return newhead;
            }
        }
    }
}

六、链表分割

单链表OJ题解析——C语言_第6张图片

测试用例:

输入:[1 8 5 6 4 3 7 ]   x=4;           输出:[1 3 8 5 6 4 7]

输入:[ ]   x=3;                输出:[]

思路

定义两个链表分别用于放大于等于x的节点,和小于x的节点,用一个指针去遍历原链表

取完后主要要去掉尾巴

最后将两个链表链接上

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) 
    {
        struct ListNode* sentry1=(struct ListNode*)malloc(sizeof(struct ListNode));//哨兵
        struct ListNode* sentry2=(struct ListNode*)malloc(sizeof(struct ListNode));//哨兵
        struct ListNode* tail1=sentry1;
        struct ListNode* tail2=sentry2;
        struct ListNode* cur=pHead;
        struct ListNode* newhead=NULL;
        while(cur)//分组
        {
            if(cur->val>=x)
            {
                tail1->next=cur;
                tail1=tail1->next;
                cur=cur->next;
            }
            else
            {
                tail2->next=cur;
                tail2=tail2->next;
                cur=cur->next;
            }
        }
        //断尾巴
        tail1->next=NULL;
        tail2->next=NULL;
        //链接
        tail2->next=sentry1->next;
        newhead=sentry2->next;
        free(sentry1);
        free(sentry2);
        return newhead;
    }
};

七、回文链表的判断

单链表OJ题解析——C语言_第7张图片

 思路:

先找到中间位置

在将后半段指针进行反转,中间位置指向NULL

最后再一起往中间靠进行比较

单链表OJ题解析——C语言_第8张图片

 同样的。如果是偶数个也是画图分析。偶数位画完图会发现,如果循环条件设为head等于空是不行的,因此只能用newhead。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) 
    {
        struct ListNode* mid=A;
        struct ListNode* head=A;
        struct ListNode* newhead=NULL;
        struct ListNode* cur=A;
        struct ListNode* next=NULL;
        while(cur&&cur->next)//先让mid指向中间位置
        {
            cur=cur->next->next;
            mid=mid->next;
        }
        //翻转后半段,翻转后newhead指向原链表的末尾
        while(mid)
        {
            next=mid->next;
            mid->next=newhead;
            newhead=mid;
            mid=next;
        }
        while(newhead)
        {
            if(head->val!=newhead->val)
            {
                return false;
            }
            head=head->next;
            newhead=newhead->next;
        }
        return true;

    }
};

总结

本章是刷完题以后,大致的对思路简单的进行总结,方便以后复习,主要的是掌握上面的思路,然后具体去结合图像实现。

你可能感兴趣的:(c语言,链表,开发语言,数据结构)