【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)

目录

  • 1.删除单链表中的元素
    • 1.1 删除排序链表中的重复元素
      • **解法1:迭代**
      • **解法2:递归**
    • 1.2 删除排序链表中的重复元素Ⅱ
      • **解法1:迭代**
      • **解法2:递归**
    • 1.3 移除链表元素
      • **解法1:迭代**
      • **解法2:递归**
  • 2.反转链表
    • 2.1 反转链表
      • **解法1:头插法**
      • **解法2:原地反转**
      • **解法3:递归**
    • 2.2 反转链表Ⅱ
      • **解法:部分反转**
  • 3.查找链表中结点
    • 3.1 链表的中间结点
      • **解法1:按长度查找**
      • **解法2:快慢指针法**
    • 3.2 链表中倒数第k个节点
      • **解法1:按长度查找**
      • **解法2:快慢指针法**
  • 4.回文链表
      • **解法:一分为二**
  • 5.相交链表
      • **解法:全遍历**
  • 6.合并链表
      • **解法1:虚拟头节点法**
      • **解法2:递归**
  • 7.分隔链表
      • **解法:双链表**
  • 8.环形链表
      • **解法1:快慢指针法**
      • **解法2:哈希表**

知识补充:

递归三要素:

  1. 大问题能拆成若干个小问题的解。
  2. 拆分后的子问题和原问题除了数据规模不同,思路完全相同。
  3. 存在问题的终止条件,不借助任何外部函数的特殊值,直接得出答案。

链表问题三大常用方法:

  1. 虚拟头节点法
  2. 递归
  3. 快慢指针法

1.删除单链表中的元素

1.1 删除排序链表中的重复元素

题目链接: 83.删除链表中的重复元素


题目内容:给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次返回已排序的链表 。

示例
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第1张图片
输入:head = [1,1,2,3,3]
输出:[1,2,3]


解法1:迭代

要找重复元素,这里使用两个引用:prev,cur

  1. base case:若链表为空或只有一个元素,肯定不会有重复元素,直接返回即可。
  2. 当链表长度大于1时,给定一个虚拟头结点,让prev指向虚拟头结点,cur指向后一个节点。判断这两个节点的值是否相同。
  3. 若不相同,则prev=prev.next。
  4. 若相同,prev.next=cur.next,使prev指向相同元素节点的下一个节点,cur=cur.next,再继续判断,重复以上过程。
  5. 最后返回dummyHead.next即可。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第2张图片

    public ListNode deleteDuplicates(ListNode head) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        //此时链表一定两个节点
        //虚拟头节点
        ListNode dummyhead=new ListNode(-101);
        dummyhead.next=head;
        //prev指向的一定不是重复元素
        ListNode prev=dummyhead;
        //双指针比较元素是否重复
        ListNode cur=prev.next;
        while(cur!=null){
            if(prev.val==cur.val){
                prev.next=cur.next;
            }else{
                prev=prev.next;
            }
            cur=cur.next;
        }
        return dummyhead.next;
    }

解法2:递归

链表是天然的递归结构。

首先,明确deleteDuplicates()方法的作用是删除所有重复的元素,得到一个所有元素只出现一次的已排序的链表 。

那么,整个链表就可以分为 head+除head以外的所有节点
在这里插入图片描述
head+deleteDuplicates(head.next)
此时deleteDuplicates(head.next)已经是一个没有重复元素的链表,那么只需要判断head和此链表的head的值是否相同。

步骤:

  1. 边界条件,若链表为空或只有一个元素,肯定不会有重复元素,直接返回即可。
  2. 要删除重复元素,先把以head.next为头结点的子链表中的所有重复元素删除完。
  3. 最后比较head和head.next的值。
    public ListNode deleteDuplicates(ListNode head) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        head.next=deleteDuplicates(head.next);
        return head.val==head.next.val ? head.next:head;
    }

1.2 删除排序链表中的重复元素Ⅱ

题目链接: 82.删除排序链表中的重复元素Ⅱ


