• 链表中的快慢指针法


    快慢指针法:

    快慢指针一般都初始化指向链表的头结点 head,前进时快指针 fast 在前,慢指针 slow 在后,巧妙解决一些链表中的问题。

    1.判定链表中是否含有环(leetcode141.环形链表)

    这应该属于链表最基本的操作了,单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表中是否含有环的。
    如果链表中不含环,那么这个指针最终会遇到空指针 null 表示链表到头了
    但是如果链表中含有环,那么这个指针就会陷入死循环,因为环形数组中没有 null 指针作为尾部节点。
    经典解法就是用两个指针,一个每次前进两步,一个每次前进一步。如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。

    public class Solution {
        public boolean hasCycle(ListNode head) {
            ListNode fast,slow;
            fast=slow=head;
    
            while(fast!=null && fast.next!=null){
                fast=fast.next.next;
                slow=slow.next;
                if(fast==slow){
                    return true;
                }
            }
            return false;       
        }
    }
    # 这里写法要注意的是因为fast快,所有如果无环,那么肯定是fast先碰到null
    # 另外因为fast=fast.next.next; 所以fast本身可能是null,fast.next也可能是null
    

    2.已知链表中含有环,返回这个环的起始位置

    当快慢指针相遇时,让其中任一个指针重新指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
    第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)
    设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点,巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。
    所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。

    public class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode fast = head, slow = head;
            //获取首次相遇时候,slow的位置
            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;
            //快指针重新出发,相遇位置就是入口位置
            fast = head;
            while(fast != slow){
                fast = fast.next;
                slow = slow.next;
            }
            return slow;
        }
    }
    //之所以要在第一个循环结束之后再判断if(fast == null || fast.next == null) return null;
    //是因为可能存在链表中只有一个值,那么相当于根本没有进行第一个循环
    //这行就是为了处理这个特殊的可能性
    
    

    3.寻找链表的中点(寻找链表中点的一个重要作用是对链表进行归并排序)

    我们还可以让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
    当链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右:

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

    以上是我的代码,虽然是分类讨论了,但要注意分类讨论完成之后还是要找共性,尽量合并的写出

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

    4.寻找链表倒数第k个元素

    思路还是使用快慢指针,让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度):

    class Solution {
        public ListNode getKthFromEnd(ListNode head, int k) {
            ListNode former = head, latter = head;
            for(int i = 0; i < k; i++)
                former = former.next;
            while(former != null) {
                former = former.next;
                latter = latter.next;
            }
            return latter;
        }
    }
    
  • 相关阅读:
    c#之继承
    javascript post数据到WCF反馈json或者XML
    五:理解控件的运行机制(例:基于Control命名空间的简单控件)
    c#之委托和事件
    项目管理中的十一个原则
    Linq:基本语法group by, order by ,into(3)
    Linq:基本语法form ,select, where(2)
    PL/SQL学习笔记视图
    七:理解控件的运行机制(例:基于CompositeControl命名空间的控件)
    PL/SQL学习笔记常量变量及数据类型初步
  • 原文地址:https://www.cnblogs.com/shiji-note/p/14394711.html
Copyright © 2020-2023  润新知