0


链表面试题(图文详解)

在这里插入图片描述

🎉🎉🎉写在前面:
博主主页:🌹🌹🌹戳一戳,欢迎大佬指点!
博主秋秋:QQ:1477649017 欢迎志同道合的朋友一起加油喔💪
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个小菜鸟嘿嘿
-----------------------------谢谢你这么帅气美丽还给我点赞!比个心-----------------------------

在这里插入图片描述


面试经典例题


1,删除链表中所有等于定值val的节点

Leetcode传送门,点击这里!!

classSolution{publicListNoderemoveElements(ListNode head,int val){if(head ==null){returnnull;//链表为空}ListNode prev = head;ListNode cur = head.next;while(cur !=null){if(cur.val == val){
                cur = cur.next;
                prev.next = cur;}else{
                prev = cur;
                cur = cur.next;}}//特殊情况,头节点也为keyif(head.val == val){
            head = head.next;}return head;}}

这个题的解答其实和我们的单链表的模拟实现的removeAllKey()是一个意思,所以具体的解析大家看这张图。


在这里插入图片描述


2,反转一个单链表

Leetcode传送门,点击这里!!

//要求原地进行反转//1,只用一个节点指针,进行头插classSolution{publicListNodereverseList(ListNode head){if(head ==null){returnnull;}ListNode cur = head.next;
        head.next =null;while(cur !=null){ListNode curNext = cur.next;
            cur.next = head;
            head = cur;
            cur = curNext;}return head;}}//2,两个节点指针,原地逆序classSolution{publicListNodereverseList(ListNode head){if(head ==null){returnnull;}ListNode prev = head;ListNode cur = head.next;
        head.next =null;while(cur !=null){ListNode curNext = cur.next;
            cur.next = prev;
            prev = cur;
            cur = curNext;}
        head = prev;return head;}}

【图示:第一种方法】

在这里插入图片描述


【图示:第二种方法】

在这里插入图片描述


3,寻找中间节点

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

Leetcode传送门,点击这里!!

可能有的同学会觉得很简单,先遍历一遍求出长度,然后不就顺理成章的知道中间节点了嘛。但是,面试题的要求肯定不会让你如此,假如要求你只能遍历一遍就把问题解决,那你如何做呢?

//这里用到了 快慢指针 的思想classSolution{publicListNodemiddleNode(ListNode head){ListNode fast = head;ListNode slow = head;while(fast !=null&& fast.next !=null){
            fast = fast.next.next;
            slow = slow.next;}return slow;}}

【图示:】

在这里插入图片描述


这里解释一下快慢指针的原理,因为它是找到中间节点,我们的fast与slow指针的起点一样,路程也一样,但是fast的速度是slow的二倍,这样当我们的fast走到终止时,slow的位置就是我们要求的中间节点。


4,链表中倒数第K个节点

牛客传送门,点击这里!!

//要求还是只让你遍历一遍链表importjava.util.*;publicclassSolution{publicListNodeFindKthToTail(ListNode head,int k){if(head ==null){returnnull;}if(k <=0){//判断k的合法性returnnull;}ListNode fast = head;ListNode slow = head;while(k -1!=0){//让fast先走k-1步
            fast = fast.next;if(fast ==null){returnnull;//说明k过大}
            k--;}while(fast.next !=null){//fast.next == null 就说明fast到了最后节点了,就不走了
            fast = fast.next;
            slow = slow.next;}return slow;}}

这个题还是快慢指针的思想。它既然想找到我们的倒数第k个节点,我们就想让fast先走k-1步,这样的话,等到fast到最后一个节点时,slow的位置就是我们的倒数第k个节点处。这里的关键就是我们的倒数第k个节点与我们的最后一个节点之间差的就是k-1步,这也是为什么要将fast先走k-1步的原因。


【图示:】
在这里插入图片描述


