发布时间:2025-06-24 18:51:57  作者:北方职教升学中心  阅读量:219


💎 欢迎各位大佬互三:我的主页

1. 反转链表

206.反转链表

思考:如果不开辟额外的空间,只在原来的链表上进行修改的话,该用什么方法呢

只需要从第二个元素开始,依次进行头插就可以了

接着修改一下引用就可以了,还要把末尾元素指向null

class Solution {    public ListNode reverseList(ListNode head) {        //head为null,直接返回        if(head == null){            return head;        }        ListNode cur = head.next;        //head变为了末尾元素,指向null        head.next = null;        while(cur!=null){            //curn为cur的next节点,便于往后移动            ListNode curn = cur.next;                cur.next = head;            head = cur;            //更新cur            cur = curn;        }        return head;    }}

2. 链表的中间节点

876. 链表的中间结点

这道题首先想到的方法肯定是定义一个cnt ,遍历一遍链表,接着求出中间的数,再遍历返回值,这种方法很简单,那如果要求只遍历一遍链表就找出中间节点呢

这里提供一个新的思路:利用快慢指针,都从head开始,fast指针每次移动两个节点,slow每次移动一个节点,这样是不是就达到了最终的目的

接着我们来实现一下:

class Solution {    public ListNode middleNode(ListNode head) {        if(head == null){            return null;        }        ListNode fast = head;        ListNode slow = head;        while(fast!= null&&fast.next!= null){            fast = fast.next.next;            slow = slow.next;        }        return slow;    }}

这样是不是效率就更高了

3. 返回倒数第k个节点

面试题 02.02. 返回倒数第 k 个节点

这道题首先想到的应该还是遍历一遍得到总的长度,接着再找倒数第k个节点,不过有了上一题的基础,就可以想到另一种方法:同样的,利用快慢指针,让fast先走 k-1 个节点,然后slow和fast同时走,当fast的next为null时,就表示fast走到了最后一个节点,此时的slow就是倒数第k个节点

这道题中指明了输入的k是合法的,就不用再额外的判断了,那如果k是不合法的怎么判断,并且要求,不能直接求链表的长度,所以slow就需要边走边判断

class Solution {    public int kthToLast(ListNode head, int k) {        if (head == null) {            return -1;        }        ListNode fast = head;        ListNode slow = head;        int cnt = 0;        while (cnt != k - 1) {            //判断k的合法性,虽然本题不用判断            fast = fast.next;            if(fast == null){                return;            }            cnt++;        }        //slow和fast同时移动        while (fast.next != null) {            fast = fast.next;            slow = slow.next;        }        return slow.val;    }}

4. 合并链表

21. 合并两个有序链表

思路:只需要再定义一个新的节点newH,然后一次比较headA和headB的val,为了最后能找到合并之后的链表的头结点,还要定义一个tmp = newH,之后谁小tmp.next就指向谁

class Solution {    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {        ListNode head = new ListNode();        ListNode tmp = head;            while(list1!=null&&list2!=null){            if(list1.val < list2.val){                tmp.next = list1;                list1 = list1.next;                tmp = tmp.next;            }else{                tmp.next = list2;                list2 = list2.next;                tmp = tmp.next;            }        }        //判断剩余的情况        if(list1!=null){            tmp.next = list1;        }else if(list2!=null){            tmp.next = list2;        }        return head.next;    }}

5. 链表分割

CM11 链表分割

这个是牛客上的一道面试题,题意就是对链表进行分割,x左边是比x小的,右边是比x大的,并且不能改变原来的顺序

思路:定义一个新的节点cur遍历原来的链表(防止原来的链表被改变),把比x大的都放到一个链表,比x小的都放在领一个链表,最后把这两个链表相连

public class Partition {    public ListNode partition(ListNode pHead, int x) {        ListNode bs = null;        ListNode be = null;        ListNode as = null;        ListNode ae = null;        ListNode cur = pHead;        while (cur != null) {            if (cur.val < x) {                //处理刚开始为null的情况                if (bs == null) {                    bs = be = cur;                }else{                    be.next = cur;                    be = be.next;                }            }else{                if(as == null){                    as = ae = cur;                }else{                    ae.next = cur;                    ae = ae.next;                }            }            cur = cur.next;        }        //都是比x大的情况        if(bs == null){            return as;        }        be.next = as;        //最后都要置为null,不然可能会报错        if(as!=null){            ae.next = null;        }        return bs;    }}

需要注意的是,如果都比x大,也就是bs = null,此时就直接返回as,还有就是如果最后要把as.next置为null

6. OR36 链表的回文结构

OR36 链表的回文结构

是牛客的一道题,因为其中有时间复杂度和空间复杂度的要求,意味着不能额外开数组,不然空间复杂度过不去,正常遍历就是O(n)的复杂度

思路是这样的:首先找到链表的中间节点slow,然后把从中间到末尾这部分进行反转,head节点往后遍历,slow也往后遍历,进行比较,如果值不相同,就意味着不是回文结构

反转链表和找中间节点前面已经练习过

public class PalindromeList {    public boolean chkPalindrome(ListNode head) {        if (head == null) {            return true;        }        //找到中间节点        ListNode fast = head;        ListNode slow = head;        while (fast != null && fast.next != null) {            fast = fast.next.next;            slow = slow.next;        }        //从slow开始到末尾反转        ListNode cur = slow.next;        while (cur != null) {            ListNode curn = cur.next;            cur.next = slow;            slow = cur;            cur = curn;        }        //判断回文        while (head != slow) {            if (head.val != slow.val) {                return false;            }            //判断偶数节点            if (head.next == slow) {                return true;            }            head = head.next;            slow = slow.next;        }        return true;    }}

需要注意的是,奇数节点的链表和偶数节点的链表的判断也有区别

偶数节点移动到上面的这种情况就会死循环,所以对于偶数节点还要额外判断

7. 相交链表

160. 相交链表

相交链表也就是一个类似于“ Y ”的转化,如图所示

下面的题中的图也很清楚

思路:因为无论是A短还是B短,他们的差值都是相交前的一部分,只需要把长的链表移动一个差值,接着两个链表节点同时往后移动,他们相同的节点就是要相交的那个节点

public class Solution {    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {        ListNode pl = headA;        ListNode ps = headB;        int cnta = 0,cntb = 0;        //计算两个链表的长度        while(pl!=null){            pl = pl.next;            cnta++;        }        while(ps!=null){            ps = ps.next;            cntb++;        }        //如果链表b长,就更新pl,ps        int len = cnta - cntb;        //上面求长度时pl,ps都为空了,所以这里再重新指向        pl = headA;        ps = headB;        if(len < 0){            pl = headB;            ps = headA;            len = cntb - cnta;        }        //移动差值        while(len!=0){            pl = pl.next;            len--;        }        //找到相交节点        while(pl!=ps){            pl = pl.next;            ps = ps.next;        }        //链表不相交的情况        if(pl == null) return null;        return pl;    }}

最后还需要加上不相交的情况,虽然在本题中不加也能过,不过为了严谨性还是要加上

8. 环形链表

8.1 判断是否有环

141. 环形链表

怎么去判断一个链表是否有环:还是使用快慢指针,就像两个人在操场跑步,一个人跑的快,一个人跑的慢,由于操场是一个环形,所以跑的快的肯定可以超过跑的慢的一圈,这时他们就相遇,但如果不是环,他们永远也不能相遇,就像你怎么追都追不到的女神一样

但是还有一种情况,如果是两个节点的环,定义的fast指针一次走3步,slow指针一次走1步,那他们还是永远也相遇不了

所以定义的指针每次走几步也需要判断,如果一个走2步,一个走1步,肯定是能相遇的

public class Solution {    public boolean hasCycle(ListNode head) {        ListNode fast = head;        ListNode slow = head;        while(fast!=null&&fast.next!=null){            fast = fast.next.next;            slow = slow.next;            if(fast == slow) return true;        }        return false;    }}

8.2 返回入环口的节点

142. 环形链表 II

这次需要在上题中判断是否为环后找出进入环的入口点,可以进行以下推理

public class Solution {    public ListNode detectCycle(ListNode head) {        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) return null;        slow = head;        //寻找环        while(slow!=fast){            slow = slow.next;            fast = fast.next;        }        return slow;    }}

需要补充的是:

当环很小的时候,最终的化简结果就变成了上面的,但原来的代码还是没问题的