Java链表oj题(快慢指针)

本文概述了如何使用双指针技术解决LeetCode中的链表问题,如反转链表、查找中间节点、倒数第K个节点、合并有序链表、链表分割、判断回文链表、链表相交和带环检测,强调了画图理解和灵活运用快慢指针的重要性。
摘要由CSDN通过智能技术生成

目录

1、反转一个链表

2、返回链表的中间节点

3、输出链表的倒数第K个节点

4、合并两个有序链表

5、链表分割

6、判断链表是否为回文链表

7、判定链表相交并求出交点

8、判断链表是否带环

9、返回链表开始入环的第一个节点

总结

快慢指针



1、反转一个链表

https://leetcode.cn/problems/reverse-linked-list/solutions/

思路:(双指针)

1、先从头开始遍历一遍链表依次翻转

2、记录要翻转的节点以及他的下一个节点(rever、reverNext)

3、head的下一个节点为rever,记录好之后把head.next置空

4、如果只有一个节点,则直接返回他的头节点

public ListNode reverseList(ListNode head) {
        //判断是否只有一个节点
        if(head == null){
            return null;
        }
        //只有一个节点
        if(head.next == null){
            return head;
        }
        
        ListNode rever = head.next;
        head.next = null;
        while(rever != null){
            ListNode reverNext = rever.next;
            rever.next = head;
            head = rever;
            rever = reverNext;
        }
        return head;

    }

2、返回链表的中间节点

876. 链表的中间结点 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/middle-of-the-linked-list/description/

思路:

快慢指针(数学思想:时间相同,A速度是B速度的两倍,则A走过的路程是B走过路程的两倍

1、一开始slow和fast都指向头节点

2、每次让fast走两步,slow走一步

3、当fast走到最后一个节点时slow刚好走到链表的中间

public ListNode middleNode(ListNode head) {
        //快慢指针
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;

    }

3、输出链表的倒数第K个节点

链表中倒数最后k个结点_牛客题霸_牛客网输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/share/jump/7547604141710814034038

两种方法:

法1:还是快慢指针,当快指针比慢指针多走k步时,如果快指针走到了最后一个节点,那么慢指针刚好在倒数第k个节点的位置

 //思路:快慢指针,快指针比慢指针多走k步
        ListNode fast = pHead;
        ListNode slow = pHead; 
        int len = 0;
        //求链表的长度
        while(cur != null){
            cur = cur.next;
            len++;
        }
        // 如果该链表长度小于k,请返回一个长度为 0 的链表。
        //这个和没有节点的链表情况一样,合并代码即可
        if(k > len){
            return null;
        }
        //快指针走k步
        while(k > 0){
            if(fast != null){
                fast = fast.next;
            }else{
                slow = null;
            }
            k--;
        }
        // 两个指针同步走
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;

法2:

public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        ListNode cur = pHead;
        int len = 0;
        //求链表的长度
        while(cur != null){
            cur = cur.next;
            len++;
        }
       // 如果该链表长度小于k,请返回一个长度为 0 的链表。这个和没有节点的链表情况一样,合并代码即可
        if(k > len){
            return null;
        }
        cur = pHead;
        for(int i = 0;  i < len-k ;i++){
            cur = cur.next;
        }
        return cur;
      

4、合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/merge-two-sorted-lists/description/

画图理解

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //先定义一个新节点作为新的节点
        ListNode newHead = new ListNode(-1);
        ListNode tmpH = newHead;
        //当两个链表都不空时比较他们数值的大小
        while(list1 != null && list2 != null){
            if(list1.val <  list2.val){
                //小的接在新的头节点后面 
                tmpH.next = list1;
                tmpH = list1;
                list1 = list1.next;
            }else{
                tmpH.next = list2;
                tmpH = list2;
                list2 = list2.next;
            }
        }
        //list1 比list2短
        if(list1 == null){
            tmpH.next = list2;
        }
        if(list2 == null){
            tmpH.next = list1;
        }
        return newHead.next;
    }