题目内容: 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第3张图片
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

本题和上一题不同的地方在于,只留下不同的元素,重复元素一个不留。


解法1:迭代

这里使用三指针法:prev,cur,sec

  • prev一定指向不同元素
  • sec一定指定cur的下一个

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第4张图片

步骤:

  1. 边界条件,若链表为空或只有一个元素,肯定不会有重复元素,直接返回即可。
  2. sec==null时,循环结束,没有可比较的对象了。
  3. cur.val != sec.valprev=prev.next,prev向后移动。
  4. cur.val==sec.val时,循环判断,sec一直向后移动,直到cur.val != sec.val,此时prev.next指向sec。
  5. cur移动到sec的位置。在新一轮循环中,让sec=cur.next。继续判断。
public ListNode deleteDuplicates(ListNode head) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        //虚拟头结点
        ListNode dummyHead=new ListNode(-101);
        dummyHead.next=head;
        ListNode prev=dummyHead;
        ListNode cur=prev.next;
        while(cur!=null){
            ListNode sec=cur.next;
            if(sec==null){
                break;
            }
            if(cur.val!=sec.val){
                prev=prev.next;
            }else{
                while( sec!=null && cur.val==sec.val){
                    sec=sec.next;
                }
                //此时,sec和cur一定不相等
                prev.next=sec;
            }
            cur=sec;
        }
        return dummyHead.next;
    }

解法2:递归

链表可以看作head以head.next为头结点的子链表的组合。
在这里插入图片描述

思路:

  1. 边界条件,若链表为空或只有一个元素,肯定不会有重复元素,直接返回即可。
  2. head.val !=head.next.val,head.next直接指向以head.next为头结点的链表。
  3. head.val ==head.next.val,此时头结点就是重复的节点,先处理头结点的情况,给定一个引用newHead等于head.next。判断head.val==newHead.val,若相等,让newHead不停向后移动,直到不相等。此时newHead一定不是重复元素,返回deleteDuplicates(newHead)
    public ListNode deleteDuplicates(ListNode head) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        if(head.val !=head.next.val){
            head.next=deleteDuplicates(head.next);
            return head;
        }else{
        	//头结点就是重复的节点,先处理完头结点的情况
            ListNode newHead=head.next;
            while(newHead!=null && head.val==newHead.val){
                newHead=newHead.next;
            }
            //此时newHead一定不是重复元素
            return(deleteDuplicates(newHead));
        }
    }

1.3 移除链表元素

题目链接: 203.移除链表元素


题目内容: 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第5张图片
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]


解法1:迭代

思路:

  1. 边界条件,如何head为空,直接返回null。
  2. 给定一个虚拟头结点,连接head。给定一个引用prev指向虚拟头结点。(注意:prev一定指向值不为val的节点)
  3. 遍历链表。若prev.next.val==val,prev直接指向prev.next的下一个节点。
  4. 若不相等,prev直接向后移动。
		public ListNode removeElements(ListNode head, int val) {
        //1.base case
        if(head==null){
            return null;
        }
        //虚拟头节点
        ListNode dummyHead=new ListNode();
        dummyHead.next=head;
        //prev一定指向值不为val的节点
        ListNode prev=dummyHead;
        while(prev.next!=null){
            if(prev.next.val==val){
                prev.next=prev.next.next;
            }else{
                prev=prev.next;
            }
        }
        return dummyHead.next;
    }

解法2:递归

链表可以看作head以head.next为头结点的子链表的组合。
在这里插入图片描述

思路:

  1. 边界条件,如何head为空,直接返回null。
  2. removeElements(head.next,val)一定是值不为val的链表。head.next直接指向removeElements(head.next,val)
  3. 此时只需判断头节点的值是否为val。若是,返回head.next,否则返回head。
    public ListNode removeElements(ListNode head, int val) {
        //base case
        if(head==null){
            return null;
        }
        head.next=removeElements(head.next,val);
        return head.val==val ? head.next:head;
    }

