编程之美3.6-判断两链表是否相交

1、判断链表中是否有环
用追赶法:设定两个指针一个快指针fast指针,一个慢指针slow指针。从链表头结点开始遍历链表,fast指针的步长为2,slow指针的步长为1。
如果链表中存在环,fast指针和slow指针一定能够相遇。如果链表中没有环,则fast指针先到达链表尾部。
这里写图片描述
2、如果链表中有环,找出进入环的第一个结点
如果链表中有环:在slow和fast相遇时,假设slow指针遍历的结点个数(总步长)为s,则fast指针的总步长为2s。假设环的长度为r。则 2s = s + nr。也就是说在他们第一次相遇时,fast已经转了n圈了。
2s = s + nr 则 s = nr

水平部分链表长度为a,红色部分长度为b,剩余部分为c。则
s = a + b, r = b + c
s = nr = (n - 1)r + r
a + b = (n - 1)r + b + c
a = (n - 1)r + c
那,最后一个式子说明,在slow指针和fast指针相遇后,一个指针从head开始,另一个指针从从相遇点开始遍历,步长均为1。他们再次相遇时,所指向上的结点即为第一个进入环的结点。

3、判断两无环链表是否相交
算法1:
如果两个链表list1和list2无环,那么先使得一个链表list1的尾节点的指针指向head结点,此时list1构成环。那么从list2的头结点开始遍历,判断list2中是否有环。如果两链表相交,则list2中一定有环,如果不相交,怎list2中没有环。
算法2
如果两个没环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的。那么最后一个节点一定也是共有的。
我们先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较。如果相同,则相交,否则,不相交。它的时间复杂度为O(Length(h1) + Length(h2))。
4、找出两个无环链表相交的第一个节点
采用3中的算法1,在结合2
5、如果链表上有环,怎么判断两链表是否相交
还是用追赶法,从两链表的头节点开始,一个快指针,一个慢指针。如果有环,它们一定会相遇。
6、如果两链表上有环,且它们相交,怎么判断两链表相交的第一个节点
使用5方法判读两链表相交,把此时相遇的节点看作尾节点。那么问题就转化为2中问题。

#include <iostream>
using namespace std;

//链表节点结构
struct Node{
    int data;
    Node* next;
};

//链表
struct LinkList{
    Node *head;
};

//初始化一个链表
void initLinkList(LinkList *list){
    list->head = NULL;
}

//判断链表是否为空
bool isEmpty(LinkList *list){
    if(list -> head == NULL){
        return true;
    }
    else 
        return false;
}
//为链表插入新的节点
void addNode(LinkList *list, int data){
    Node *tmp = new Node;
    tmp->data = data;
    tmp->next = NULL;
    if(list->head == NULL){
        list->head = tmp;
        return;
    }

    Node *p = list->head;
    while(p->next != NULL){
        p = p->next;
    }
    p->next = tmp;
    return;
}

//判断一个链表中是否有环路
bool hasCircle1(LinkList *list){
    //如果链表为空,返回false
    if(isEmpty(list)){
        return false;
    }
    //判断链表中是否有环的方法:定义两个指针,一个fast,一个slow.
    //fast指针每次走两步,slow指针每次走一步,如果链表中有环,在某一时刻,fast指针一定能和slow指针相遇
    Node* fast;
    Node* slow;
    fast = slow = list->head;
    while(fast && fast->next){//如果链表中无环,fast指针一定会先到达链表的尾部
        fast = fast -> next -> next;
        slow = slow -> next;
        if(fast == slow){
            return true;
        }
    }
    return false;
}

//判断一个链表中是否有环,并返回指向第一个进入环的节点的指针
Node* hasCircle2(LinkList *list){
    if(list->head == NULL){
        return NULL;
    }
    Node *fast;
    Node *slow;
    fast = list->head;
    slow = list->head;

    while(fast && fast->next){
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow){//链表中有环
            slow = list->head;
            while(slow != fast){
                slow = slow->next;
                fast = fast->next;
            }
            return fast;
        }
    }
    return NULL;//无环是返回空指针
}

//判断两无环链表是否相交
bool isCrossed(LinkList* list1, LinkList* list2){
    if(isEmpty(list1) || isEmpty(list2)){
        return false;
    }
    //两个链表无环
    if(!hasCircle1(list1) && !hasCircle1(list2)){
        //首先设置将list2变为一个环
        Node *pNode = list2->head;
        while(pNode->next != NULL){
            pNode = pNode -> next;
        }
        pNode->next = list2->head;

        //判断list1中是否有环,如果list1中有环,则list1和list2相交
        if(hasCircle1(list1)){
            pNode->next = NULL;//将list2恢复到原先的无环状态
            return true;
        }
        else
            return false;
    }
}

int main(){
    int arr1[5] = {1, 3, 5, 7, 9};
    int arr2[5] = {2, 4};

    //list1链表存储arr1数组中的元素
    LinkList *list1 = new LinkList;
    LinkList *list2 = new LinkList;
    initLinkList(list1);
    initLinkList(list2);
    for(int i = 0; i < 5; i++){
        addNode(list1, arr1[i]);
    }
    for(int i = 0; i < 2; i++){
        addNode(list2, arr2[i]);
    }
    /*实验一:为链表list1添加环,使最后一个节点的next指针指向5所在的节点,构成环
    Node *tmp1;
    Node *tmp2;
    for(tmp2 = list1->head; tmp2->next != NULL; tmp2=tmp2->next){
        if(tmp2 -> data == 5){
            tmp1 = tmp2;
        }
    }
    tmp2->next = tmp1;

    Node *yes = hasCircle2(list1);
    cout << yes->data << endl;*/

    /*实验二:使得list1和list2相交
    Node *tmp1;
    Node *tmp2;
    tmp2 = list2->head;
    tmp1 = list1->head;
    while(tmp2->next != NULL)
        tmp2 = tmp2->next;
    while(tmp1 && (tmp1->data != 5))
        tmp1 = tmp1->next;
    tmp2->next = tmp1;
    cout << isCrossed(list1, list2) << endl;*/



    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值