单链表的节点只有一个指针指向下一个节点,两个单链表相交的话就会导致两个单链表的尾节点是相同的,所以只需要比较尾节点是否相同就可以知道两个单链表是否相交。但是,这样是否就完全没有问题了呢?
环!
其实不然,单链表有可能没有尾节点!为什么?因为有可能存在环!如果存在环的话上面那个办法就行不通了!考虑到因为环的存在,原来单链表的尾节点消失了,既然因为环没有尾节点了,那我们就创造一个尾节点!
假设两个单链表分别为A和B,第一步,遍历A,使用两个指针,一个指针每一步移动一个节点,另一个指针每一步移动两个节点(当然这里的一个节点和两个节点可以替换为别的数目,只要保证两个数的最大公约数为1就可以了,因为这个可以保证两个指针在环中一定会相遇)。使用两个指针遍历链表A,如果某一步两个指针相等,那么说明存在环,就将这一点作为A的尾节点,如果不存在环,我们最终也能获得A的尾节点。第二步,遍历链表B,并将每一个节点和A的尾节点相比较,如果存在相同的节点,那么说明两个链表相交,否则不相交。使用这种方法我们最终能在O(n+m)(n为A的长度,m为B的长度)的时间复杂度内解决问题。
环的长度怎么求?
如果知道链表A有环,那么怎么求出A的环的长度呢?这个问题其实非常简单!上文中已经描述了找到链表A的环中的一个节点,也就是那个尾节点,只要绕环一周便可以计算出环的 长度。
环的第一个节点?
如果链表有环,怎么求环的第一个节点呢?我在查阅资料的时候发现有这样一个定理:对于有环的单链表A,使用一个每次走一个节点的指针P和一个每次走两个节点的指针K遍历链表A,当指针P和K第一次碰撞时,此时用一个每次走一个节点的指针H从单链表头开始遍历,当指针P和H相遇时一定位于环的第一个节点!使用数学方法可以很容易来证明这个定理。设链表的节点为o1,o2,o3,……,oN,i1,i2,……,iK,……,iM其中o1是链表的第一个节点,oN是环的第一个节点,iK是指针P和K发生碰撞的节点(指针P和K发生碰撞的节点是唯一的),iM的next指针指向oN。当指针P和K第一次相遇时,此时指针P走过了N+K个节点,而指针K走过了2N+2K个节点,可以得到这样一个等式:N+K = N+((N+2K) mod (M+1)),化简可得:0 = (N+K) mod (M+1)!这个化简后的等式说明,指针P走过N个节点会处于oN节点,而指针H开始指向链表头,它走过N个节点也会位于oN节点,虽然不知到N究竟是多少,但是指针P和H同步走一定会相交于oN,而且相交的第一个位置就是oN。
链表的长度怎么求?
有了上一段的分析,链表的长度就容易得到了!有环的链表,其长度可以分为两部分来求,一部分是环外,一部是环!如果将无环链表看作是环长度为0的链表,则可以得到一个统一的求解链表长度的代码,见行93到125,其中,行99到106计算得到iK,行107到115计算环的长度,行116到122计算环外节点数目。
两个单链表的交点怎么求?
如果单链表不存在环,求解交点有两种比较好的方法,第一种方法是将链表尾节点指向其中一个链表的头节点,这样求交点的问题就转换成了求环的第一个节点问题,第二种方法效率更高一点,首先求出链表A和B的长度LA和LB,假设LA大于LB,将每次移动一个节点的指针PA指向链表A的第LA-LB+1个节点,将每次移动一个节点的指针PB指向链表B的第一个节点,然后同步移动指针PA和PB,两个指针相同时所指向的节点便是A和B的交点。如果单链表存在环的话,求解交点就麻烦一点,这里提一个比较简单的办法,对于链表A/B,取环上的第一个节点,将该节点指向链表B/A的头节点,这样得到一个新的有环链表,分别求解环的第一个节点,可以得到两个节点,如果两个节点相同,说明链表A和B的交点在环外,否则这两个链表在环外没有交点,这两个节点分别是链表A和B在环上的第一个节点!
下面贴上相关代码!
#include <iostream>
using namespace std;
struct list {
int value;
struct list *next;
};
struct list *insert(struct list *head, int v)
{
if (head) {
head->next = insert(head->next, v);
} else {
head = new struct list;
head->value = v;
head->next = 0;
}
return head;
}
void set_next_node(struct list *node, struct list *next)
{
if (node) {
node->next = next;
}
}
struct list *find(struct list *head, size_t idx)
{
while (idx-- && head && head->next) {
head = head->next;
}
return head;
}
struct list *first_node(struct list *head1, struct list *head2)
{
struct list *tail1, *next1 = 0;
struct list *cursor1 = head1, *cursor2 = head1;
do {
if (cursor2) {
cursor2 = cursor2->next;
if (cursor2)
cursor2 = cursor2->next;
}
tail1 = cursor1;
next1 = cursor1->next;
cursor1 = cursor1->next;
} while (cursor1 != cursor2);
cursor2 = head1;
do {
tail1 = cursor1;
next1 = cursor1->next;
cursor2 = cursor2->next;
if (cursor1)
cursor1 = cursor1->next;
} while (cursor1 != cursor2);
if (cursor1) {
tail1 = cursor1;
next1 = cursor1->next;
}
tail1->next = head2;
cursor1 = head1, cursor2 = head1;
do {
if (cursor2) {
cursor2 = cursor2->next;
if (cursor2)
cursor2 = cursor2->next;
}
cursor1 = cursor1->next;
} while (cursor1 != cursor2);
do {
if (cursor2) {
cursor2 = cursor2->next->next;
}
if (cursor1) {
cursor1 = cursor1->next;
}
} while (cursor1 != cursor2);
cursor2 = head1;
do {
cursor2 = cursor2->next;
if (cursor1)
cursor1 = cursor1->next;
} while (cursor1 != cursor2);
tail1->next = next1;
return cursor1;
}
size_t length(struct list *head)
{
size_t l = 0;
if (head) {
struct list *cursor1 = head;
struct list *cursor2 = head;
do {
if (cursor2) {
cursor2 = cursor2->next;
if (cursor2)
cursor2 = cursor2->next;
}
cursor1 = cursor1->next;
} while (cursor1 != cursor2);
do {
if (cursor2) {
cursor2 = cursor2->next->next;
}
if (cursor1) {
l++;
cursor1 = cursor1->next;
}
} while (cursor1 != cursor2);
cursor2 = head;
do {
cursor2 = cursor2->next;
if (cursor1)
cursor1 = cursor1->next;
l++;
} while (cursor1 != cursor2);
}
return l;
}
void print_length(struct list *head, size_t l)
{
cout << "list: ";
while (head && l--) {
cout << head->value;
if (head->next) {
if (l)
cout << "->";
else
cout << "->[" << head->next->value <<"]";
}
head = head->next;
}
cout << endl;
}
void print(struct list *head)
{
print_length(head, length(head));
}
int main()
{
struct list *head1 = 0;
struct list *head2 = 0;
head1 = insert(head1, 0);
head1 = insert(head1, 1);
head1 = insert(head1, 2);
head1 = insert(head1, 3);
head1 = insert(head1, 4);
head1 = insert(head1, 5);
head1 = insert(head1, 6);
head1 = insert(head1, 7);
head1 = insert(head1, 8);
head1 = insert(head1, 9);
head2 = insert(head2, 10);
head2 = insert(head2, 11);
head2 = insert(head2, 12);
head2 = insert(head2, 13);
set_next_node(find(head1, -1), find(head1, 4));
set_next_node(find(head2, -1), find(head1, 6));
print(head1);
print(head2);
cout << length(head1) << endl;
cout << length(head2) << endl;
cout << "node: " << first_node(head1, head2)->value << endl;
cout << "node: " << first_node(head2, head1)->value << endl;
return 0;
}