链表经典面试oj题


前言

如果可以自己做出了这些链表oj题,那么说明链表基础掌握的就还行,当然,一开始不会的话也没什么,我相信学完这些题会进一步加深对链表的理解和一些技巧;


一、移除链表元素

在这里插入图片描述

把节点的值和val相等的节点释放掉,再组成新的链表;
这个时候为了方便头指针的释放,我们链表带头,因为不用考虑头是否要释放;然后再用两个指针,一前一后走,如果值不相等,就迭代走下去,代码是:tail=cur,cur=cur->next;,但是开始的时要,phead->next=head,tail=head;
下面代码我就多了些操作,但是思路都作业,要是值相等,那么先保存该节点的下一个节点,然后free该节点,把前面一个节点和下一个节点连接起来;
next=cur->next,free(cur),tail->next=cur=next;
最后值得注意的是:如果最后一个或者几个都是相等的节点,那么全部free完后,最后一个不相等的节点的next还存着free掉的节点地址,所有出循环后要把最后一个节点的next赋空指针;然后就是把头free了,返回头的下一个节点;

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


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

二 . 反转链表

在这里插入图片描述

我们用三个指针,后面两个指针用于反转两个节点,最前面的指针用于保存下一个节点,便于迭代;
这样就走起来了,prev指针在最左边,也就是后面,cur是当前,next是最前面的指针,走的时候是cur->next=prev;prev=cur;cur=next;而next则是一直向后的,所有就这样走起来了;不清楚的可以画一下图,更直观;

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

三 . 链表的中间节点

在这里插入图片描述

这里有奇数链表和偶数链表,奇数返回的奇数中间的节点,偶数就是第二个中间节点嘛;然后这里介绍一个方法叫做快慢指针,也许听过,这个方法用来解决这题非常简单,快指针走两步,慢指针走一步,当链表为奇数时,快指针走到fast->next==NULL的时候,慢指针就是停在了中间节点;当链表为偶数时,fast指针刚好走到空的时候,慢指针就是第二个中间节点了;

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

四. 链表中倒数第k个结点

在这里插入图片描述

先看正常情况,思路是让一个指针先走k步,然后再和头部指针一起走,当前面指针走到空时,后面的指针指向的就是倒数第K个节点了;
当k=0的时候,这个思路没问题;
当k=链表长度的时候,前面的指针直接走到空了,后面的指针还在第一个节点,没问题;
当k>链表长度的时候,这个时候就会有问题了,前面的指针会走到空了还在走,这是不对的,所以我们要让前面走k步的循环里面指针进来的里面不可以是空指针,因为空指针不可能在向后走了,直接返回空指针;

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    // write code here
    struct ListNode* fast=pListHead,*slow=pListHead;
    while(k--){
        if(fast==NULL)
            return NULL;
        fast=fast->next;
    }
    while(fast){
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

五. 合并两个有序链表

在这里插入图片描述

还是为了方便,带个头节点,有的题带头节点是会方便很多的,我们考虑问题的时候要有加头节点的考虑哦;把两个链表中小的值的节点尾插到我们自己的链表后面,就这样一直尾插,到一个链表都尾插完后跳出循环,然后把没有插完的链表直接连接就行了;
但是有可能两个或者其中一个链表为空,我的话单独处理;

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if(list1==NULL&&list2==NULL)
return NULL;
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
struct ListNode* phead=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail=phead,*head1=list1,*head2=list2;
while(head1&&head2){
    if(head1->val<head2->val){
        tail->next=head1;
        tail=tail->next;
        head1=head1->next;
    }
    else{
        tail->next=head2;
        tail=tail->next;
        head2=head2->next;
    }
}
if(head1)
tail->next=head1;
if(head2)
tail->next=head2;
struct ListNode* head=phead->next;
free(phead);
phead=NULL;
return head;
}

六. 链表分割

在这里插入图片描述

我们可以把小于x的节点组成一个链表,然后大于x的节点组成一个链表,然后把大于的链表连接到小于的链表后面就行了;不够值得注意的是,我们有要带头,把两个链表都带头,不过那么可以不带头试试,的确会更麻烦;
还有要注意的是,链表都大于等于或者小于等于x的情况,这个时候我们的方法可以解决,不过就是把头的下一个节点置空一下就行了;还有就是大的链表的尾节点不一定是原链表的尾节点,它的next不一定指向空,所以我们需要自己把那个节点置空;

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        //分成两个链表,一个所有值小于x的链表,另一个大于或者等于x的值的链表
        struct ListNode* pheadsmall=(struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* pheadgreater=(struct ListNode*)malloc(sizeof(struct ListNode));
        pheadsmall->next=NULL;//带头方便头节点问题
        pheadgreater->next=NULL;
        struct ListNode* tail=pHead,*smalltail=pheadsmall,*greatertail=pheadgreater;
        while(tail){
            if(tail->val<x){
                smalltail->next=tail;
                smalltail=smalltail->next;
                tail=tail->next;
            }
            else{
                greatertail->next=tail;
                greatertail=greatertail->next;
                tail=tail->next;
            }
        }
        smalltail->next=pheadgreater->next;
        greatertail->next=NULL;//大的链表尾节点的next不一定是空指针;
        struct ListNode* head=pheadsmall->next;
        free(pheadsmall);
        free(pheadgreater);
        pheadgreater=NULL;
        pheadsmall=NULL;
        return head;
    }
};

七. 链表的回文结构

在这里插入图片描述

