链表经典面试题(反转链表,中间节点,倒数第k个节点,合并分割链表,删除重复节点

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

if(k<=0) {//输入的节点位置不合法,k是从1开始的
    return null;
}
    while(cur!=null) {//求链表的长度

count++;
cur=cur.next;
}

    while((count-k)!=0) {//找倒数第k个节点

head=head.next;
count–;

    }
  return head;  

}

}


这种思路的时间复杂度为o(n),要遍历链表求链表的长度,至于说要遍历链表两次,没有双指针好,我觉得差不多,双指针也要遍历两次链表。  
 方法二:双指针法,该方法只用一次循环不用求链表的长度。  
 基本思路:



> 
> 1.初始化: 定义快指针fast,慢指针slow(这里是引用变量),双指针都指向头节点 head​ 。  
>  2.构建双指针距离:快指针fast先向前走 k步(结束后,快指针fast,慢指针slow 间相距 k 步)。  
>  3.双指针共同移动:在 循环中,快指针fast,慢指针slow 每轮都向前走一步,直至 fast 走到链表的 尾节点 时跳出(跳出后, slow与尾节点距离为 k-1步,这时fast 指向的就是倒数第 k 个节点)。  
>  4.返回值: 返回 slow 即可。 时间复杂度为O(n),空间复杂度为O(1),快慢指针法不用求链表的长度。
> 
> 
> 


图示分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/8968343f9152452cb2d81717d15a98ab.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 代码实现:



class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast=head;
ListNode slow=head;
if(headnull) {
return head;
}
if(k<=0)
return null;
while(k-1!=0) {
if(fast.next
null)
return fast;
else {
fast=fast.next;
k–;
}

    }
    while(fast.next!=null) {
        fast=fast.next;
        slow=slow.next;
    }

return slow;
}

}

动图分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1389c822b2a448d394ed8579ac368d8d.gif#pic_center)


## 🎓将两个有序链表合并为一个新的有序链表并返回。


题目描述:  
 理解题目:注意,两个升序链表合并为一个新的链表后任然是升序的,不是简单的拼接,可以存在相同的值。  
 [题目链接](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9ed23d783c684e9ebf110961a9c9fa53.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 基本思路:  
 方法一:迭代  
 📝我们可以用迭代的方法来实现上述算法。当 链表A 和 链表B 都不是空链表时,同时从链表A 和 链表B的头节点出发,两两比较,哪一个链表的头节点的值更小,将较小值的节点添加到新的链表里面,当一个节点被添加到新的链表之后,将对应链表中的节点向后移一位。如果比较后链表中的结点值没有被添加到新的链表,则节点值位置不变和另一个链表中下一个节点比较,在比较值的时候小于和等于的情况可以归为一类,小于的节点先接在新的链表后面,两个链表的节点值相等的时候先接哪个节点都一样,如此循环下去直到其中一个链表遍历完(循环结束的条件为其中一个链表为空,两个链表不可能同时为空)。因为是两两比较和升序链表,所以两个链表中当一个先遍历完,把没有遍历完的链表剩余的节点接在新链表的节点之后。之后返回新链表的头节点即可。不咋清楚的再看看图。时间复杂度:O(n + m),空间复杂度:O(1)O(1)  
 图示分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/ae651491f8744dd4bab1972f936261be.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)


代码实现:



class Solution {
public ListNode mergeTwoLists(ListNode headA, ListNode headB) {
if( headAnull) return headB;
if( headB
null) return headA;
if( headBnull&&headAnull) return null;
ListNode NewHead=new ListNode(-1);
ListNode tem=NewHead;
while (headA!=null&&headB!=null) {
if(headA.val<headB.val) {
tem.next=headA;
headA=headA.next;
}
else {
tem.next=headB;
headB=headB.next;
}
// else {

//tem.next=headB;
//headB=headB.next;
//headA=headA.next;
// }
tem=tem.next;
}
//最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
if(headBnull) tem.next=headA;
if(headA
null) tem.next=headB;
return NewHead.next;
}
}


动图分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d810a40b77544cbebd7721cb2d714c07.gif#pic_center)  
 方法二:递归法:  
 递归法通常将一个大型复杂的问题层层转化为一个与原问题相似的较小的问题来求解,递归只需要少量几步就可以解决复杂的问题,求解递归如果找到公式很容易的决解问题,如果没有公式则难以入手,对于这道题的求解公式大概如下:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/59fb9db832f34adeb40ca74a3088c7a8.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 基本思路:  
 如果 l1 或者 l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。如果两个链表有一个为空,递归结束。时间复杂度:O(n + m),空间复杂度为 O(n+m)。  
 代码实现:



class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}

    if(l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
}

}


## ✏️以给定值x为基准将链表分割成两部分


