链表相关笔试面试题

1.判断两个链表是否相交

    两个链表是否相交可分为以下几种情况
    (1)两个链表都不带环,此时两个链表所对应的最后一个节点是相等的
    (2)两个链表一个带环,一个不带环,两个链表一定不相交
    (3)连个链表都带环,如果相交则可以分为以下三种情况
      1)两个链表在环外相交, 此时链表1 和链表2 对应的环的入口地址是相等的
      2)两个链表在换上相交,此时从一个环的入口地址出发,一定可以到达另一个环的入口
      3)如果不是以上两种情况,则连个链表一定不相交

/* * *链表是否相交没有环 * */

int LinkListHasCrossWithCircle(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return 0;
    }
    LinkNode* cur1 = head1;
    while(cur1 -> next != NULL)
    {
        cur1 = cur1 -> next;
    }

    LinkNode* cur2 = head2;
    while(cur2 -> next != NULL)
    {
        cur2 = cur2 -> next;
    }

    if(cur1 == cur2)
    {
        return 1;
    }

    return 0;
}   

/* * * 链表是否相交,不确定有没有环 * */

int LinkListHasCrossWithCircle2(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return 0;//两链表至少一个为空
    }                                                                                                                                                          
    LinkNode* circle1 = LinkListHasCircle(head1);
    LinkNode* circle2 = LinkListHasCircle(head2);
    //两个链表都不带环
    if(circle1 == NULL && circle2 == NULL)
    {
        return LinkListHasCrossWithCircle(head1, head2);
    }
    //两链表都带环
    else if(circle1 != NULL && circle2 != NULL)
    {
        //在环外相交
       LinkNode* entry1 = LinkListGetCircleEntry(head1);
       LinkNode* entry2 = LinkListGetCircleEntry(head2);
       if(entry1 == entry2)
       {
           return 1;
       }
       //再换上相交
       else if(entry1 != entry2)
       {
           LinkNode* cur1 = entry1;
           //从一个环入口点出发可以到达另外一个环入口点,则证明相交
           while(cur1 != entry2)
           {
               cur1 = cur1 -> next;
           }
           return 1;
       }
       //不是上述情况则没有相交
       return 0;
    }
    //一个带环一个不带环,则一定不相交
    else if( ( circle1 == NULL && circle2 != NULL )
        || (circle1 != NULL && circle2 == NULL))
    {
        return 0;
    }
}

                       《链表相关笔试面试题》

2.求链表的交点

    求换的交点和上述判断是否有环是一个思路,可以分为一下几种情况
    (1)两个链表都不带环的情况下,先利用上面的函数判断出连个链表是否相交,再求出两个链表的长度 size1, size2,再将两个链表的长度做差(长的减短的)得到一个大于等于0 的数 offset,再让分别定义两个指针 cur1, cur2, 指向 head1, head2, 然后让长链表对应的 cur1(假定cur1所指的链表长) 先走 offset 步,cur2 不动,cur1 每走一步 offset 就减1, 等到 offset 等于 0 的时候这个时候就说明两个指针cur1, cur2 都相对于交点的距离相等, 这个时候让 cur1, cur2 一起向后走,当它们两个指针所对应的节点相同时, 此时这个相同的节点就是两个链表的交点
    (2)当两个链表一个带环,一个不带环时, 此时链表一定不相交,直接返回空
    (3)当两个链表都有环时,分为一下三种情况
      1)两个链表在环外相交,此时的交点求法如图所示

     《链表相关笔试面试题》
      2)连个链表在环上相交
                         《链表相关笔试面试题》
      3)如果不是以上两种,那么一定不相交

/* * *环的相交点不带环 * */

LinkNode* LinkListCrossPoint(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return NULL;//两链表至少一个为空
    }

    int len1 = LinkListSize(head1);
    int len2 = LinkListSize(head2);

    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    int offset = 0;
    if(len1 > len2)
    {
        offset = len1 - len2;
        for(; offset > 0; cur1 = cur1 -> next)
        {
            offset--;
        }
    }
    else if(len2 > len1)
    {
        offset = len2 - len1;
        for(; offset > 0; cur2 = cur2 -> next)
        {
            offset--;
        }
    }
    for(; cur1 != cur2; cur1 = cur1 -> next, cur2 = cur2 -> next)
    {
        ;
    }
    return cur1;                                                                                                                                               
}

/* * *环的相交点带环不带环不确定 * */

