链表OJ笔试面试必刷题

1.删除链表中等于给定值 val 的所有结点

        leetcode 链接:203. 移除链表元素 - 力扣(LeetCode)

                分析:我们定义两个指针一个cur指向当前要查找的结点,另一个prev指向cur前面一个结点,因为cur如果被删除了则没办法找到前一个结点;删除分为两种情况,val在头结点上此时需要改变链表的头结点;如果不在头结点上则用cur向后访问即可;

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode*cur=NULL;
    struct ListNode*prev=NULL;
    cur=prev=head;
    while(cur)
    {
        if(cur->val==val)//找到val了
        {
            //删除;
            //val在头结点上
            if(cur==head)
            {
                head=cur->next;
                free(cur);
                cur=head;
            }
            //val不在头结点上
            else
            {
                prev->next=cur->next;
                free(cur);
                cur=prev->next;
            }
        }
        else
        {
            prev=cur;
            cur=cur->next;
        }
    }
    return head;
}

2.反转一个单链表 

        leetcode链接:206. 反转链表 - 力扣(LeetCode)

                分析:此题我们可以定义三个的指针,分别指向三个连续的结点,每次反转一次,然后三个指针都向后走,直到整个链表反转完; 

struct ListNode* reverseList(struct ListNode* head)
{
    //n1刚开始指向head前面的结点,因此为NULL;
    struct ListNode*n1=NULL;
    //n2指向head;
    struct ListNode*n2=head;
    //如果链表为空,则直接返回;
    if(head==NULL)
    {
        return head;
    }
    //链表不为空;
    //n3指向head后一个结点;
    struct ListNode*n3=head->next;//head不为空,才能访问head后面的结点;

    while(n3)//可以自己画图理解,结束条件是n3==NULL
    {
        //进行反转,每次反转n1与n2之间的指向;
        n2->next=n1;
        
        //更换n1  n2  n3 的位置进行下次操作;
        n1=n2;
        n2=n3;
        n3=n3->next;
    }
    //最后一次反转在循环结束后;
    n2->next=n1;
    return n2;
}

3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

                leetcode链接:876. 链表的中间结点 - 力扣(LeetCode)

                分析:此题我们采用一种比较巧妙的方法,定义快慢两个指针,快指针每次走两个结点,慢指针每次走一个结点。快指针走完时慢指针的位置就是中间结点;

struct ListNode* middleNode(struct ListNode* head)
{
    //定义快慢两个指针;
    struct ListNode*fast=NULL;
    struct ListNode*slow=NULL;
    fast=slow=head;
    //fast每次走两步,若结点树是奇,则走完时fast的下个结点为空;
    //若结点数是偶,则走完时,fast为空;
    while(fast &&fast->next)
    {
        //fast每次走两步;
        fast=fast->next->next;
        //slow每次走一次;
        slow=slow->next;

    }

    return slow;
}