2.反转链表

2.1 反转链表

题目链接:206.反转链表


题目内容: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第6张图片
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]


解法1:头插法

思路:若题目没有空间限制,则可以遍历原链表,不断产生新节点,在头插到新链表中,最后返回新链表。

   public ListNode reverseList(ListNode head) {
        //base case 
        if(head==null || head.next==null){
            return head;
        }
        //虚拟头结点
        ListNode dummyHead=new ListNode(-1);
        //不断遍历原链表,产生新节点,头插到新链表
        while(head!=null){
            ListNode cur=new ListNode(head.val);
            cur.next=dummyHead.next;
            dummyHead.next=cur;
            head=head.next;
        }
        return dummyHead.next;
    }

解法2:原地反转

思路:

  1. 原先prev是指向cur的,反过来,让cur指向prev就实现了反转。
  2. 但是cur.next指向prev之后,剩下的链表就断开了找不到了,所以需要先用一个next记住原链表cur的下一个节点。
    当cur为null时,prev刚好在最后一个节点,直接返回prev即可。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第7张图片

    public ListNode reverseList(ListNode head) {
        //base case
        if(head==null || head.next==null){
            return null;
        }
        ListNode prev=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode next=cur.next;
            cur.next=prev;
            prev=cur;
            cur=next;
        }
        return prev;
    }

解法3:递归

思路:

  1. reverseList(ListNode head)的作用是得到一个反转后的链表。

  2. 则可以把链表分为头节点和以head.next为头节点的反转后的链表。

  3. 此时只需要将head连接到子链表的尾端即可。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第8张图片

    public ListNode reverseList(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode next=head.next;
        ListNode newHead=reverseList(head.next);
        //拼接当前头节点和转后的子链表
        head.next=null;
        next.next=head;
        return newHead;
    }

2.2 反转链表Ⅱ

题目链接:92.反转链表Ⅱ


题目内容: 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第9张图片
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]


解法:部分反转

思路:

  1. 给定两个引用,记住left的前一个节点和right的后一个节点。
  2. 先将待反转的区域反转
  3. 把 pre 的 next 指针指向反转以后的链表头节点,把反转以后的链表的尾节点的 next 指针指向 succ。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第10张图片

    public ListNode reverseBetween(ListNode head, int left, int right) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        ListNode dummyHead=new ListNode(-1);
        dummyHead.next=head;
        
        ListNode prev=dummyHead;
        //从temp开始走,来到left的前一个节点
        for(int i=0;i<left-1;i++){
            prev=prev.next;
        }
        //从prev开始走,一直到right节点
        ListNode rightNode=prev;
        for(int i=0;i<right-left+1;i++){
            rightNode=rightNode.next;
        }
        
        //截取要反转的链表
        ListNode next=rightNode.next;
        ListNode leftNode=prev.next;
        //切断连接
        prev.next=null;
        rightNode.next=null;

        //反转链表子区间
        reverseList(leftNode);

        //接回原链表
        prev.next=rightNode;
        leftNode.next=next;
        return dummyHead.next;
    }

    public ListNode reverseList(ListNode head) {
        ListNode prev=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode next=cur.next;
            cur.next=prev;
            prev=cur;
            cur=next;
        }
        return prev;
    }

3.查找链表中结点

3.1 链表的中间结点

题目链接:876.链表的中间结点


题目内容: 给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

示例:
在这里插入图片描述
输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。


解法1:按长度查找

步骤:

  1. 遍历原链表,得到链表的长度
  2. 若长度为奇数,直接走 长度/2 的步数。
  3. 若长度为偶数,也是直接走 长度/2 的步数。
    public ListNode middleNode(ListNode head) {
        int count=0;
        ListNode temp=head;
        while(temp!=null){
            temp=temp.next;
            count++;
        }
        int n=count/2;
        while(n>0){
            head=head.next;
            n--;
        }
        return head;
    }

解法2:快慢指针法