LinkNode* LinkListCrossPoint2(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL || head2 == NULL)                                                                                                                       
    {    
        return NULL;//两链表至少一个为空,一定不相交
    }    

    LinkNode* entry1 = LinkListGetCircleEntry(head1);
    LinkNode* entry2 = LinkListGetCircleEntry(head2);
    //如果两个都不带环,直接用第一个版本
    if(entry1 == NULL && entry2 == NULL)
    {    
        return LinkListCrossPoint(head1, head2);
    }    
    //如果一个带环,一个不带环,一定不相交
    if(( entry1 == NULL && entry2 != NULL ) || ( entry1 != NULL && entry2 == NULL ))
    {    
        return NULL;
    }    
    //如果两个都有环
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    if(entry1 != NULL && entry2 != NULL)
    {    
//环外相交
        if(entry1 == entry2)
        {
            LinkNode* end = entry1;

            int size1 = 0;
            int size2 = 0;
            for(; cur1 != end; cur1 = cur1 -> next)
            {
                size1++;
            }
            size1 = size1 + 1;
            for(; cur2 != end; cur2 = cur2 -> next)
            {
                size2++;
            }
            size2 = size2 + 1;

            int offset =  0;
            if(size1 > size2)
            {
                offset = size1 - size2;                                                                                                                      
                for(cur1 = head1, cur2 = head2; offset > 0; offset--)
                {
                    cur1 = cur1 -> next;
                }
                for(; cur1 != end && cur1 != cur2; cur1 = cur1 -> next, cur2 = cur2 -> next)
        {
                    ;
                }
                return cur1;
            }
            else if(size2 > size1)
            {
                offset = size2 - size1;
                for(cur1 = head1, cur2 = head2; offset > 0; offset--)
                {
                    cur2 = cur2 -> next;
                }
                for(; cur1 != end && cur1 != cur2; cur1 = cur1 -> next, cur2 = cur2 -> next)
                {
                    ;
                }
                return cur1;
            }
            else//size1 = size2
            {
                for(cur1 = head1, cur2 = head2; cur1 != cur2 && cur1 != end && cur2 != end; cur1 = cur1 -> next, cur2 = cur2 -> next)
                {                                                                                                                                            
                    ;
                }
                return cur1;
            }
        }
 //环内相交,相交点就是入口
        else if(entry1 != entry2)
        {
            LinkNode* cur = entry1;
            for(; cur != entry2; cur = cur -> next)
            {
                ;
            }
            return entry2;
            cur = entry2;
            for(; cur != entry1; cur = cur -> next)
            {
                ;
            }
            return entry1;
        }
        //不是上述两种情况,则不相交
        else
        {
            return NULL;
        }                                                                                                                                                    

    }
}

                《链表相关笔试面试题》

3.求连个链表的交集

    求交集就是分别定义两个指针 cur1, cur2,指向两个对应的链表的首元素, 然后将 cur1 和 cur2 所指的链表的 data 进行比较, 如果相等, 将这个结点插入到一个新链表中, 然后两个指针 cur1, cur2, 一起向后走一步,如果不相等, 就将 data 值小的那个指针向后移动, 而另外一个指针不动, 在进行比较,重复以上动作,直到 cur1 或者 cur2 两个中其中一个为空,则停止循环

LinkNode* LinkListUnionSet(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return NULL;//两个链表至少有一个为空
    }
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    LinkNode* new_head = NULL;
    LinkNode* new_tail = NULL;
    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 = LinkNodeCreat(cur1 -> data);
                new_tail = new_head;
            }
            else
            {
                new_tail -> next = LinkNodeCreat(cur1 -> data);
                new_tail = new_tail -> next;
            }
            cur1 = cur1 -> next;                                                                                                                               
            cur2 = cur2 -> next;
        }
    }
    return new_head;
}

《链表相关笔试面试题》

4. 拷贝复杂链表

    先看一下复杂链表的数据结构

typedef struct ComplexNode
{
    LinkNodeType data;
    struct ComplexNode* next;
    struct ComplexNode* random;
}ComplexNode;

    复杂链表中除了 data, next, 之外,还有一个 random, 它可能指向链表中的如何一个节点, 还可能指向空。
    在进行复杂链表的拷贝时, 可以采用下面的方法, 先将复杂链表按照简单链表进行复制,将其复制到一个新链表中, 此时的新链表还是一个简单链表, 然后再遍历旧链表,求出每一个节点所对应的 random 相对于头节点的偏移量, 再遍历新链表, 根据所求得的偏移量确定新链表中的 random 的指针的指向。

/* * * 复杂链表的拷贝 * */                                                                                                                                                            

ComplexNode* ComplexCopy(ComplexNode* head)
{
    if(head == NULL)
    {
        return NULL;//空链表
    }
    ComplexNode* cur = head;
    ComplexNode* new_head = NULL;
    ComplexNode* new_tail = NULL;
    while(cur != NULL)
    {
        if(new_head == NULL)
        {
            new_head = head ;
            new_tail = new_head;
        }
        else
        {
            new_tail -> next = cur;
            new_tail = new_tail -> next;
        }
        cur = cur -> next;
    }
    //求每个random相对于头结点的偏移量
    int offset = 0;
    ComplexNode* new_cur = new_head;
    for(cur = head; cur != NULL; cur = cur -> next,new_cur = new_cur -> next)
    {
        //求出每个结点相对于head的偏移量
        offset = Diff(head, cur -> random);
        //修改新链表中的random的值
        new_cur -> random = Step(new_head, offset);
    }
    return new_head;
}

/* * * 通过offset找到random * */

ComplexNode* Step(ComplexNode* head, int offset)
{
    if(head == NULL)
    {    
        return NULL;//空链表
    }    
    if(offset == -1)
    {    
        return NULL;
    }    
    ComplexNode* cur = head;
    ComplexNode* random = head;
    //让 random 走 offset 步,再将 cur -> random = random
    for(; offset > 0; offset --)
    {    
        random = random -> next;
    }    
    return random; 
}


/* * * 求出每个结点相对于head的偏移量 * */

int Diff(ComplexNode* head, ComplexNode* random)
{
    if(head == NULL)
    {
        return -2;
    }
    if(random == NULL)
    {
        return -1;
    }
    ComplexNode* cur = head;
    ComplexNode* new_cur = random;
    int count = 0;
    while(cur != new_cur)
    {
        count++;
        cur = cur -> next;
    }
    return count;
}

《链表相关笔试面试题》

    原文作者:qq_41027326
    原文地址: https://blog.csdn.net/qq_41027326/article/details/79787434
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