【顺序表】【链表】高频必刷笔试考研OJ题

1、删除链表中等于给定值 val 的所有节点OJ链接

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode*perv=NULL;
    struct ListNode*cur=head;
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur==head)
            {
                head=cur->next;
                free(cur);
                cur=head;
            }
            else
            {
                perv->next=cur->next;
                free(cur);
                cur=perv->next;
            }
        }
        else
        {
            perv=cur;
            cur=cur->next;
        }
    }
    return head;
}

2、反转一个单链表OJ链接

//方法一
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*n1,*n2,*n3;
    n1=NULL;
    n2=head;
    if(head==NULL)
    {
        return NULL;
    }
    n3=head->next;
    while(n2)
    {
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3)
        {
            n3=n3->next;
        }
        
    }
    return n1;
}

在这里插入图片描述
这是方法一的核心思想,运用3个指针,迭代往后走。

//方法二
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*cur=head;
    struct ListNode*newhead=NULL;
    while(cur)
    {
        struct ListNode*next = cur->next;
        cur->next=newhead;
        newhead=cur;
        cur=next;
    }
    return newhead;
}

方法二是运用头插的方法。


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

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

在这里插入图片描述

运用快慢指针,慢指针走一步,快指针走两步,那么快指针走完的时候,慢指针正好走了一半。


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

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    if(pListHead==NULL)
        return NULL;
    struct ListNode*slow,*fast;
    slow=fast=pListHead;
    for(int i=0;i<k;i++)
    {
        if(fast==NULL)
            return NULL;
        fast=fast->next;
    }
    while(fast)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;
}

这道题也是使用了快慢指针,让快指针先走k步,然后再让slow和fast一起走,直到fast为NULL,此时slow就是倒数第k的位置。


5、将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的OJ链接

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    if(l1==NULL)
    return l2;
    if(l2==NULL)
    return l1;
    struct ListNode*head=NULL;
    struct ListNode*tail=NULL;
    if(l1->val<l2->val)
    {
        head=tail=l1;
        l1=l1->next;
    }
    else
    {
        head=tail=l2;
        l2=l2->next;
    }
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            tail->next=l1;
            tail=l1;
            l1=l1->next;
        }
        else
        {
            tail->next=l2;
            tail=l2;
            l2=l2->next;
        }
    }
    if(l1==NULL)
    {
        tail->next=l2;
    }
    else
    {
        tail->next=l1;
    }
    return head;
}

6、编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前OJ链接

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode*lesshead,*lesstail,*greaterhead,*greatertail;
        lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lesstail->next=NULL;
        greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
        greatertail->next=NULL;
        struct ListNode*cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                lesstail->next=cur;
                lesstail=lesstail->next;
            }
            else
            {
                greatertail->next=cur;
                greatertail=greatertail->next;
            }
            cur=cur->next;
        }
        greatertail->next=NULL;//注意一下防止死循环比如2,4,6,7,3  k=5
        lesstail->next=greaterhead->next;
        struct ListNode*head=lesshead->next;
        free(lesshead);
        free(greaterhead);
        return head;
    }
};

7、链表的回文结构OJ链接

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

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

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        struct ListNode*mid=middleNode(A);
        struct ListNode*rhead=reverseList(mid);
        struct ListNode*curA=A;
        struct ListNode*curR=rhead;
        while(curA&&curR)
        {
            if(curA->val!=curR->val)
            {
                return false;
            }
            else
            {
                curA=curA->next;
                curR=curR->next;
            }
        }
        return true;
    }
};

这个需要上面的取中间结点,和链表的反转,之后比较就行了


8、输入两个链表,找出它们的第一个公共结点OJ链接

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    assert(headA&&headB);
    struct ListNode*tailA=headA;
    struct ListNode*tailB=headB;
    int numA=1;
    while(tailA->next)
    {
        numA++;
        tailA=tailA->next;
    }
    int numB=1;
    while(tailB->next)
    {
        numB++;
        tailB=tailB->next;
    }
    if(tailA!=tailB)
    return NULL;

    else
    {
        int gap=abs(numA-numB);
        struct ListNode*longList=headA;
        struct ListNode*shortList=headB;
        if(numA<numB)
        {
            longList=headB;
            shortList=headA;
        }
        while(gap--)
        {
            longList=longList->next;
        }
        while(longList!=shortList)
        {
            longList=longList->next;
            shortList=shortList->next;
        }
        return longList;
    }
}