4. 输入一个链表,输出该链表中倒数第k个结点

                牛客网链接:链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com) 

        分析: 此题与上题类似,定义快慢两个指针,快指针先走k步,然后快慢指针都开始各走一步,直到快指针走完时,慢指针的位置就是倒数第k个结点的位置。

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k )
 {
    struct ListNode*fast=pListHead;
    struct ListNode*slow=pListHead;
    //快指针先k步到正数第k+1个结点的位置处;
    while(k)
    {
        //中间如果遇到空,则说明倒数第k个结点不存在,直接返回;
        if(fast==NULL)
        {
            return NULL;
        }
        fast=fast->next;
        k--;
    }
    
    //快慢指针都只走一步
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

5.合并两个有序链表

        leetcode链接:21. 合并两个有序链表 - 力扣(LeetCode) 

                分析:此题定义一个新的链表,然后分别比较两个链表的值,将更小的结点放到新链表上,此时会有新链表为不为空的问题,两种情况放法不一样;然后一个链表走完后,将还没走完的链表直接放到新的链表上。 

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    //新链表的头和尾;
    struct ListNode*head=NULL;
    struct ListNode*tail=NULL;

    //第一个链表为空;直接返回第二个链表;
    if(list1==NULL)
    {
        return list2;
    }
    //第二个链表为空;直接返回第一个链表;
    if(list2==NULL)
    {
        return list1;
    }


    //两个链表都不为空;
    while(list1&&list2)
    {
        //第一个链表的值小于第二个链表的值;
        if(list1->val <list2->val)
        {
            //如果此时新链表为空;
            if(head==NULL)
            {
                head=tail=list1;
            }
            //如果此时新链表不为空
            else
            {
                tail->next=list1;
                tail=list1;

            }
            list1=list1->next;
        }
        //第一个链表的值不小于第二个链表的值
        else
        {
            //如果此时新链表为空;
            if(head==NULL)
            {
                head=tail=list2;

            }
             //如果此时新链表不为空
            else
            {
                tail->next=list2;
                tail=list2;

            }
             list2=list2->next;
        }

        //第一个链表走完,第二个链表还没有走完;
        if(list1==NULL)
        {
            tail->next=list2;
        }
        //第二个链表走完,第一个链表还没有走完;
        if(list2==NULL)
        {
            tail->next=list1;
        }

    }

6.链表分割

                链接:链表分割_牛客题霸_牛客网 (nowcoder.com) 

                分析: 定义两个新链表,且为这两个新的链表加上哨兵头结点,目的是加上头结点可以避免在链表上插入结点时会有新链表为不为空的问题,两种情况放法不一样;然后将小于x的结点放到a链上,不小于x的结点放到b链表上。然后将两个链表链接起来;

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x)
     {
        //定义两个新链表,且为这两个新的链表加上哨兵头结点;
        ListNode* heada=(ListNode*)malloc(sizeof(ListNode));
        ListNode* headb=(ListNode*)malloc(sizeof(ListNode));

        ListNode* taila=heada;
        ListNode* tailb=headb;

        //用cur来访问需要处理的链表
        ListNode*cur=pHead;

        while(cur)
        {
            //将小于x的结点放到a链表上;
            if(cur->val<x)
            {
                taila->next=cur;
                taila=cur;
            }
            //将不小于x的结点放到b链表上;
            else 
            {
                tailb->next=cur;
                tailb=cur;
            }
            cur=cur->next;
        }

        //将a,b两个链表的尾结点的next都置为空,
        //因为尾结点是从处理链表上拿下来的它们的next可能指向其他的结点;
        taila->next=NULL;
        tailb->next=NULL;

        //将两个链表连起来;
        taila->next=headb->next;

        return heada->next;

    }
};

7.环形链表

                链接:141. 环形链表 - 力扣(LeetCode) 

                分析: 定义快慢两个指针,快指针每次走两步,慢指针每次走一步;链表可以走完,说明链表里没有环;快慢指针相遇,说明有环;

bool hasCycle(struct ListNode *head)
 {
    //如果链表为空:
     if(head==NULL)
     {
         return false;
     }
     //定义快慢两个指针;
     struct ListNode *fast=head;
     struct ListNode *slow=head;

    //链表可以走完,说明链表里没有环;
     while(fast &&fast->next)
     {
         //快指针每次走两步,慢指针每次走一步;
         fast=fast->next->next;
         slow=slow->next;

         //快慢指针相遇,说明有环;
         if(fast ==slow)
         {
             return true;
         }
     }

     return false;

}

8.环形链表二

                链接:142. 环形链表 II - 力扣(LeetCode) 

                 分析: 定义快慢两个指针,快指针每次走两步;慢指针每次走一步;快慢指针相遇,记录下相遇结点的下个结点;从相遇点断开环;形成两条链;将两条链分为一条长链和一条短链;将长链先走差距步,使得两条链长度一样,避免短链走完还没找到相遇点的情况;找到两条链的重合点,即相遇到点;

/计算一条没有环的链表的长度;
int longth(struct ListNode *head)
{
    int k=0;
    if(head==NULL)
    {
        return 0;
    }
    while(head)
    {
        k++;
        head=head->next;
    }
    return k;
}



struct ListNode *detectCycle(struct ListNode *head) 
{
    //定义快慢两个指针:
    struct ListNode *fast=head;
    struct ListNode *slow=head;

    //快慢指针相遇时的位置:
    struct ListNode *meet=NULL;

    while(fast && fast->next)
    {   
        //快指针每次走两步;慢指针每次走一步;
        fast=fast->next->next;
        slow=slow->next;

        //快慢指针相遇
        if(fast==slow)
        {
            //记录下相遇结点的下个结点;
            meet=slow->next;
            //从相遇点断开环;形成两条链;
            slow->next=NULL;
        }
    }

    
    int sz1=longth(head);//第一条链的结点数;
    int sz2=longth(meet);//第二条链的结点数;
    //abs求两个数的绝对值;
    int dis=abs(sz1-sz2);//计算出它们差值的绝对值;

