1.单链表是否有环及求环入口
(1)判断单链表是否有环
方法很简单,设置两个指针,如slow和fast,都指向头结点,slow每次走一步,fast每次走两步,如果slow和fast相遇,则说明链表是有环的
这样设置两个指针的方法在一些链表问题上很有帮助的。
例如:a.如何在仅遍历链表一遍的情况下求无环链表的中心节点,即最中间的那个节点。这时就是设置快慢两个指针,快指针每次走两步,慢指针每次走一步,当快指针走到链表尾节点时,慢指针也就走到中间节点了。
b.求无环链表的倒数第k个节点。同理,设置两个指针,快指针先走k-1步,然后两个指针同时走,这样,当快指针走到尾节点时,慢指针的位置刚好就是倒数第K个节点。
判断单链表是否有环的代码:
bool is_exit_loop(ListNode * head)
{
//空指针的情况
if(head == NULL)
return false;
ListNode *slow = head, *fast = head;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
break;
}
if(fast != NULL && fast->next != NULL)
return true;
else
return false;
}
求无环链表中心节点的代码:
ListNode * find_mid_node(ListNode * head)
{
//头结点为空
if(head == NULL)
return NULL;
//只有一个节点的情况,中心节点即为头结点
if(head->next == NULL)
return head;
ListNode * one_step = head, *two_step = head;
while(two_step != NULL && two_step->next != NULL)
{
one_step = one_step->next;
two_step = two_step->next->next;
}
//当节点的个数为偶数个,中心节点有两个,这里输出较后的那个
return one_step;
}
求无环链表倒数第k个节点的代码:
ListNode * find_Kth_to_tail(ListNode * head, unsigned int k)
{
if(head == NULL || k == 0)
return NULL;
//这里要注意一个问题就是,链表的长度小于k
ListNode * slow = head, * fast = head;
unsigned int step = 1;
while(step < k )
{
//链表长度小于K的情况
if(fast->next == NULL)
return NULL;
step ++;
fast = fast->next;
}
if(fast->next == NULL) //链表长度刚好为K
return slow;
else
{
while(fast->next != NULL)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
(2)找有环链表的入口。
设置连个指针,slow和fast,slow每次走一步,fast每次走两步。定义一些变量:链表环长度为r,链表头结点到环的入口距离为a,环的入口到两指针相遇点的距离为b
假设fast走了k步后,指针slow和fast相遇了。
我们可以得出下列关系式: k = 2 * k - n * r(n >= 1),即 k = n * r,
a + b = k;
即n * r = a + b => a = (n - 1) * r + r - b;
r - b为指针相遇点到环入口的距离,即从链表头走到环入口等于从环内(n-1)次循环+从相遇点走到环入口。
所以,得到相遇点后,然后设置两个指针,一个从头结点出发,一个相遇点出发,每次都走一步,则两指针第一次相遇的地方就是环的入口。
代码如下:
ListNode * find_loop_entry(ListNode * head)
{
//这里默认链表已经有环,所以就进行链表是否有环的判断
if(head == NULL)
return NULL;
ListNode * slow = head, * fast = head;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
//找到相遇点
if(slow == fast)
break;
}
//找到相遇点后,让slow节点从头结点出发,fast节点从相遇点出发,二者第一次相遇的地方即为链表的环入口
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
2.两个链表是否相交的问题
判断方法:先判断两个链表是否有环(1)若一个有环,一个无环,则两链表肯定是不相交的
(2)若两个都是无环链表,则很容易,判断方法比较多,
方法1:暴力破解,设两个链表分别为链表1和链表2,依次判断链表2的每个节点是否在链表1中
方法2:分别遍历两个链表,求出每个的尾节点,若二者的尾节点相同,则说明两个链表是相交的,否则不相交
方法3:将链表2接在链表1的后面,然后判断链表1是否有环,若有环的话,则两链表相交,否则两链表不相交
(3)若两个链表都是有环链表,方法如下:如果两个链表相交,那么一个链表环的入口一定在另一个链表上,得到一个链表的环入口,然后对另一个链表遍历,依次判断该环入口是否在另一个链表上。
代码如下:
bool if_two_linklist_intersect(ListNode * head1, ListNode * head2)
{
//假设已知两链表都有环,所以这里不再进行是否都有环的判断
if(head1 == NULL || head2 == NULL)
return false;
ListNode * list1_loop_entry = find_loop_entry(head1); //得到链表1的环入口
ListNode * list2_loop_entry = find_loop_entry(head2); //得到链表2的环入口
ListNode * start = head2;
//对链表2遍历,当start节点第二次到达list2_loop_entry时,表示已经完成对链表2的遍历
int times = 0; //表示start节点经过list2_loop_entry节点的次数
while(times < 2)
{
if(list1_loop_entry == start)
break;
if(list2_loop_entry == start)
times ++;
start = start->next;
}
if(times == 2) //如果start完成了对链表2的遍历,则表示两链表不相交
return false;
else
return true;
}
这里对于两个链表的公共交点问题,因为若两链表相交的话,公共交点可能不止一个。