给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null
法1
能通过,但是复杂度过大,思路为遍历
首先从头节点开始,依次遍历单链表的每一个节点。
每遍历到一个新节点,就从头节点重新遍历新节点之前的所有节点,
用新节点ID和此节点之前所有节点ID依次作比较。
如果发现新节点之前的所有节点当中存在相同节点ID, 则说明该节点被遍历过两次,链表有环;
如果之前的所有节点当中不存在相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。
时间复杂度o(n*n) 空间复杂度o(1)
public ListNode EntryNodeOfLoop1(ListNode pHead) {
ListNode cur = pHead;
ListNode inner = cur;
int count = 0;//计数,防止cur和inner相撞,
// 每次移动cur,inner只能遍历cur的前面的元素,其他不允许
while (cur != null) {
count++;
int incount = count;
while (inner != null) {
incount--;
if (incount == 0) break;
if (inner.val == cur.val) return inner;
inner = inner.next;
}
cur = cur.next;
inner = pHead;
}
return null;
}
法二
首先创建一个以节点ID为键的HashSet集合, 用来存储曾经遍历过的节点。
然后同样是从头节点开始,依次遍历单链表的每一个节点。
每遍历到一个新节点,就用新节点和HashSet集合当中存储的节点作比较, 如果发现HashSet当中存在相同节点ID,则说明链表有环, 如果HashSet当中不存在相同的节点ID, 就把这个新节点ID存入HashSet,之后进入下一节点,继续重复刚才的操作。
思路:HashSet实现了Set接口,它不允许集合中出现重复元素。
Set添加元素时,如果元素值重复时返回 "false",如果添加成功则返回"true"
时间复杂度o(1) 空间复杂度o(n)
public ListNode EntryNodeOfLoop2(ListNode pHead) {
HashSet<Integer> set = new HashSet<>();
while (pHead != null) {
if (!set.add(pHead.val)) {//如果add->false->!false->true
return pHead;
}
pHead = pHead.next;
}
return null;
}
法三(最正统的写法)
首先创建两个指针fast和slow,同时指向这个链表的头节点。
然后开始循环,在循环体中,让指针slow每次向下移动一个节点,让指针fast每次向下移动两个节点, 然后比较两个指针指向的节点是否相同。如果不同,则继续下一次循环。
如果相同,则判断出链表有环,
==>fast留在环里,slow重新指向头节点, slow每次往后一次移动一个结点遍历,因为fast留在环里,所以它总会和slow相遇 相遇后就是返回的入口(至于为什么会slow往下走一定能够会和fast相遇却不会错过,这里涉及到数学问题..)
时间复杂度o(n) 空间复杂度o(1)
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null || pHead.next == null) {
return null;
}
ListNode slow = pHead;
ListNode fast = pHead;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (fast == null) {
return null;
}
if (slow == fast) {
break;
}
}
slow = pHead;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
测试用例
public void tests() {// 1 2 3 4 5 3
ListNode node = new ListNode(1);
node.next = new ListNode(2);
node.next.next = new ListNode(3);
node.next.next.next = new ListNode(4);
node.next.next.next.next = new ListNode(5);
node.next.next.next.next.next = node.next.next;//3
System.out.println(EntryNodeOfLoop1(node).val);
}