5、链表分割

链表分割_牛客题霸_牛客网现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/share/jump/7547604141710813826653

public ListNode partition(ListNode pHead, int x) {
        // write code here
        //思路:x把链表分为两部分,一部分比x小,一部分比x大,
        //分别用双指针分别记录下两段,最后第一段的尾巴接上第二段的头即可
        ListNode fBegin = null;//作为第一段的头节点  
        ListNode fAfter = null;
        ListNode rBegin = null;//作为第二段的头节点
        ListNode rAfter = null;
        ListNode cur = pHead;
        //依次遍历链表进行分组
        while(cur != null){
            //如果链表的值比x小,放在第一段
            if(cur.val < x){
                //如果是第一次插入
               if(fBegin == null){
                    fBegin = cur;
                    fAfter = cur;
               }else {
                fAfter.next = cur;
                fAfter = fAfter.next;
               }
                
            }else{
                 //如果是第一次插入
               if(rBegin == null){
                    rBegin = cur;
                    rAfter = cur;
               }else {
                rAfter.next = cur;
                rAfter = rAfter.next;
               }    
            }
            cur = cur.next;
        }
        if(fBegin == null){
            return rBegin;
        }
        fAfter.next = rBegin;
        //如果第二段的最后一个不为空,则需要置空,否则会形成环
        if(rBegin != null){
             rAfter.next = null;
        }
        return fBegin;
        
    }

6、判断链表是否为回文链表

链表的回文结构_牛客题霸_牛客网对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/share/jump/7547604141710813920410集合了前面找中间节点和翻转节点的思想,是重点题型,画图理解

 public boolean chkPalindrome(ListNode A) {
        // write code here
        //1、先找中间节点
        ListNode fast = A;
        ListNode slow = A;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
           
        }
        //此时slow正处在中间节点的位置,而fast在最后一个节点(如果偶数的话就越界溢出了)
        //2、翻转后面的节点
        ListNode cur = slow.next;
        while(cur != null){
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        //3、开始判断是否为回文,此时slow在最后一个节点,head和slow进行比较
        while(A != slow){
            if(A.val != slow.val){
                return false;
            }
            //针对偶数节点
            if(A.next == slow){
                return true;
            }
            A = A.next;
            slow = slow.next;

        }
        return true;

    }

7、判定链表相交并求出交点

https://leetcode.cn/problems/intersection-of-two-linked-lists/submissions/513940055

思路:还是可以使用快慢指针,相交节点之后的长度是一样的,所以两个链表的长度不同在前面,所以只需要快指针多走两个链表长度之差,然后再一起走如果他们相遇了(slow == fast)就找到相交节点了

步骤:1、首先先求出两链表的长度

 2、求差值

 3、先让fast多走len步

 4、slow和fast同步走,直到相遇

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //思路:还是可以使用快慢指针,相交节点之后的长度是一样的,
        //所以两个链表的长度不同在前面,所以只需要快指针多走两个链表长度之差,然后再一起走
        //如果他们相遇了(slow.next == fast.next)就找到相交节点了


        //1、首先先求出两链表的长度
        //先假设headA的长度要长
        int lenA = 0;
        int lenB = 0;
        ListNode tmpHA = headA;
        ListNode tmpHB = headB;
        ListNode fast = null;
        ListNode slow = null;
        while(tmpHA != null){
            tmpHA = tmpHA.next;
            lenA ++;
        }
        while(tmpHB != null){
            tmpHB = tmpHB.next;
            lenB ++;
        }

        //2、求差值
        int len = lenA - lenB;
        if(len < 0){//说明lenB的长度长
            len = lenB - lenA;
            fast = headB;
            slow = headA;
        }else {
            fast = headA;
            slow = headB;
        }

        //3、先让fast多走len步
        while(len > 0){
            fast = fast.next;
            len--;
        }

        //4、slow和fast同步走,直到相遇
        while(fast != slow ){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;

       


    }
}