思路:

  1. 两个指针,low 和 fast
  2. low每走一步,fast就走两步
  3. 当fast走到终点,low就停在我们想要的位置

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第11张图片

    public ListNode middleNode(ListNode head) {
        ListNode low=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            low=low.next;
            fast=fast.next.next;
        }
        return low;
    }

3.2 链表中倒数第k个节点

题目链接:剑指Offer 22.链表中倒数第k个结点


题目内容: 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.


解法1:按长度查找

思路:

  1. 遍历链表,得到链表的长度n。
  2. 倒数第k个结点就是正数第n-k个结点。
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head==null || k<=0){
            return null;
        }
        ListNode temp=head;
        int n=0;
        while(temp!=null){
            temp=temp.next;
            n++;
        }
        int count=n-k;
        while(count>0){
            head=head.next;
            count--;
        }
        return head;
    }

解法2:快慢指针法

思路:

  1. 给定两个引用sec,fir
  2. fir先向前走k步。
  3. sec 和 fir 同时向前走,当fir为null时,sec刚好指向倒数dik个结点。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第12张图片

    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head==null || k<=0){
            return null;
        }
        ListNode fir=head;
        ListNode sec=head;
        for(int i=0;i<k;i++){
            //判断k>链表长度的情况
            if(fir==null){
                return null;
            }
            fir=fir.next;
        }
        while(fir!=null){
            fir=fir.next;
            sec=sec.next;
        }
        return sec;
    }

4.回文链表

题目链接:234.回文链表


题目内容: 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第13张图片
输入:head = [1,2,2,1]
输出:true


解法:一分为二

思路:

  1. 判断回文链表其实就是判断对称链表。
  2. 将链表一份为二,l1为头节点到中间结点的链表,l2为中间结点到尾节点的链表。
  3. 将l2反转,与l1的值进行对比。遍历,直到其中一条链表走到null,若有不一样的值,则不为回文链表。反之,则是回文链表。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第14张图片

   public boolean isPalindrome(ListNode head) {
        if(head==null || head.next==null){
            return true;
        }
        ListNode middleNode=middleNode(head);
        //反转中间结点之后的链表
        ListNode l2=reverseList(middleNode);
        //找反例
        while(head!=null && l2!=null){
            if(head.val != l2.val){
                return false;
            }
            head=head.next;
            l2=l2.next;
        }
        return true;
    }
    //查找中间节点
    public ListNode middleNode(ListNode head) {
        int count=0;
        ListNode temp=head;
        while(temp!=null){
            temp=temp.next;
            count++;
        }
        int n=count/2;
        while(n>0){
            head=head.next;
            n--;
        }
        return head;
    }
    //反转链表
      public ListNode reverseList(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode next=head.next;
        ListNode newHead=reverseList(head.next);
        //拼接当前头节点和转后的子链表
        head.next=null;
        next.next=head;
        return newHead;
    }

5.相交链表

题目链接:160.相交链表


题目内容: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第15张图片
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。


解法:全遍历

如图所示:

  • listA的长度=x+z
  • listB的长度=y+z

但若让A和B都走一遍对方的路程:即 x+z+y=y+z+x。此时它们的路程一定相等,走完之后一定会相交。

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第16张图片
过程图举例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第17张图片

步骤:

  1. 引入两个引用listA,listB。
  2. listA走到终点后倒过来走listB。
  3. listB走到终点后倒过来走listA。
  4. 若不存在交点,则listA和listB最后都指向null。
  5. 若存在交点,则两个链表走完全程后一定指向交点。
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //引入两个引用
        ListNode listA=headA;
        ListNode listB=headB;

        while(listA!=listB){
            listA = listA==null ? headB:listA.next;
            listB = listB==null ? headA:listB.next;
        }
        return listA;
    }

6.合并链表

题目链接:21.合并两个有序链表

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


示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第18张图片
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]


解法1:虚拟头节点法

思路:

  1. 给定一个虚拟头结点dummyHead;
  2. 遍历两个链表。比较结点值的大小,将较小值拼接在新链表后面。