while(k -1!=0){//让fast先走k-1步
    fast = fast.next;if(fast ==null){returnnull;//说明k过大}
    k--;}

这里还有一个点就是我们的k肯定是不能大于我们的链表长度的,但是要求是只能遍历一遍链表,所以我们没办法去求链表的长度,但是我们知道,限制k不能过大的原因就是fast走的时候可能直接变成null了,所以我们可以把这个条件转换到这里的先走k-1步中,因为假设我们的链表长度是len,那么极端情况就是求倒数第len个,那么fast最多先走len-1步,所以如果当k> size()的时候,k-1 >= size(),明显就不符合了,fast在移动的过程中就会变成null,这个时候就说明k过大了。


5,合并两个有序链表

具体题目:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

Leetcode传送门,点击这里!!

classSolution{publicListNodemergeTwoLists(ListNode list1,ListNode list2){ListNode newNode =newListNode(-1);//虚拟节点ListNode tmp = newNode;while(list1 !=null&& list2 !=null){if(list1.val < list2.val){
                tmp.next = list1;
                tmp = list1;
                list1 = list1.next;}else{
                tmp.next = list2;
                tmp = list2;
                list2 = list2.next;}}if(list1 ==null){//也包含初始条件下某一个就为null的情况
            tmp.next = list2;}if(list2 ==null){
            tmp.next = list1;}return newNode.next;//然会新的头节点,原两个链表的头节点都动了}}

看到这个题呢,可能很多同学会想起合并两个有序数组,这二者之间确实有着异曲同工之妙,不一样的是这里我不需要额外的空间,因为链表我可以直接改变next域让其串起来,即使是两个链表的元素。同样是指针遍历,list1,list2两个节点指针去遍历元素,tmp记录当前所在元素的位置。


【图示:】

在这里插入图片描述


6,分割链表

具体题目:编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。

牛客传送门,点击这里!!

importjava.util.*;publicclassPartition{publicListNodepartition(ListNode pHead,int x){ListNode as =null;ListNode ae =null;ListNode bs =null;ListNode be =null;ListNode cur = pHead;while(cur !=null){if(cur.val < x){if(as ==null){//可能是放入第一个数据
                    as = cur;
                    ae = cur;}else{
                    ae.next = cur;
                    ae = ae.next;}}else{if(bs ==null){
                    bs = cur;
                    be = cur;}else{
                    be.next = cur;
                    be = be.next;}}
            cur = cur.next;}//然后把两个段链接起来if(as ==null){//有可能没有第一段return bs;//bs也有可能是null}
        ae.next = bs;//把两段连起来,如果第二段不存在,刚好用来把第一段结尾置为nullif(bs !=null){//如果存在第二段,那么第二段的尾巴需要手动置null一下
            be.next =null;}return as;}}

【图示:】

在这里插入图片描述


7,链表的回文结构

牛客传送门,点击这里!!

importjava.util.*;publicclassPalindromeList{publicbooleanchkPalindrome(ListNode head){if(head ==null){returntrue;//空链表算回文}ListNode fast = head;ListNode slow = head;//先找到中间节点while(fast !=null&& fast.next !=null){
            fast = fast.next.next;
            slow = slow.next;}//从中间后进行链表的翻转ListNode cur = slow.next;while(cur !=null){ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;}//翻转完毕,slow现在在最后一个节点处while(head != slow){if(head.val != slow.val){returnfalse;}if(head.next == slow){returntrue;}
            head = head.next;
            slow = slow.next;}returntrue;}}

回文结构,按照面试的要求,肯定是不能用除链表之外的空间的,所以你只能原地判断。那想法肯定是前后遍历然后对比值,但是链表如何能够从后往前走呢?此时反转是不是就可以达到目的了,所以我们的思路就是
1,找到中间节点 2,从中间节点往后开始反转链表 3,判断回文。


【图示:】

在这里插入图片描述


8,相交链表的公共节点

Leetcode传送门,点击这里!!

