• 链表重点问题(下)


     

    1.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度

    思路:还是通过快慢指针来解决,让fast指针每次走2个结点,慢指针一次走一个结点

    时间复杂度为O(n)   空间复杂度O(1 )

    注意fast每次走3个 4个.. 结点是不行的。分析如下:

    考虑清楚后,便可以写下代码:

    //(1)判断链表是否带环
    SListNode* SListIsCycle(SListNode* list) { assert(list); SListNode* fast = list; SListNode* slow = list; while(fast && fast->_next){ fast = fast->_next->_next; slow = slow->_next; if(slow == fast){ return fast; //返回相遇点地址 } } return NULL; }


    (2)求环长度
    思路如下:
    把相遇点的下一个结点地址赋给cur ,cur按环遍历一圈,通过计算器count便可求得环长
    时间复杂度O(n) 空间复杂度O(1)
    int
    SListCycleLen(SListNode* meetNode){ assert(meetNode); size_t count = 0; SListNode* cur = meetNode->_next; while(cur != meetNode){ count++; cur = cur->_next; } return count+1
    ; }

    (3) 求入口点

    如果说不太理解“slow和fast第一次相遇,slow没绕环走超过一圈”。可以考虑这样一种情况:

    注:slow和fast同时从一个地方走,这种情况下它们之间相差距离是最大的(刚好一圈)。v(fast) = 2v(slow), fast先进入环内。

    有了以上分析,不难写得如下代码:

    //meet的下一个结点地址赋给cur,list,cur同时往后走,相遇点即是入口点
    时间复杂度O(n) 空间复杂度O(1)
    SListNode* SListEntryNode(SListNode* list, SListNode* meetNode) { assert(list && meetNode); SListNode* cur = meetNode; while(cur != list){ cur = cur->_next; list = list->_next; } return cur; }

    2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)

    思路:
    1.先遍历两链表求得长度分别为x1,x2; 2.让指向长链表的指针先走|x1 - x2|的长度 3.
    最后两指针同时走,直到相遇
    SListNode* SListNonCircleIsCrossNode(SListNode* list1, SListNode* list2){
        assert(list1 && list2);
        
        size_t x1 = 0,x2 = 0;
        SListNode* cur1 = list1;  //注意定义中间变量来遍历,list1、list2是两链表的头指针,不能改变其指向,故不能用来遍历,
        SListNode* cur2 = list2;
        while(cur1){
            cur1 = cur1->_next;
            x1++;
        } 
        while(cur2){
            cur2 = cur2->_next;
            x2++;
        }
        
        SListNode* longList = list1;
        SListNode* shortList = list2;
        if(x2 > x1){
            longList = list2;
            shortList = list1;
        }
        int gap = abs(x1 - x2);  //abs() 调用求绝对值函数 
        while(gap--){
            longList = longList->_next;
        } 
        //两指针同时走
        while(longList){
            longList = longList->_next;
            shortList = shortList->_next;
            if(longList == shortList){
                return longList;     //返回交点地址 
            }
        } 
        return NULL;
    } 

    3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

    需分下面几种情况讨论:
    (1)都不带环,转换成2题。

    (2)一个带环,一个不带环。那一定不相交(若相交了,原链表结构肯定被改变了)。

    (3)都带环。都带环也分为以下三种情况(利用两个相遇点(M1,M2) 判断是否带环——让M1不动,M2走一圈,如果两个能相遇,那就相交,反之不相交)

    ①不相交
           
    从meet遍历一遍环,能找到meet2则判定两环是相交
            ②环外相交(入口点相同)——从入口点处把链表截断,问题就转换成求两个不带环链表相交点。 

                

            ③环内相交(入口点不同)
    SListNode* SListCrossNode(SListNode* list1, SListNode* list2){
        assert(list1 && list2);
        //情况①:都不带环,转到上题 
        if(!SListIsCycle(list1) && !SListIsCycle(list2)){
            SListNonCircleIsCrossNode(list1,list2);
        }
        
        //情况②:都带环 
        else if(SListIsCycle(list1) && SListIsCycle(list2)){
            SListNode* pMeetNode1 = SListIsCycle(list1);  //上题函数 
            SListNode* pMeetNode2 = SListIsCycle(list2);     
            SListNode* cur = pMeetNode1->_next;
            
            while(cur != pMeetNode1 || pMeetNode1 == pMeetNode2){
                if(cur == pMeetNode2){ //相交的情况
                    SListNode* entry1 = SListEntryNode(list1, pMeetNode1); //返回list1入口点 
                    SListNode* entry2 = SListEntryNode(list2, pMeetNode2); //返回list2入口点 
                    
                    if(entry1 == entry2){   //两入口点相同,环外相交 
                        entry1->_next = NULL;
                        return SListNonCircleIsCrossNode(list1 , list2);
                    }
                
                    
                    else{                //两入口点不同,环内相交 
                        printf("有两个交点,分别就是两个入口点
    ");
                        int n = 0;
                        scanf("%d",&n);
                        if(n) 
                        return entry1;
                        else
                        return entry2; 
                    } 
                }
                cur = cur->_next;
            } 
            
        } 
        else return NULL;
        
    }

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

    思路:依次比较两指针所指内容,谁小往后移动,若指向相同,两指针同时往后移动

    void UnionSet(SListNode* l1, SListNode* l2){
        assert(l1 && l2);
        
        while(l1 && l2){
            if(l1->_data < l2->_data){
                l1 = l1->_next;
            }
            else if(l1->_data > l2->_data){
                l2 = l2->_next;
            }
            else{
                printf("%d ",l1->_data);
                l1 = l1->_next;
                l2 = l2->_next;
            }
        } 
    }  

    5.复杂链表的复制。一个链表的每个节点,有一个指向next指针指向 下一个节点,还有一个random指针指向这个链表中的一个随机节点 或者NULL,现在要求实现复制这个链表,返回复制后的新链表。

    思路: ①复制原链表的每一个结点,把复制的结节点插在原节点的后面。 
                ②依次对新结点的random处理,新结点的random所指结点就是原结点的random所指结点的下一个结点  
                ③最后分离两个链表 

    typedef struct ComplexListNode 
    { 
        int _data; 
        struct ComplexListNode* _next; 
        struct ComplexListNode* _random; 
    }CLNode; 
    
    CLNode* BuyCLNode(int x)
    {
        CLNode* newCLNode = (CLNode*)malloc(sizeof(CLNode));
        if (newCLNode != NULL)
        {
            newCLNode->_data = x;
            newCLNode->_next = NULL;
            newCLNode->_random = NULL;
        }
        return newCLNode;
    }
    
    CLNode* CopyComplexList(CLNode* list)   //复制复杂链表函数
    {   
        assert(list);
        //拷贝链表
        CLNode* cur = list;
        while (cur != NULL)
        {
            CLNode* newCLNode = BuyCLNode(cur->_data);
            newCLNode->_next = cur->_next; 
            cur->_next = newCLNode;
    
            cur = newCLNode->_next;
        }
    
        //置random
        cur = list;
        while (cur)
        {
            CLNode* next = cur->_next;
            if (cur->_random )
            {
                next->_random = cur->_random->_next;
            }
            cur = next->_next;
        }
    
    
        //拆断两链表
        cur = list;
        CLNode* NewHead = cur->_next;  //NewHead作复制链表的头指针
    
        CLNode* next = cur->_next;
        CLNode* Nextnext = next->_next;
         
        while (cur != NULL)
        {
            cur->_next = Nextnext;
            if (next->_next != NULL)  //最后一次循环时,next->_next已指向NULL
            {
                next->_next = Nextnext->_next;
            }
    
            cur = Nextnext;
            if (cur != NULL)   //最后一次循环,Nextnext赋值给cur ,cur已指向NULL
            {
                next = cur->_next;
                Nextnext = next->_next;
            }
        }
        return NewHead;
    }
        
  • 相关阅读:
    BZOJ1409 : Password
    BZOJ2862 : 分糖果
    BZOJ2093 : [Poi2010]Frog
    BZOJ2506 : calc
    BZOJ3290 : Theresa与数据结构
    BZOJ1397 : Ural 1486 Equal squares
    BZOJ2789 : [Poi2012]Letters
    BZOJ3417 : Poi2013 Tales of seafaring
    BZOJ3251 : 树上三角形
    BZOJ3262 : 陌上花开
  • 原文地址:https://www.cnblogs.com/tp-16b/p/8186032.html
Copyright © 2020-2023  润新知