这个题分为两步:
1、判断是否相交,如果不相交何谈交点是哪,那怎么看是否相交,只需看最后一个结点的地址是否相同即可。
2、找到交点。
在这里插入图片描述如果长度相同就直接一起走,走到地址相同的地方就是交点了。
如果长度不相同,就让长的先走两链表长度之差的步数(目的就是让它俩同步),然后再一起走。


9、给定一个链表,判断链表中是否有环OJ链接

bool hasCycle(struct ListNode *head) 
{
   struct ListNode*slow=head,*fast=head;
   while(fast&&fast->next)
   {
       slow=slow->next;
       fast=fast->next->next;
       if(fast==slow)
       {
           return true;
       }
   }
   return false;
}

运用快慢指针
快指针走两步,慢指针走一步,快指针先进环,之后慢指针进环,如果快指针追上了慢指针说明存在环。

环的定义:

如果链表中有某个结点,可以通过连续跟踪next指针再次到达,则链表中存在环

如图都可以算环不管你环大环小
在这里插入图片描述
1、slow走一步,fast走两步,带环,一定会相遇
分析:
第一步:fast先进环,当fast刚进环的瞬间,这时slow走了环前的一般路程
第二步:随着slow进环,fast已经在环里走了一段了,走了多少由环的大小决定
假设slow进环时,slow与fast距离为N,fast开始追slow,slow每往前走一步,fast往前走两步,每追一次,fast和slow的距离会减少1,变成N-1,N-2,N-3…1,0最终会减到0相遇。

2、slow走一步,fast走三步,带环,不一定会相遇
分析:
同样还是fast先进环,当slow进环的时候,fast已经走了一段了
假设两者距离为N,fast开始追,每追一次两者距离减少2,所以分两种情况讨论
当N是偶数,那么两者距离变化为N-2,N-4,N-6…2,0当减到0相遇,这种情况可以追上;当N为奇数,两者距离变化为N-2,N-4,N-6…1,-1这一次没有追上,这时他们之间的距离变为C-1(C为环长),如果C-1是偶数就可以追上,如果是奇数就追不上。
3、slow走一步,fast走四步,带环,不一定会相遇
分析:
和第二种一样,每追一次,两者距离减少3,分类讨论
当N为3的倍数时,两者距离变化为N-3,N-6…3,0最后追上;
当N不是3的倍数,两者距离变化为N-3,N-6…1,-2或者N-3,N-6…2,-1这两种就要看C-2,C-1是不是3的倍数,是的话就能追上,否则不能。


10、
带环链表,slow走一步,fast走两步,我们已经知道肯定可以相遇,那如何求环的入口结点呢?

先直接说结论

一个指针从相遇点开始走,一个指针从链表头开始走,每次都是走一步,他们会在环的入口点相遇。

证明:
在这里插入图片描述
追上相遇的过程中:
慢指针走的距离为:L+X
快指针走的距离为:L+X+N*C (N是相遇之前快指针走的圈数,N>=1)

已知快指针走的路程是慢指针的2倍

2(L+X)=L+X+N×C
L+X=N×C
L=N×C-X
L=(N-1)C+C-X

所以可以证明结论是正确的

做一个题:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULLOJ链接

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode*slow=head,*fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)
        {
            struct ListNode*meetnode=slow;
            while(meetnode!=head)
            {
                head=head->next;
                meetnode=meetnode->next;
            }
            return meetnode;
        }
    }
    return NULL;
}

11、复制带随机指针的链表,给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。要求返回这个链表的深度拷贝。OJ链接

struct Node* copyRandomList(struct Node* head) {
    struct Node*cur=head;
    while(cur)
    {
        struct Node*copy=(struct Node*)malloc(sizeof(struct Node));
        copy->val=cur->val;
        copy->next=cur->next;
        cur->next=copy;
        cur=copy->next;
    }
    cur=head;
    while(cur)
    {
        struct Node*copy=cur->next;
        if(cur->random==NULL)
        copy->random=NULL;
        else
        copy->random=cur->random->next;
        cur=copy->next;
    }
    struct Node*copyhead=NULL,*copytail=NULL;
    cur=head;
    while(cur)
    {
        struct Node*copy=cur->next;
        struct Node*next=copy->next;
        if(copytail==NULL)
        {
            copyhead=copytail=copy;
        }
        else
        {
            copytail->next=copy;
            copytail=copy;
        }
        cur->next=next;
        cur=next;
    }
    return copyhead;
}

本题解题思路是将拷贝结点插入到原结点后面,根据原结点,处理copy结点的random,最后拆解下来copy链表,恢复原链表。
在这里插入图片描述


感谢阅读,我们下期再见
如有错 欢迎提出一起交流
关注周周汪

关注三连么么么哒

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

周周汪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值