8、判断链表是否带环

https://leetcode.cn/problems/linked-list-cycle/submissions/513777277

快慢指针,快指针走两步,慢指针走一步,如果有环,则快慢指针一定会相遇

 

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(slow == fast){
            return true;
        }
    }
    return false;
    }
}

9、返回链表开始入环的第一个节点

https://leetcode.cn/problems/linked-list-cycle-ii/description/

思路:快指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始走,两个指针每次均走一步,最终肯定会在入口点的位置相遇

步骤:

1、先找相遇点(快慢指针,快指针走两步,慢指针走一步,如果有环,则快慢指针一定会相遇)

2、让slow指向相遇点,然后两个指针一起走

public class Solution {
    public ListNode hasCycle(ListNode head) {
    //快慢指针,快指针走两步,慢指针走一步,如果有环,则快慢指针一定会相遇
    ListNode fast = head;
    ListNode slow = head;
    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;
        if(slow == fast){
            return fast;
        }
    }
    return null;
    }
    
    public ListNode detectCycle(ListNode head) {
    //方法:快指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始走,
    //两个指针每次均走一步,最终肯定会在入口点的位置相遇

    //1、先找相遇点(快慢指针,快指针走两步,慢指针走一步,如果有环,则快慢指针一定会相遇)
    
    ListNode hasNode = hasCycle(head);

     //2、让slow指向相遇点,然后两个指针一起走
    ListNode fast = head;
    ListNode slow = hasNode;
    //如果有环就开始走
     if(slow != null){
        while(fast !=  slow){
            fast = fast.next;
            slow = slow.next;
        }
     }
      return slow;
    }



}

总结

这几道oj题运用了双指针等思想,重点是一定要画图理解,然后慢慢整理思路,考虑各种情况,最后调试

双指针包括:快慢指针、对撞指针、滑动窗口等,以上大部分编程题都用的是快慢指针解决,需了解其思想,掌握应用场景

快慢指针

在数组、序列、链表中以不同速度移动的指针。一般来说,快指针一次走两步,慢指针一次走一步,即快指针是慢指针速度的两倍(走的步数可以依题改变),可以解决:

  • 处理链表或数组中的循环的问题(链表带环)
  • 找链表中点或需要知道特定元素的位置

等应用场景。以上题大多应用到了这个思想,多练多想

Java中,使用快慢指针(也称为龟兔赛跑法)是常见的判断链表是否有环的方法。这种方法基于两个指针,一个每次移动一个节点,另一个每次移动两个节点。如果链表中有环,那么快指针最终会追上慢指针;如果没有环,快指针会先到达链表尾部。 下面是基本的实现步骤: 1. 初始化两个指针:`slow`(慢指针)和`fast`(快指针),分别指向链表的头节点。 2. 指针遍历:如果链表不为空,循环执行以下操作: a. `slow`向前移动一步(`slow.next`)。 b. `fast`向前移动两步(`fast.next.next`)。 3. 判断环的存在:如果`fast`指针在某次迭代中到达了`null`,说明链表没有环,因为快指针走过的距离是慢指针的两倍,如果链表长度为偶数,快指针应该在链表末尾找到慢指针,如果奇数,则会先到尾部再回环,不会追上。如果`fast`始终不为`null`,且与`slow`相遇(它们都指向同一个节点),那么链表中存在环。 下面是伪代码形式的实现: ```java public boolean hasCycle(ListNode head) { ListNode slow = head; ListNode fast = head; // 如果链表为空或只有一个节点,不存在环 if (head == null || head.next == null) { return false; } // 快慢指针开始遍历 while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; // 如果快指针先到末尾,则链表无环 if (fast == null) { return false; } // 如果快慢指针相遇,说明链表有环 if (slow == fast) { return true; } } return false; // 如果没有提前结束循环,说明链表无环 } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值