    //将两条链分为一条长链和一条短链;
    struct ListNode *longlist=head;
    struct ListNode *shortlist=meet;
    if(sz1<sz2)
    {
        longlist=meet;
        shortlist=head;
    }

    //将长链先走差距步,使得两条链长度一样,避免短链走完还没找到相遇点的情况;
    while(dis--)
    {
        longlist=longlist->next;
    }

    //找到两条链的重合点,即相遇到点;
    while(longlist !=shortlist)
    {
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return longlist;

}

 9.链表的回文结构

                链接:链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

                分析: 此题可以分解为三步,首先找到链表的中间结点,再反转中间结点之后的结点,再判断两条链是否相同,如果相同则是回文结构,否则不是回文结构;

//找链表的中间结点;
ListNode* findmid(ListNode*head)
{
    ListNode*fast=head;
    ListNode*slow=head;
    while(fast &&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
    }
    return slow;
}

//反转链表;
ListNode* reverse(ListNode*mid)
{
    if(mid=NULL)
    {
        return NULL;
    }
    ListNode*n1=NULL;
    ListNode*n2=mid;
    ListNode*n3=mid->next;
    while(n3)
    {
        n2->next=n1;
        n1=n2;
        n2=n3;
        n3=n3->next;
    }
    n2->next=n1;
    return n2;
}

    bool chkPalindrome(ListNode* A) 
    {
        ListNode*head=A;

        //先找中间;
        ListNode*mid=findmid(head);  

        //颠倒中间之后的结点;
        ListNode*rmid=reverse(mid);


        while(head && rmid)
        {
            if(head->val !=rmid->val)
            {
                return false;
            }
            head=head->next;
            rmid=rmid->next;

        }
        return true;
    }

10.相交链表

                链接:160. 相交链表 - 力扣(LeetCode) 

                分析:先分别计算A,B两条链表的长度;如果两条链不相交,则两条链表的尾结点指针不一样;将两条链分为长链和短链,长链先走差距步;使得两条链的长度一样,防止短链走完时,长链还没有走完;同时走长链和短链,直到有结点相同时,则就是两链的交点;

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
     {
         ListNode *cura=headA;
         ListNode *curb=headB;

         int lena=0;
         int lenb=0;

         //分别计算A,B两条链表的长度;
         while(cura->next)
         {
             cura=cura->next;
             lena++;
         }
        while(curb->next)
         {
             curb=curb->next;
             lenb++;
         }

         //不相交;两条链表的尾结点指针不一样;
        if(cura !=curb)
         {
             return NULL;
         }
         

         int gap=abs(lena-lenb);
         //将两条链分为长链表和短链;
         ListNode* longlist=headA;
         ListNode* shortlist=headB;
         if(lena < lenb)
         {
             longlist=headB;
             shortlist=headA;
         }


         //长链先走差距步;
         //使得两条链的长度一样,防止短链走完时,长链还没有走完;
         while(gap)
         {
 
             longlist=longlist->next;
             gap--;
         }

        //同时走长链和短链,直到有结点相同时,则就是两链的交点;
         while(longlist !=shortlist)
         {
             longlist=longlist->next;
             shortlist=shortlist->next;
         }
         return longlist;
    }
};

11.随机链表的复制

                链接:138. 随机链表的复制 - 力扣(LeetCode) 

                分析: 此题分为三个步骤,首先创建出新的结点并分别将其插到原结点之后,为复制的节点赋值随机指针,分离原始链表和复制链表。

struct Node* copyRandomList(struct Node* head) 
{
    if (head == NULL)
        return NULL;

    struct Node* cur = head;
    struct Node* headNext = NULL;
    struct Node* copy = NULL;

    // 创建新节点并插入到原始节点之后
    while (cur)
    {
        headNext = cur->next;

        copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        copy->next = headNext;
        cur->next = copy;
        
        cur = headNext;
    }

    // 为复制的节点赋值随机指针
    cur = head;
    while (cur)
    {
        if (cur->random != NULL)
            cur->next->random = cur->random->next;
        else
            cur->next->random = NULL;

        cur = cur->next->next;
    }

    // 分离原始链表和复制链表
    struct Node* newhead = NULL;
    struct Node* tail = newhead;
    cur=head;
    while(cur)
    {
        if(newhead==NULL)
        {
            newhead=tail=cur->next;
            cur=cur->next->next;
        }
        else
        {
            tail->next=cur->next;
            tail=tail->next;

            cur=cur->next->next;
        }
    }

    tail->next=NULL;
    
    return newhead;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

(int*) nightfall

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值