数据结构之单链表常见面试题(二)

本文介绍如何判断两个链表是否相交并找出交点,探讨链表可能带环的情况,展示如何求两个已排序链表中的相同数据,以及提出两种复杂链表复制的有效方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

判断两个链表是否相交,若相交返回其交点位置(两链表都不带环)

解析:两个不带环的链表要能相交只能是Y字型相交,如下图。
这里写图片描述

所以判断相交只需要分别让两个链表都走到结束时,判断他们的data值是否相等。

//实现

LinkNode* HasCross(LinkNode* head1, LinkNode* head2)//判断两个链表是否相交
{
    if(head1 == NULL)
    {
        return NULL;
    }
    if(head2 == NULL)
    {
        return NULL;
    }

    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    size_t len1 = 0;
    size_t len2 = 0;

    while(cur1->Next != NULL)
    {
        cur1 = cur1->Next;
        len1++;
    }
    while(cur2->Next != NULL)
    {
        cur2 = cur2->Next;
        len2++;
    }

    if(cur1->data != cur2->data)
    {
        return NULL;
    }

    size_t len = 0;

    cur1 = head1;
    cur2 = head2;

    if(len1 > len2)
    {
        len = len1-len2;
        while(len--)
        {
            cur1 = cur1->Next;
        }
        while(cur1->data != cur2->data)
        {
            cur1 = cur1->Next;
            cur2 = cur2->Next;
        }
        return cur1;
    }
    else
    {
        len = len2-len1;
        while(len--)
        {
            cur2 = cur2->Next;
        }
        while(cur1->data != cur2->data)
        {
            cur1 = cur1->Next;
            cur2 = cur2->Next;
        }
        return cur2;
    }
}

判断两个链表是否相交,可能带环

两个链表总共有三种情况,两个都不带环、都带环、一个带环一个不带环,那就要分开讨论。如果都不带环就是我们刚才所写的Y字相交法,如果一个带环一个不带环那么是不可能相交的。着重讨论一下两个都带环的。
这里写图片描述

这里写图片描述

由此可见,若两个链表都带环且相交将有三种情况需要讨论,逐步进行。

//实现
LinkNode* HasCross2(LinkNode* head1, LinkNode* head2)//判断两个链表是否相交,可能带环
{
    if(head1 == NULL)
    {
        return NULL;
    }
    if(head2 == NULL)
    {
        return NULL;
    }

    LinkNode* ret1 = GetCycleEntry(head1);//调用之前的函数查看是否带环
    LinkNode* ret2 = GetCycleEntry(head2);

    if(ret1 == NULL && ret2 == NULL)
    {
        HasCross(head1,head2);//两个都不带环
    }
    else if((ret1 == NULL && ret2 != NULL) || (ret2 == NULL && ret1 != NULL))//一个不带环一个带环不可能相交
    {
        return NULL;
    }
    else
    {
        if(ret1->data == ret2->data)
        {
            //交点在环外
           size_t len1 = 0; 
           size_t len2 = 0; 

           LinkNode* tmp1 = head1;
           LinkNode* tmp2 = head2;
           while(tmp1 != ret1)
           {
               len1++;
               tmp1 = tmp1->Next;
           }
           while(tmp2 != ret1)
           {
               len2++;
               tmp2 = tmp2->Next;
           }

           size_t len = 0;
           tmp1 = head1;
           tmp2 = head2;

           if(len1 > len2)
           {
               len = len1-len2;
               while(len--)
               {
                   tmp1 = tmp1->Next;
               }
               while(tmp1->data != tmp2->data)
               {
                   tmp1 = tmp1->Next;
                   tmp2 = tmp2->Next;
               }
               return tmp1;
           }
           else
           {
               len = len2-len1;
               while(len--)
               {
                   tmp2 = tmp2->Next;
               }
               while(tmp1->data != tmp2->data)
               {
                   tmp1 = tmp1->Next;
                   tmp2 = tmp2->Next;
               }
               return tmp2;
           }
        }
        else//交点在环内
        {
            LinkNode* cur1 = ret1;
            while(cur1 != ret2)
            {
                if(cur1->Next == ret1)
                {
                    return NULL;//遍历一圈发现没有相同的,那么就没有相交
                }
                cur1 = cur1->Next;
            }
            return ret2;
        }
    }
}