图解:

【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第19张图片

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1==null){
            return list2;
        }
        if(list2==null){
            return list1;
        }
        ListNode dummyHead=new ListNode(-1);
        ListNode tail=dummyHead;
        //取较小值拼接在链表的尾部
        while(list1!=null && list2!=null){
            if(list1.val<list2.val){
                tail.next=list1;
                tail=list1;
                list1=list1.next;
            }else{
                tail.next=list2;
                tail=list2;
                list2=list2.next;
            }
        }
        //此时,至少一个链表为Null
        if(list1==null){
            tail.next=list2;
        }
        if(list2==null){
            tail.next=list1;
        }
        return dummyHead.next;
    }

解法2:递归

思路:

  1. 已知 mergeTwoLists(ListNode list1, ListNode list2)方法的作用是将两个升序链表合并为一个新的 升序 链表并返回。
  2. 如果list1.vallist1+ mergeTwoLists(ListNode list1.next, ListNode list2)
  3. 如果list2.valmergeTwoLists(ListNode list1, ListNode list2.next)
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1==null){
            return list2;
        }
        if(list2==null){
            return list1;
        }
        if(list1.val<=list2.val){
            list1.next=mergeTwoLists(list1.next,list2);
            return list1;
        }else{
            list2.next=mergeTwoLists(list1,list2.next);
            return list2;
        }
    }

7.分隔链表

题目链接:86.分隔链表


题目内容: 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于x 的节点都出现在大于或等于 x 的节点之前。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第20张图片

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]


解法:双链表

思路:

  1. 题目要求值小于x的结点都放在x结点之前。所以,可以新建两个链表。
  2. 一个链表的值都小于x,一个链表的值都大于等于x。
  3. 遍历原链表,将对应结点尾插到相应链表后面。
  4. 小链表拼接大链表即可。

图解:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第21张图片
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第22张图片

    public ListNode partition(ListNode head, int x) {
        //base case
        if(head==null || head.next==null){
            return head;
        }
        //新建两个链表,一个为值小于x的链表,一个为值大于x的链表
        ListNode smallNode=new ListNode();
        ListNode bigNode=new ListNode();
        //定义两个链表的尾节点
        ListNode smallTail=smallNode;
        ListNode bigTail=bigNode;
        //遍历原链表
        while(head != null){
            //值小于x的结点尾插到小链表的尾部
            if(head.val<x){
                smallTail.next=head;
                smallTail=head;
                head=head.next;    
            }else{
                //值大于x的结点尾插到大链表的尾部
                bigTail.next=head;
                bigTail=head;
                head=head.next;
            }
        }
        //小链表连接大链表
        smallTail.next=bigNode.next;
        bigTail.next=null;
        return smallNode.next;
    }

8.环形链表

题目链接:141.环形链表


题目内容: 给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例:
【数据结构练习题】单链表问题解决(虚拟头节点法,递归,快慢指针法)_第23张图片
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。


解法1:快慢指针法

思路:

  1. 给定两个引用,一个一次走一步,一个一次走两步。
  2. 若它们不相遇,则不为环形链表。
  3. 若相遇,则为环形链表。
    public boolean hasCycle(ListNode head) {
        if(head==null || head.next==null){
            return false;
        }
        ListNode low=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            low=low.next;
            fast=fast.next.next;
            if(low==fast){
                return true;
            }
        }
        return false;
    }

解法2:哈希表

知识补充:HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。

思路:

  1. 遍历链表,将结点存入HashSet。
  2. 遍历过程中,若有结点已经存在哈希表中,说明是环形链表。
    public boolean hasCycle(ListNode head) {
        if(head==null || head.next==null){
            return false;
        }
        Set<ListNode>list=new HashSet<>();
        while(head!=null){
            if(!list.add(head)){
                return true;
            }
            head=head.next;
        }
        return false;
    }

你可能感兴趣的:(数据结构,数据结构,链表,算法,java)