链表中环的入口结点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null){
return null;
}
ListNode p1 = pHead;
ListNode p2 = pHead;
while(p2 != null && p2.next != null){
p1 = p1.next;
p2 = p2.next.next;
if(p1 == p2){
p2 = pHead;
while (p1 != p2){
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
}
return null;
}
}
判断有没有环、
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow=head,fast=head;//双指针 :快慢指针
while (fast!=null && fast.next!=null)
{
slow = slow.next;
fast = fast.next.next;
if(slow==fast)
return true;
}
return false;
}
}
不用快慢指针判断链表中是否有环
方法一:HashSet
定义一个HasshSet用来存放节点的引用,并将其初始化为空,从链表的头节点开始向后遍历,每遍历到一个节点就判断HashSet中是否有这个节点的引用。如果没有,说明这个节点是第一次访问,还没有形成环,那么将这个节点的引用添加到HashSet中去。如果在HashSet中找到了同样的节点,那么说明这个节点已经被访问了,于是就形成了环。这种方法的时间和空间复杂度都为O(N)。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//创建集合对象
HashSet<ListNode> set = new HashSet<ListNode>();
//遍历链表
ListNode p = head;
while (p != null){
if (set.contains(p)){
return true;
}else{
set.add(p);
}
p = p.next;
}
return false;
}
}
题目
一个链表中包含环,请找出该链表的环的入口结点。
思路
思路 1
可以用两个指针来解决这个问题。先定义两个指针P1和P2指向链表的头结点。如果链表中的环有n个结点,指针P1先在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向的入口结点时,第一个指针已经围绕着揍了一圈又回到了入口结点。
以下图为例,指针P1和P2在初始化时都指向链表的头结点。由于环中有4个结点,指针P1先在链表上向前移动4步。接下来两个指针以相同的速度在链表上向前移动,直到它们相遇。它们相遇的结点正好是环的入口结点。
现在,关键问题在于怎么知道环中有几个结点呢?
可以使用快慢指针,一个每次走一步,一个每次走两步。如果两个指针相遇,表明链表中存在环,并且两个指针相遇的结点一定在环中。
随后,我们就从相遇的这个环中结点出发,一边继续向前移动一边计数,当再次回到这个结点时,就可以得到环中结点数目了。
思路 2
1、哈希表
遍历整个链表,并将链表结点存入哈希表中(这里我们使用容器set),如果遍历到某个链表结点已经在set中,那么该点即为环的入口结点;
2、两个指针
如果链表存在环,那么计算出环的长度n,然后准备两个指针pSlow,pFast,pFast先走n步,然后pSlow和pFase一块走,当两者相遇时,即为环的入口处;
3、改进
如果链表存在环,我们无需计算环的长度n,只需在相遇时,让一个指针在相遇点出发,另一个指针在链表首部出发,然后两个指针一次走一步,当它们相遇时,就是环的入口处。(这里就不说明为什么这样做是正确的,大家可以在纸上推导一下公式)
代码
代码1
public class ListNode {
int val;
/**
*保存链表的值
*/
ListNode next;
/**
*下一个结点
*/
ListNode(int x) {
val = x;
next = null;
}
@Override
public String toString(){
return val+"";
}
}
public class hasCycle {
public static void main(String[] args) {
// test1();
//test2();
test3();
}
// 1->2->3->4->5->6 <-+
// | |
// +---+
private static void test3() {
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
//ListNode n6 = new ListNode(6);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n3;
// n6.next = n3;
System.out.println(hasCycle(n1));
}
private static void test2() {
// 1->2->3->4->5->6
// ^ |
// | |
// +--------+
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
ListNode n6 = new ListNode(6);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
n6.next = n3;
System.out.println(hasCycle(n1));
}
//1->2->3->4->5->6
private static void test1() {
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
ListNode n6 = new ListNode(6);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
System.out.println(hasCycle(n1));
}
public static ListNode hasCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode fast = head;
ListNode slow = head;
//首先定义两个指针
while (fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast) {
fast = head;
while(slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}
代码2
/**
* 链表中环的入口节点
*
* 思路:
* 1.判断是否存在环,并找到快慢两个指针相遇的位置
* 2.根据找到的这个相遇位置,统计环中节点的数目n,先让快指针走n步,然后快慢两个指针一起运动,快慢指针相遇时的节点就是环的入口节点
*/
public class EnterNodeInLink {
public ListNode getEnterNode(ListNode head){
//参数校验
if(head == null){
return null;
}
//如果有环,第一个和第二个指针在环中相遇时的节点
ListNode meetingNode = meetingNode(head);
int ringLength = 0; //环的长度
if(meetingNode != null){ //如果存在环,就求出环的长度
ListNode tempNode = meetingNode;
meetingNode = meetingNode.next;
while(meetingNode != tempNode){
ringLength++;
meetingNode = meetingNode.next;
}
ringLength++;
}else{ //如果不存在环,就返回null
return null;
}
ListNode ahead = head; //第一个指针
ListNode behind = head; //第二个指针
while(ringLength > 0){
ahead = ahead.next; //第一个指针先在链表上向前移动ringLength步
ringLength--;
}
while(ahead != behind){
ahead = ahead.next;
behind = behind.next;
}
return behind;
}
//在链表存在环的情况下,找到一快一慢两个指针相遇的节点
public ListNode meetingNode(ListNode head){
//参数校验
if(head == null){
return null;
}
ListNode behind = head.next; //后面的指针
//如果只有一个节点直接返回null
if(behind == null){
return null;
}
ListNode ahead = behind.next; //前面的指针
while(behind != null && ahead != null){
if(behind == ahead){
return ahead;
}
//behind指针在链表上移动一步
behind = behind.next;
//ahead指针在链表上移动两步
ahead = ahead.next;
if(ahead != null){
ahead = ahead.next;
}
}
return null;
}
public static void main(String[] args) {
EnterNodeInLink test = new EnterNodeInLink();
ListNode head = new ListNode();
ListNode temp1 = new ListNode();
ListNode temp2 = new ListNode();
ListNode temp3 = new ListNode();
ListNode temp4 = new ListNode();
ListNode temp5 = new ListNode();
head.value=1;
temp1.value=2;
temp2.value=3;
temp3.value=4;
temp4.value=5;
temp5.value=6;
head.next=temp1;
temp1.next=temp2;
temp2.next=temp3;
temp3.next=temp4;
temp4.next=temp5;
temp5.next=null;
ListNode resultNode = test.getEnterNode(head);
if(resultNode != null){
System.out.println(resultNode.value);
}else{
System.out.println("您输入的参数有误!");
}
}
}
class ListNode{
int value;
ListNode next;
}
LeetCode
环形链表
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
代码
public boolean hasCycle(ListNode head) {
Set<ListNode> nodesSeen = new HashSet<>();
while (head != null) {
if (nodesSeen.contains(head)) {
return true;
} else {
nodesSeen.add(head);
}
head = head.next;
}
return false;
}
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow=head,fast=head;//双指针 :快慢指针
while (fast!=null && fast.next!=null)
{
slow = slow.next;
fast = fast.next.next;
if(slow==fast)
return true;
}
return false;
}
}
环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。
链表的中间结点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
提示:
给定链表的结点数介于 1 和 100 之间。
public ListNode middleNode(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode first = head;
ListNode second = head;
while (second != null && second.next != null){
first = head.next;
second = head.next.next;
}
return first;
}