题目描述:  
 题目也好理解以给定值x,将链表分成两部分,但要注意在分割的时候不需要排序,保持原有的顺序,就比如示例的4,3分割后为4,3不要写成3,4。  
 [题目链接](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/88939f7302c448e093ab74e3518a6f5e.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 基本思路:  
 只需要遍历链表的所有节点,小于x的放到一个小的链表中,大于等于x的放到一个大的链表中,最后再把这两个链表串起来即可。  
 图示分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/80556c61ac8442d2b331f762f9536c1a.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)


![在这里插入图片描述](https://img-blog.csdnimg.cn/5d5243d009c543798b85ff2c78b1e09d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 代码实现,带哨兵节点(省掉一些特殊条件的判断,使代码更加简洁,如本题中不需要判断第一次添加节点时头结点的情况)



public ListNode partition(ListNode head, int x) {
//小链表的头
ListNode smallHead = new ListNode(0);
//大链表的头
ListNode bigHead = new ListNode(0);
//小链表的尾
ListNode smallTail = smallHead;
//大链表的尾
ListNode bigTail = bigHead;
//遍历head链表
while (head != null) {
if (head.val < x) {
//如果当前节点的值小于x,则把当前节点挂到小链表的后面
smallTail = smallTail.next = head;
} else {//否则挂到大链表的后面
bigTail = bigTail.next = head;
}

        //继续循环下一个结点
        head = head.next;
    }
    //最后再把大小链表拼接在一块即可。
    smallTail.next = bigHead.next;
    bigTail.next = null;
    return smallHead.next;
}

## ✏️在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点i


题目描述:  
 [题目链接](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9ed52517105f4d55901efdf099810a2d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 基本思路:  
 首先一个比较直观且通用的思路是,采用边遍历边构造的方式:


1. 定义和构造新的节点,通过类的实例化产生一个虚拟节点newHead,以减少边界判断,用于之后接收不重复的节点,构造新的链表并返回新链表,定义一个临时节点cur指向需要删除的链表进行遍历链表节点,tem节点开始指向虚拟机节点之后代替虚拟节点移动接收不重复的节点(虚拟节点不能移动)
2. 从头节点一直遍历元素直到把链表元素遍历完,遍历时如果从某一位置起有节点和后面的一个或多个节点相等,就一步一步的往后走跳过重复的元素,如果不是重复的元素就一个一个按顺序接在虚拟节点的后面,形成新的链表,之后返回虚拟节点的下一个位置位置即可以得到不重复的链表了。  
 一些详细要拿捏的细节我们一起来看图示分析吧!


![在这里插入图片描述](https://img-blog.csdnimg.cn/a8ef438c318f440b9e376fe7090b3ebd.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 动图分析:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/68ede380a94a4cd19bf411672fd8f090.gif#pic_center)代码实现:



public ListNode deleteDuplicates(ListNode head) {
if(headnull||head.nextnull) return head;
ListNode dummy=new ListNode(-1);
// ListNode pre=dummy;
ListNode cur=head;
ListNode pre=dummy;

while(cur!=null){//一个一个节点的看有没有重复
if(cur.next!=null&&cur.valcur.next.val){
//跳过重复的节点
while(cur.next!=null&&cur.val
cur.next.val){
cur=cur.next;
}
cur=cur.next;
}else{
pre.next=cur;
pre=pre.next;
cur=cur.next;
}
}
pre.next=null;//手动设置防止最后一个节点是重复的
return dummy.next;

}


## ✏️删除链表中重复的元素ii


题目描述:  
 [题目链接](https://bbs.csdn.net/topics/618668825)  
 下面的这道题和上面的有一些相似,所以我也把它总结在一起了,下面的这道题要求重复的节点出现一次,比上面的题简单一些,从头的位置和后面的节点比较,后面和前面相同就删除,删完了就移动。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1e665cac6f7745a48b4d3c24a24f4ffa.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Nzc5MjkzMw==,size_16,color_FFFFFF,t_70#pic_center)  
 基本思路:


1. 指定 cur 指针指向头部 head
2. 当 cur 和 cur.next的存在为循环结束条件,当二者有一个不存在时说明链表没有去重复的必要了 ,也就是说最少要有两个节点。
3. 当 cur.val 和 cur.next.val相等时说明需要去重,则将 cur 的下一个指针指向下一个的下一个,这样就能达到去重复的效果
4. 如果不相等则 cur移动到下一个位置继续循环 时间复杂度:O(n)


代码实现:



![img](https://img-blog.csdnimg.cn/img_convert/ce5ad09390a8968db35f45e62b9f01cb.png)
![img](https://img-blog.csdnimg.cn/img_convert/d60d25d5c96e9242d02286c962a49189.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

X-1715891269188)]
[外链图片转存中...(img-S8NLoso2-1715891269189)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值