链表面试题----快慢指针

一.何为快慢指针

快慢指针指的是定义两个指针,一个指针移动速度快(称之为快指针),一个指针移动速度慢(称之为慢指针),这样之间就会产生我们想要的差值,然后通过这种差值我们就可以找到我们想要的节点.一般我们会让快指针一次走两步,慢指针一次走一步.(为防止空指针异常).

二.利用快慢指针的面试题

1.寻找中间节点

我们首先定义一个fast快指针,然后定义一个slow慢指针.让slow每次走一步,fast每次走两步.当fast走到空的时候,slow节点就是中间节点.

代码如下:

//寻找中间节点
    public ListNode middle(ListNode head){
        if (head==null){
            return null;
        }
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }
2.链表中的倒数第k个节点

很多同学就会想到既然是求倒数的第k个节点,那么就先可以把链表的总长度求出来,然后用链表的长度减去k,得到的差值就是我们要走的步数.对于这种解法是可以实现,但是就说明要遍历两次这个链表,时间复杂度就比较高.为了达到面试的高度,我们可以使用快慢指针实现这一题.

快慢指针的思想:首先我们让快指针走k-1步(此时我们要判断一下k的值是否合法,k<0的情况或者k>链表的长度的情况).

然后让fast和slow同时走,当fast==null || fast.next==null的时候我们就停止

往后走,此时slow为倒数第k个节点.

如下图所示:

代码如下:

    //链表中倒数第k个结点
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode slow=head;
        ListNode fast=head;
        if(head==null||k<=0){
            return null;
        }
       //让快指针先走k-1步
        while(k-1!=0){
            fast=fast.next;
            if(fast==null){
                return null;
            }
            k--;
        }
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next;
        }
        return slow;
    }
3.判断链表是否有环

有同学可能会想,一个链表怎样才能算一个环呢?我们来,上图给同学.

这一题我们还是需要利用快慢指针的方法,让快指针一次走两步,慢指针一次走一步.如果这个链表是带环的,slow和fast是会遇到(及slow=fast),这就是类似我们数学当中的追及问题.

//判断一个链表是否有环
    public boolean hasCycle(){
        if (head==null){
            return false;
        }
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            //说明走到相等了
            if (fast==slow){
                return true;
            }
        }
        //如果链表走到了空说明没有环
        return false;
    }
4.寻找第一个入环地址

首先我们还是要判断这个链表是否有环,就是上一题的代码.如果确定有环,就让slow指针指向链表的头位置,然后fast和slow没人一步走,当fast等于slow这就是第一个入环的地址.

//寻找第一个入环地址
public ListNode detectCycle(){
        if (head==null){
            return null;
        }
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            //找到相遇点
            if (fast==slow){
               break;
            }
        }
        if (fast==null&&fast.next!=null){
            return null;
        }
        slow=head;
        //走到相等就是第一个入环地址
        while(slow!=fast){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
5.判断一个链表是否回文

首先我们要知道什么是回文结构?回文,知道回文结构之后,我们就要开始对链表进行操作了,首先我们要找到这个链表的中间节点(利用快慢指针),然后反转中间节点之后的链表,最后开始判断是否回文.

还要注意一点是:在找节点之前要判断head是否为null,为null就需要返回一个null.

只有一个节点时就是一个回文结构.

反转之后的结构:

然后让head和slow同时走,如果走到相等就是一个回文结构.但是要注意偶数的情况的话head.next=slow就是一个回文结构.

//判断一个链表是否为回文的链表
    public boolean chkPalindrome(ListNode head) {
        // write code here
        if(head==null){
            return true;
        }
        if(head.next==null){
            return true;
        }
        //1.首先要找到中间节点:利用快慢指针
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        //循环走完slow就是中间节点
        //2.然后反转slow之后的链表
        ListNode cur=slow.next;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.next=slow;
            slow=cur;
            cur=curNext;
        }
        //开始判断是否回文
        while(head!=slow){
            if(head.val!=slow.val){
                return false;
            }
            //用来判断链表为偶数的情况
            if(head.next==slow){
                return true;
            }
            head=head.next;
            slow=slow.next;
        }
        return true;
    }

以上是我对快慢指针的一些面试的理解,希望对大家有所帮助,如有错误望大家指出.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值