求两个已排序链表中的相同数据

两个已排序链表当中,如果其中一个链表遍历结束,那么另外一个就没有遍历的必要了,因为必定没有相同的元素,将两个链表相同的元素插入一个新的链表即可。

//实现

LinkNode* UnionSet(LinkNode* head1, LinkNode* head2)//求两个已排序链表中的相同数据
{   
    if(head1 == NULL)
    {
        return NULL;
    }
    if(head2 == NULL)
    {
        return NULL;
    }
    LinkNode* new_head = NULL;
    LinkNode* tail = NULL;
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;

    while(cur1 != NULL && cur2 != NULL)
    {
        if(cur1->data < cur2->data)
        {
            cur1 = cur1->Next;
        }
        else if(cur1->data > cur2->data)
        {
            cur2 = cur2->Next;
        }
        else 
        {
            if(new_head == NULL)
            {
                new_head = Creat(cur1->data);
                tail = new_head;
            }
            else
            {
                tail->Next = Creat(cur1->data);
                tail = tail->Next;
            }
            cur1 = cur1->Next;
            cur2 = cur2->Next;
        }
    }
    return new_head;
}

这个较为简单,所以不再赘述。


复杂链表的复制问题

首先说说什么是复杂链表,复杂链表就是链表的每个节点内的元素多一个random指针,这个指针可以指向任意位置,可以指向链表内其他元素,也可以指向NULL,所以在对其进行复制的时候需要注意对random的复制。

对复杂链表的复制这里有两个思路可以进行。

首先先定义一个新的节点

#define Comtype char//节点内元素类型

typedef struct ComplexNode{
    Comtype data;
    struct ComplexNode* next;
    struct ComplexNode* random;
}ComplexNode;//定义一个新类型的复杂单链表节点

接着,再封装一些基本函数来确保后面操作的执行。

//新节点的创建

ComplexNode* Creat1(Comtype value)//创建一个新的节点
{
    ComplexNode* new_Node = (ComplexNode*)malloc(sizeof(ComplexNode));
    if( new_Node == NULL)
    {
        printf("申请失败\n");
        return NULL;
    }
    new_Node->data = value;
    new_Node->next = NULL;
    new_Node->random = NULL;
    return new_Node;
}

//对复杂链表的打印,即要打印data,还有打印random所指向的内容

void ComPrint(ComplexNode* head)
{
    if(head == NULL)
    {
        return;
    }

    ComplexNode* cur = head;

    for( ; cur!=NULL; cur = cur->next)
    {
        printf("[%c] ",cur->data);
    }

    printf("\n");

    cur = head;

    for( ; cur!=NULL; cur = cur->next)
    {
        if(cur->random == NULL)
        {
            printf("[%c]->%p ",cur->data,cur->random);
            continue;
        }
        printf("[%c]->[%c] ",cur->data,cur->random->data);
    }
    printf("\n");
}

思路一

将链表内的每个元素的random的指向利用其偏移量的方式进行保存,偏移量是random指向的元素相对于头指针的偏移距离。如下图。
这里写图片描述
然后再进行复制新链表的data内容,复制完毕以后根据偏移量在进行random的复制。

//实现

void OffSet(ComplexNode* head, ComplexNode* new_head, ComplexNode* pos, ComplexNode* tail)//计算偏移量,并且根据复制其偏移量指向
{
    if(head == NULL)
    {
        return;
    }
    if(pos == NULL)
    {
        return;
    }
    if(pos->random == NULL)
    {
        tail->random = NULL;
        return;
    }

    ComplexNode* cur = head;
    ComplexNode* tmp = new_head;

    while(cur->data != pos->random->data)
    {
        cur = cur->next;
        tmp = tmp->next;
    }
    tail->random = tmp;
}

