链表中环的入口节点

问题描述

如果一个链表中包含环,如何找到环的入口节点?
例如:1–>2–>3–>4–>5–>6(–>3),在6后并没有结束,其next指向了中间的3,形成了一个环,所以环的入口节点是节点3。

思路

首先,确定一个链表里是否有环。由前面的题目启发,可以设置两个指针,一个快一个慢,然后当快的遇到慢的说明链表有环,如果慢的指针走到链表结尾还没有遇到快的指针,则不包含环。
第二步,找到环的入口。依然可以用两个指针,如果环中包含N个节点,那么第一个指针先向前移动N步,然后两个指针以相同的速度移动,如果第二个指针移动到环的入口节点时,第一个指针已经围绕环走了一圈,又回到环的入口,两指针相遇。
最后,统计环中节点的数目。第一个指针走到环的入口时,开始计数,当两个指针相遇时,得到的数就是环的节点数.

using System;

namespace 链表中环的入口节点
{
    public class ListNode
    {
        public int val;
        public ListNode next;
        public ListNode(int x)
        {
            val = x;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            ListNode list1 = CreateLoopList(null);
            PrintList(list1);
            Console.WriteLine("\n上述链表中环入口节点为:"+EntryNodeOfLoop(list1).val);

            ListNode list2 = CreateNoLoopList(null);
            PrintList(list2);
        }

        /// <summary>
        /// 找到链表环的入口节点
        /// </summary>
        /// <param name="pHead">链表的头节点</param>
        /// <returns>环的入口节点</returns>
        public static ListNode EntryNodeOfLoop(ListNode pHead)
        {
            if (pHead == null)
                return null;

            ListNode meetingNode = MeetingNode(pHead);
            if (meetingNode == null)
                return null;

            //得到环中节点数目
            int nodeInLoop = 1;
            //快慢指针相遇的节点位置
            ListNode pNode1 = meetingNode;  
            while (pNode1.next != meetingNode)
            {
                pNode1 = pNode1.next;
                nodeInLoop++;
            }

            //移动第一个指针,次数为环中节点数目
            pNode1 = pHead;
            for (int i = 0; i < nodeInLoop; i++)
            {
                pNode1 = pNode1.next;
            }

            //同时移动两个指针
            ListNode pNode2 = pHead;
            while (pNode1 != pNode2)
            {
                pNode1 = pNode1.next;
                pNode2 = pNode2.next;
            }

            return pNode1;
        }

        /// <summary>
        /// 在链表存在环的情况下找到一快一慢指针相遇的节点
        /// </summary>
        /// <param name="pHead">链表的头节点</param>
        /// <returns>相遇的节点</returns>
        private static ListNode MeetingNode(ListNode pHead)
        {
            if (pHead == null)
                return null;

            //慢指针,一次只走一步
            ListNode firstNode = pHead.next;
            if (firstNode == null)
                return null;

            //快指针,一次走两步
            ListNode secondNode = firstNode.next;

            while (firstNode != null && secondNode != null)
            {
                if (firstNode == secondNode)
                    return secondNode;

                firstNode = firstNode.next;
                secondNode = secondNode.next;
                if (secondNode != null)
                    secondNode = secondNode.next;
            }
            return null;
        }

        /// <summary>
        /// 创建一个有环的链表
        /// </summary>
        /// <param name="pHead">链表的头节点</param>
        /// <returns>链表的头节点</returns>
        private static ListNode CreateLoopList(ListNode pHead)
        {
            ListNode list = null;
            ListNode loopNode = null;           //环入口
            for (int i = 1; i < 7; i++)
            {
                ListNode temp = new ListNode(i) { next = null };
                //设置第三个节点为环入口节点
                if (i == 3)
                    loopNode = temp;
                if (list == null)
                {
                    pHead = temp;
                    list = temp;
                }
                else
                {
                    list.next = temp;
                    list = temp;
                }
            }
            //让链表尾节点指向环入口节点
            list.next = loopNode;
            return pHead;
        }

        /// <summary>
        /// 创建一个无环的链表
        /// </summary>
        /// <param name="pHead">链表的头节点</param>
        /// <returns>链表的头节点</returns>
        private static ListNode CreateNoLoopList(ListNode pHead)
        {
            ListNode list = new ListNode(1) { next = null };
            if (pHead == null)
                pHead = list;
            else
                pHead.next = list;

            for (int i = 2; i < 7; i++)
            {
                ListNode temp = new ListNode(i) { next = null };
                list.next = temp;
                list = temp;
            }

            return pHead;
        }

        /// <summary>
        /// 打印链表
        /// </summary>
        /// <param name="pHead">链表的头节点</param>
        private static void PrintList(ListNode pHead)
        {
            if (pHead == null)
            {
                Console.WriteLine("null");
                return;
            }
            ListNode list = pHead;
            bool isLoop = false;
            ListNode meetingNode = MeetingNode(pHead);      //找到链表的相遇节点,如果节点不为空,说明有环,令isLoop为真
            if (meetingNode != null)
                isLoop = true;
            int count = 0;
            //如果链表有环,输出二十次,否则,输出完一次链表则结束(测试中链表节点数永远小于10,所以设置20可以查看到循环的环节点)
            if (isLoop)
            {
                if (pHead == null)
                    return;
                Console.Write(pHead.val + "-->");

                ListNode pNode1 = pHead.next;
                if (pNode1 == null)
                    return;

                ListNode pNode2 = pNode1.next;
                while (count < 20)
                {
                    count++;
                    Console.Write(pNode1.val + "-->");
                    pNode1 = pNode1.next;
                }
            }
            else
            {
                while (list != null)
                {
                    Console.Write(list.val + "-->");
                    list = list.next;
                }
            }
            Console.WriteLine();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值