两个思路,第一个就是把原链表复制一个,把复制链表反转一下,再于原链表比较,要是一样,就没错,要是有一个节点不对,那么就不是回文结构;
第二个方法就是把把链表分成两条,前面的节点一条,中间节点后面的一条链表(包括中间节点),把后面的一条链表反转,再和前面链表对比,直到有链表到空,就是回文链表,要是有一个对不上,就不是;
还有要注意的是,第一个思路比较多人想到的,第二个思路不好想;我就写一下第二个方法的代码;

class PalindromeList {
public:
    struct ListNode* Getmid(struct ListNode* phead){
        struct ListNode* fast=phead,*slow=phead;
        while(fast&&fast->next){
            fast=fast->next->next;
            slow=slow->next;
        }
        return slow;
    }
    struct ListNode* Turnslist(struct ListNode* phead){
        struct ListNode* n1=NULL,*n2=phead,*n3=phead;
        while(n2){
            n3=n3->next;
            n2->next=n1;
            n1=n2;
            n2=n3;
        }
        return n1;
    }
    bool chkPalindrome(ListNode* A) {
        // write code here
        struct ListNode* phead1=Getmid(A);
        struct ListNode* phead2=Turnslist(phead1);
        while(A&&phead2){
            if(A->val!=phead2->val)
                return false;
            A=A->next;
            phead2=phead2->next;
        }
        return true;
    }
};

八. 相交链表

在这里插入图片描述

思路:要是相交,总有一个节点的地址相同,只要判断最后一个节点是否相同,那么就可以判断链表是否相交;要是相交,怎么判断第一个交点呢?
我们可以得出两条链表长度,然后长链表先走差值步数,然后两个链表指针一起走,直到指针相等时就是第一个节点指针了;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headB==NULL)
    return NULL;
    if(headA==NULL)
    return NULL;
    struct ListNode* tail1=headA,*tail2=headB;
    int l1=1,l2=1;
    while(tail1->next){
        ++l1;
        tail1=tail1->next;
    }
    while(tail2->next){
        ++l2;
        tail2=tail2->next;
    }
    if(tail1!=tail2)//两个链表最后一个节点不相等,那么肯定不相交
    return NULL;
    struct ListNode* longslist=headA,*shortslist=headB;
    if(l1<l2)
    {
        longslist=headB;
        shortslist=headA;
    }
    //让长的链表先走差的步数
    int n=abs(l1-l2);
    while(n--){
        longslist=longslist->next;
    }
    while(longslist!=shortslist){
        longslist=longslist->next;
        shortslist=shortslist->next;
    }
    return longslist;

}

九. 环形链表

在这里插入图片描述

判断是否带环,直接快慢指针解决,要是快慢指针相遇,那么肯定有环,要是循环可以结束,那么就不带环;

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

带环问题还有延伸的问题:快指针走两步,慢指针走一步,是否一定可以追上?要是快指针走三步,慢指针走一步,结果当如何,会相遇不?快指针走四步,慢指针 一步呢,结果又是如何?当快指针走n步,慢指针走m步,结果又如何?

在这里插入图片描述


在这里插入图片描述

当fast走三步,slow走一步的情况:

在这里插入图片描述

在这里插入图片描述

其它情况和上面类似,都可以解释;

十. 环形链表 ||

在这里插入图片描述

两种方法;第一种:公式法,快慢指针的交点和起点一起开始走,都走一步,当相遇时,就是环的人口点;会证明;
第二种方法:快慢指针相遇节点的下一个节点为第二条链表的起节点,原链表起点就是第一条链表起节点,把快慢指针相遇节点的next置空,然后两条链表的第一个相交节点不就是环的人口节点吗;然后看情况要不要恢复链表,不能通过就恢复链表;

在这里插入图片描述

//第一种方法:
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast=head,*slow=head;
    while(fast&&fast->next){
        fast=fast->next->next;
        slow=slow->next;
        if(slow==fast){//如果有环,可以根据公式得出人口
        //快慢指针相遇节点开始和头节点一起走,相遇点就是答案
            struct ListNode* cur1=head,*cur2=slow;
            while(cur1!=cur2){
                cur1=cur1->next;
                cur2=cur2->next;
            }
            return cur1;        
        }
    }
    return NULL;//如果出来了循环,说明不带环
}
//第二种方法:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headB==NULL)
    return NULL;
    if(headA==NULL)
    return NULL;
    struct ListNode* tail1=headA,*tail2=headB;
    int l1=1,l2=1;
    while(tail1->next){
        ++l1;
        tail1=tail1->next;
    }
    while(tail2->next){
        ++l2;
        tail2=tail2->next;
    }
    if(tail1!=tail2)//两个链表最后一个节点不相等,那么肯定不相交
    return NULL;
    struct ListNode* longslist=headA,*shortslist=headB;
    if(l1<l2)
    {
        longslist=headB;
        shortslist=headA;
    }
    //让长的链表先走差的步数
    int n=abs(l1-l2);
    while(n--){
        longslist=longslist->next;
    }
    while(longslist!=shortslist){
        longslist=longslist->next;
        shortslist=shortslist->next;
    }
    return longslist;

}
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast=head,*slow=head;
    while(fast&&fast->next){
        fast=fast->next->next;
        slow=slow->next;
        if(slow==fast){//如果有环,可以将快慢指针相等的节点的next设置为空指针,然后本来的下一个节点变成第二个链表的头节点,这两个链表的交节点
//即是答案
            struct ListNode* cur1=head,*cur2=fast->next;
            fast->next=NULL;
            struct ListNode* dst=getIntersectionNode(cur1,cur2);
            return dst;
        }
    }
    return NULL;//如果出来了循环,说明不带环
}


总结

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340