【面试算法】——链表(二)

一、链表是否有环判断

题型:

判断一个链表是否有环?如果无环则返回空,如果有环则返回第一个入环节点。

思路:

这类题目如果对于空间复杂度没有要求的话,我们可以使用哈希表来帮助我们实现遍历链表过程中,链表节点的访问记录。但是这类题目的最佳解法可以做到空间复杂度为O(1),如果链表长度为N,那么空间复杂度可以做到O(N)。

最优解法思路为:

创建两个指针,分表表示快指针和慢指针,快指针每次走两个节点,慢指针每次走一个节点,如果链表没有环结构的话,当快指针next指向空时,结束并返回false;如果链表存在环结构的话,那么快慢指针一定会在某一个点相遇,这个时候我们保持慢指针在当前位置不动,让快指针回到头节点,并且向后遍历链表,这一次遍历指针一次只走一个节点,当两者再次相遇时,相遇位置为第一个入环节点

代码举例:

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class ChkLoop {
    public int chkLoop(ListNode head, int adjust) {
        // write code here
        if(head == null || head.next == null)
            return -1;
        //建立两个指针,一个快指针一次走两步,一个慢指针一次走一步
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow != fast){
                continue;
            }else{
                break;
            }
        }
        if(fast == null || fast.next == null)
            return -1;
        //快指针从新从头节点开始遍历,每次走一步,直到再次相遇
        fast = head;
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return slow.val;
    }
}

二、链表相交问题

题型:

在链表的算法面试题中,常常会出现要求判断两个链表是否相交的问题,这类问题可以划分为不同的情况

  • 无环链表相交判断
  • 有环链表相交判断
  • 有无环未知链表相交判断

思路:

如果是判断无环链表是否相交的问题,在没有要求空间复杂度的情况下,可以使用哈希表结构帮助我们解题,但是最优解可以达到空间复杂度为O(1),如果两个链表的长度为M和N,那么时间复杂度为O(M+N),主要做法是,遍历同时两个链表,比较每一次遍历的节点是否相同,如果A链表比B链表长,例如为100和50,则让A链表先遍历50再和B链表同时遍历。

代码举例:

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class CheckIntersect {
    public boolean chkIntersect(ListNode headA, ListNode headB) {
        // write code here
        //无环单链表相交判断
        if (headA == null || headB == null)
            return false;
        ListNode cur1 = headA;
        while (cur1.next != null) {
            cur1 = cur1.next;
        }
        ListNode cur2 = headB;
        while (cur2.next != null) {
            cur2 = cur2.next;
        }
        return cur1 == cur2;
    }
}

如果是有环链表,那么如果两个链表相加,就有两种可能,一种是在入环节点前相交,这种情况下,两者的入环节点相等;另一种是在环结构中相交,这种情况下,我们只需要找到两个链表的入环节点,同时让其中一个链表遍历环结构,如果在遍历过程中遇到了另一个链表的环结构,证明两个链表相交,则返回这两个入环节点中的任意一个。

代码举例:

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class ChkIntersection {
    public boolean chkInter(ListNode head1, ListNode head2, int adjust0, int adjust1) {
        // write code here
        //首先求出两个环的入环节点
        ListNode p1 = Chloop(head1);
        ListNode p2 = Chloop(head2);
        //如果入环节点相同,那么说明在入环之前相交
        if(p1 == p2)
            return true;
        else
        {
            ListNode cur = p1.next ;
            while(cur != p1)
            {
                if(cur == p2)
                    return true;
                cur = cur.next;
                    
            }
            return false;
        }
    }
    //求有环链表的入环节点
    public ListNode Chloop(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;
        fast = head;
        while(fast!=slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}

如果是未知环情况,则首先要判断是否有环结构

代码举例:

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class ChkIntersection {
    public boolean chkInter(ListNode head1, ListNode head2, int adjust0, int adjust1) {
        // write code here
        if (head1 == null || head2 == null) {
            return false;
        }
        // 返回两个有环单链表的如环节点
        ListNode h1 = chkLoop(head1);
        ListNode h2 = chkLoop(head2);
        ListNode cur = h1.next;
        if (h1 == h2) return true;
        while (cur != h1) {
            if (cur == h2) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    public ListNode chkLoop(ListNode head) {
        // 合法性判断
        if (head == null) {
            return null;
        }
        // 定义两个快慢指针
        ListNode pSlow = head;
        ListNode pFast = head;
        while (pFast != null && pFast.next != null) {
            pSlow = pSlow.next;
            pFast = pFast.next.next;
            if (pFast == pSlow) { // 有环
                break;
            }
        }
        // 无环
        if (pFast == null || pFast.next == null) {
            return null;
        }
        // 有环
        pFast = head;
        while (pFast != pSlow) {
            pFast = pFast.next;
            pSlow = pSlow.next;
        }
        return pFast;
    }
}

 

 

 

 

    原文作者:CodeLikeWind
    原文地址: https://blog.csdn.net/qq_36125072/article/details/81905823
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