publicclassSolution{publicListNodegetIntersectionNode(ListNode headA,ListNode headB){if(headA ==null|| headB ==null){returnnull;}ListNode fast = headA;//fast指向的是长的那个链表,现在假设是headA长ListNode slow = headB;int subLen =0;int lenA =0;int lenB =0;while(fast !=null){
            lenA++;//从headA计算长度
            fast = fast.next;}while(slow !=null){
            lenB++;//从headB计算长度
            slow = slow.next;}

        subLen = lenA - lenB;//因为动了fast与slow,现在重新让他们指向开头,同时更新一下fast与slow的指向if(subLen <0){
            fast = headB;
            slow = headA;
            subLen = lenB - lenA;}else{
            fast = headA;
            slow = headB;}while(subLen !=0){//让长的那一方的指针fast先走差值步
            fast = fast.next;
            subLen--;}while(fast != slow){
            fast = fast.next;
            slow = slow.next;}return fast;}}

首先我们得弄清楚一个点,两个链表相交,是怎么相交? X 型 或者是 Y型?答案肯定是 Y型,因为我们的链表一个节点的后继肯定只有一个,所以不肯能是X型。


【图示:】

在这里插入图片描述


9,判断链表中是否有环

Leetcode传送门,点击这里!!

publicclassSolution{publicbooleanhasCycle(ListNode head){ListNode fast = head;ListNode slow = head;while(fast !=null&& fast.next !=null){//奇数个节点或者偶数个节点没环的终止条件
            fast = fast.next.next;
            slow = slow.next;if(fast == slow){returntrue;}}returnfalse;}}

可以看到,原理还是很简单,快慢指针的追及问题,因为如果有环存在,那么在快指针与慢指针的移动过程中,一定会相遇。但是,这里有一个关键问题就是,快指针与慢指针之间的速度差是多少?怎么确定?

答案是:快指针一次走两步,慢指针一次走一步。原因如下:

在这里插入图片描述

快指针肯定是先进环,慢指针后进环,那么这个时候,最好的情况就是刚好slow过来就直接和fast相遇了;最差的情况就是二者之间刚好差一个环的长度。fast指针每次走两步,slow指针每次走一步,那么每移动一次,二者之间的距离就会少一步(相对于slow,fast的每次移动都多一步,所以移动一次,中间距离就会缩小一步),这个时候其实就是一个fast去追slow的问题,并且不会出现套圈的问题。所以,在slow把这圈走完之间,fast绝对可以把slow追上。


可能有的同学会问快指针可不可以一次走3步,或者4步,或者更多?其实我们一次只走两步,这个决定的考量就是为了防止套圈的问题出现,因为快慢指针之间的速度差越大,在某种程度上,可能会更加快的追上,但同时套圈的危险性就更大了,比如:

在这里插入图片描述

注意,我们是两个都走完了才开始比,移动过程中的相遇是不算的,综上,就是我们选择走两步与走一步的原因。


10,返回入环节点

具体题目:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL。

Leetcode传送门,点击这里!!

publicclassSolution{publicListNodedetectCycle(ListNode head){if(head ==null){returnnull;}ListNode fast = head;ListNode slow = head;while(fast !=null&& fast.next !=null){//奇数个节点或者偶数个节点没环的终止条件
            fast = fast.next.next;
            slow = slow.next;if(fast == slow){break;}}if(fast ==null|| fast.next ==null){//说明没有环returnnull;}//上面没有return掉,就说明有环,并且fast与slow相遇了
        fast = head;while(fast != slow){
            fast = fast.next;
            slow = slow.next;}return fast;}}

【图示:】

在这里插入图片描述


最后,今天的文章分享比较简单,就到这了,如果大家觉得还不错的话,还请帮忙点点赞咯,十分感谢!🥰🥰🥰
在这里插入图片描述

标签: 链表 数据结构

本文转载自: https://blog.csdn.net/qq_61688804/article/details/125612459
版权归原作者 影子,你陪着我累吗? 所有, 如有侵权,请联系我们删除。

“链表面试题(图文详解)”的评论:

还没有评论