ComplexNode* CopyComplex(ComplexNode* head) //复杂链表的复制
{
    if(head == NULL)
    {
        return NULL;
    }
    ComplexNode* new_head = NULL;
    ComplexNode* tail = NULL;
    ComplexNode* cur = head;

    while(cur != NULL)
    {
        if(new_head == NULL)
        {
            new_head = Creat1(cur->data);
            tail = new_head;
        }
        else
        {
            tail->next = Creat1(cur->data);
            tail = tail->next;
        }
        cur = cur->next;
    }
    cur = head;
    tail = new_head;

    while(cur != NULL)
    {
        OffSet(head, new_head, cur, tail);
        cur = cur->next;
        tail = tail->next;
    }
    return new_head;
}


思路二

利用重复插入的方法,将每一个节点的后面再次插入一个与其相同的节点,且random的指向也是相同的,最后再将重复的拆下即可。如下图。
这里写图片描述

//实现

void  ComPush(ComplexNode* head, ComplexNode* cur, Comtype value)//在每个元素后插入一个相同的元素
{
    if(head == NULL)
    {
        return;
    }
    ComplexNode* tmp = cur->next;
    cur->next = Creat1(value);
    cur->next->next = tmp;
}

ComplexNode* CopyComplex2(ComplexNode* head) //复杂链表的复制
{
    if(head == NULL)
    {
        return NULL;
    }

    ComplexNode* cur = head;

    while(cur != NULL)
    {
        ComPush(head,cur,cur->data);
        cur = cur->next->next;
    }

    cur = head;
    ComplexNode* tmp = cur->next;

    while(1)
    {
        if(cur->random == NULL)
        {
            cur = cur->next->next;
            if(cur == NULL)
            {
                break;
            }
            tmp = cur->next;
            continue;
        }
        tmp->random = cur->random->next;
        cur = cur->next->next;
        if(cur == NULL)
        {
            break;
        }
        tmp = cur->next;
    }

    cur = head;
    tmp = cur->next;
    ComplexNode* new_head = NULL;
    ComplexNode* tail = NULL;

    while(1)
    {
        if(new_head == NULL)
        {
            new_head = tmp;
            cur->next = tmp->next;
            tail = new_head;
        }
        else
        {
            tail->next = tmp;
            cur->next = tmp->next;
            tail = tail->next;
        }
        cur = cur->next;
        if(cur == NULL)
        {
            break;
        }
        tmp = cur->next;
    }
    return new_head;
}

测试代码如下:

void TestCom()
{
    HEAD;
    printf("\n");
    printf("\n");
    printf("\n");
    ComplexNode* head;
    ComplexNode* a = Creat1('a');
    ComplexNode* b = Creat1('b');
    ComplexNode* c = Creat1('c');
    ComplexNode* d = Creat1('d');

    head = a;
    a->next = b;
    b->next = c;
    c->next = d;

    b->random = d;
    c->random = a;
    a->random = d;
    d->random = NULL;

    printf("=======================原链表=========================\n");
    ComPrint(head);
    ComplexNode* new_head = CopyComplex(head);
    printf("=======================新链表=========================\n");
    ComPrint(new_head);
}


void TestCom2()
{
    HEAD;
    printf("\n");
    printf("\n");
    printf("\n");
    ComplexNode* head;
    ComplexNode* a = Creat1('a');
    ComplexNode* b = Creat1('b');
    ComplexNode* c = Creat1('c');
    ComplexNode* d = Creat1('d');

    head = a;
    a->next = b;
    b->next = c;
    c->next = d;

    b->random = d;
    c->random = a;
    a->random = d;
    d->random = NULL;

    printf("=======================原链表=========================\n");
    ComPrint(head);
    printf("=======================新链表=========================\n");
    CopyComplex2(head);
    ComPrint(head);
}

void Test1()
{
    TestCom();
    TestCom2();
}

欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值