一、链表是否有环判断
题型:
判断一个链表是否有环?如果无环则返回空,如果有环则返回第一个入环节点。
思路:
这类题目如果对于空间复杂度没有要求的话,我们可以使用哈希表来帮助我们实现遍历链表过程中,链表节点的访问记录。但是这类题目的最佳解法可以做到空间复杂度为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;
}
}