单链表的几道经典面试题(2)

1.判断单链表是否带环
2.计算环的长度
3.求环的入口
4.判断两个链表是否相交,若相交求交点(默认链表不带环)
5.判断两个链表是否相交,若相交求交点(链表可能带环)
6.找两个已排序链表的相同部分
7.复杂链表拷贝


判断单链表是否带环
方法一:将每个节点保存到顺序表中,再将每个节点遍历,看是否带环。

但是这种方法的时间复杂度为O(n^2),所以,又想出了一种方法,来优化第一种方法,来使时间复杂度变为O(n)

方法二:创建两个指针分别为fast和slow,让这两个指针开始都指向链表头部,让fast指针每次走两步,slow指针每次走一步,若最终两个指针相遇,则链表一定带环。

 696 //判断单链表是否带环
 697 LinkNode* LinkListHasCricle(LinkNode* head)
 698 {
 699     if(head == NULL)
 700     {
 701         //空链表
 702         return NULL;
 703     }
 704     LinkNode* fast=head;
 705     LinkNode* slow=head;                                                                                 
 706     while(fast!=NULL&&fast->next!=NULL)
 707     {
 708         fast=fast->next->next;
 709         slow=slow->next;
 710         if(fast==slow)
 711         {
 712             return slow;
 713         }
 714     }
 715     return NULL;
 716 }
 717 

计算环的长度

定义一个指针cur,和一个长度len,让cur指针刚开始就指向环的相遇点,让cur指针一直往下走,cur每走一步len都加1,当再次回到相遇点,返回len,就是环的长度。
 718 //计算环的长度
 719 size_t LinkListCricleLen(LinkNode* head)
 720 {
 721     if(head == NULL)
 722     {
 723         //空链表
 724         return 0;
 725     }
 726     LinkNode* meet_node=LinkListHasCricle(head);
 727     if(meet_node == NULL)
 728     {
 729         return 0;
 730     }                                                                                                    
 731     LinkNode* cur=meet_node->next;
 732     size_t len=1;
 733     while(cur!=meet_node)
 734     {
 735         cur=cur->next;
 736         len++;
 737     }
 738     return len;
 739 }
 740 

求环的入口
结论:从链表开始点到入口点的长度等于入口点到相遇点的长度
 741 //求环的入口
 742 LinkNode* LinkListCricleEntry(LinkNode* head)
 743 {
 744     if(head == NULL)
 745     {
 746         //空链表
 747         return NULL;
 748     }
 749     LinkNode* meet_node=LinkListHasCricle(head);
 750     if(meet_node == NULL)
 751     {
 752         return NULL;                                                                                     
 753     }
 754     LinkNode* cur1=head;
 755     LinkNode* cur2=meet_node;
 756     while(cur1!=cur2)
 757     {
 758         cur1=cur1->next;
 759         cur2=cur2->next;
 760     }
 761     return cur1;
 762 }
 763 
判断两个链表是否相交,若相交求交点(默认链表不带环)
两条链表相交有两种可能,一种为“X”型相交,一种为“Y”型相交,但由于链表的每一个节点的下一个节点只能有一个,所以“X”型相交不成立,只有“Y”型相交成立。
(1)判断是否相交
对于“Y”型相交,判断是否相交,需要先定义两个指针cur1和cu2分别指向两个链表的头部,然后分别往后移动,直到cur1和cur2都指向链表的尾部,若最终两个指针cur1=cur2,则说明,链表相交。
 764 //判断两个链表是否相交(链表不带环)
 765 int LinkListHasCross(LinkNode* head1,LinkNode* head2)
 766 {
 767     if(head1 == NULL||head2 == NULL)
 768     {
 769         //空链表
 770         return 0;
 771     }
 772     LinkNode* cur1=head1;
 773     LinkNode* cur2=head2;
 774     for(;cur1->next!=NULL;cur1=cur1->next);
 775     for(;cur2->next!=NULL;cur2=cur2->next);
 776     return cur1 == cur2?1:0;                                                                             
 777 }

(2)若相交,求交点

首先要定义两个指针cur1和cur2,使它们指向两个链表的头部,然后计算出两条链表的长度,分别为len1和len2,若两个长度不相等,则设两个长度差为offset,让长的链表的的cur指针先走offset步,然后就相当于两个cur指针在同一起点出发,然后让cur1和cur2每次走一步,直到cur1=cur2,则相等的点就是交点。

 779 //接上一问题:若相交,求交点
 780 LinkNode* LinkListCrossPos(LinkNode* head1,LinkNode* head2)
 781 {
 782     size_t len1=LinkListSize(head1);
 783     size_t len2=LinkListSize(head2);
 784     LinkNode* cur1=head1;
 785     LinkNode* cur2=head2;
 786     if(len1>len2)
 787     {
 788         size_t i=0;
 789         for(;i<len1-len2;++i)
 790         {                                                                                                
 791             cur1=cur1->next;
 792         }
 793     }
 794     else
 795     {
 796         size_t i=0;
 797         for(;i<len2-len1;++i)
 798         {
 799             cur2=cur2->next;
 800         }
 801         while(cur1!=NULL&&cur2!=NULL)
 802         {
 803             if(cur1 == cur2)
 804             {
 805                 return cur1;
 806             }                                                                                            
 807             cur1=cur1->next;
 808             cur2=cur2->next;
 809         }
 810         return NULL;
 811     }
 812 }
 813 

判断两个链表是否相交,若相交求交点(链表可能带环)
(1)判断链表是否带环
这个问题分为了几种情况,分别为
1.两个链表都不带环       ——方法同上
2.两个链表一个带环,一个不带环        ——一定不相交
3.两个链表都带环
    1)不相交
    2)相交
        a)交点在环外
        b)交点在环上
思路:
分别求两个链表环的入口,若两个链表都不带环,则用上面的方法,若有一个链表带环,一个不带环,则直接返回不相交,若两个链表都带环,若两个链表环的入口相同,则是环外相交,若从第一个入口出发绕环一周能到达第二个入口点,则是环内相交,若都不是以上情况,则直接返回不相交。

 814 //两个链表是否相交(可能带环)
 815 int LinkListHasCrossWithCircle(LinkNode* head1,LinkNode* head2)
 816 {
 817     //分别求两个链表的入口
 818     LinkNode* entry1=LinkListCricleEntry(head1);
 819     LinkNode* entry2=LinkListCricleEntry(head2);
 820     //(1)若两个链表都不带环,则用上面的方法
 821     if(entry1 == NULL && entry2 == NULL)
 822     {
 823         return LinkListHasCross(head1,head2);
 824     }
 825     //(2)若有一个带环,一个不带环,则返回不相交
 826     if((entry1 == NULL && entry2!=NULL)||(entry1!=NULL && entry2 == NULL))                               
 827     {
 828         return 0;
 829     }
 830     //(3)若两个都带环
 831     //1)若入口点重合,说明环外相交
 832     if(entry1 == entry2)
 833     {
 834         return 1;
 835     }
 836     //2)若从一个入口点出发,绕环一周能到达第二个入口点,则说明是环内相交
 837     LinkNode* cur=entry1->next;
 838     while(cur!=entry1)
 839     {
 840         if(cur == entry2)
 841         {
 842             return 1;
 843         }
 844         cur=cur->next;                                                                                   
 845     }
 846     //3)若以上两种情况都不是,则不相交
 847     return 0;
 848 }
 849 

(2)若相交,求交点

若是两个环相交,分为环外相交和环内相交。
    1)环外相交:两个环环外相交,则说明只有一个入口点,将入口点设为结束标记,转化为两条不带环链表“Y”字形相交问题
             2)环内相交:求出两个入口点,就是交点
 850 //接上一问题:若相交,求交点(环外相交)
 851 LinkNode* LinkListCrossWithCirclePos(LinkNode* head1,LinkNode* head2)
 852 {
 853     LinkNode* longlist=NULL;                                                                             
 854     LinkNode* shortlist=NULL;
 855     LinkNode* cur1=head1;
 856     LinkNode* cur2=head2;
 857     int count1=0;
 858     int count2=0;
 859     int gap=0;
 860     LinkNode* meet_node=LinkListHasCricle(head1);
 861     while(cur1!=meet_node)
 862     {
 863         count1++;
 864         cur1=cur1->next;
 865     }
 866     while(cur2!=meet_node)
 867     {
 868         count2++;
 869         cur2=cur2->next;
 870     }
 871     longlist=head1;
 872     shortlist=head2;
 873     if(count1<count2)
 874     {
 875         longlist=head2;
 876         shortlist=head1;
 877     }                                                                                                    
 878     gap=abs(count1-count2);
 879     while(gap--)
 880     {
 881         longlist=longlist->next;
 882     }
 883     while(shortlist!=longlist)
 884     {
 885         shortlist=shortlist->next;
 886         longlist=longlist->next;
 887     }
 888     return shortlist;
 889 }
找两个已排序链表的相同部分
 890 //找两个已排序链表的相同部分(默认每个链表中不包含连续相同的元素,如2333333)
 891 LinkNode* LinkListUnionSet(LinkNode* head1,LinkNode* head2)
 892 {
 893     LinkNode* cur1=head1;
 894     LinkNode* cur2=head2;                                                                                
 895     LinkNode* new_head=NULL;
 896     LinkNode* new_tail=NULL;
 897     while(cur1!= NULL && cur2!=NULL)
 898     {
 899         if(cur1->data<cur2->data)
 900         {
 901             cur1=cur1->next;
 902         }
 903         else if(cur1->data>cur2->data)
 904         {
 905             cur2=cur2->next;
 906         }
 907         else{   //cur1=cur2
 908             if(new_head==NULL)
 909             {
 910                 new_head=new_tail=CreateNode(cur1->data);
 911                 cur1=cur1->next;
 912                 cur2=cur2->next;
 913             }
 914             else{
 915                 new_tail->next=CreateNode(cur2->data);
 916                 new_tail=new_tail->next;
 917                 cur1=cur1->next;                                                                         
 918                 cur2=cur2->next;
 919             }
 920             //cur1=cur1->next;
 921             //cur2=cur2->next;
 922 
 924         //return new_head;
 925     }
 926     return new_head;
 927 }                                                                                                        
 928 
复杂链表拷贝
方法一:先按照简单链表的方式拷贝,然后遍历链表,找到找到每个链表节点的random指针相对于链表头部的偏移量,遍历新链表,根据偏移量设新链表的random指针
 929 //复杂链表拷贝
 930 typedef struct ComplexNode
 931 {
 932     LinkNodeType data;
 933     struct ComplexNode* next;                                                                            
 934     struct ComplexNode* random;
 935 }ComplexNode;
 936 
 937 ComplexNode* CreateComplexNode(LinkNodeType value)
 938 {
 939     ComplexNode* new_node=(ComplexNode*)malloc(sizeof(ComplexNode));
 940     new_node->data=value;
 941     new_node->next=NULL;
 942     new_node->random=NULL;
 943     return new_node;
 944 }
 945 
 946 size_t Diff(ComplexNode* src,ComplexNode* dst)
 947 {
 948     size_t offset=0;
 949     while(src!=NULL)
 950     {
 951         if(src == dst)
 952         {
 953             break;
 954         }
 955         ++offset;
 956         src=src->next;                                                                                   
 957     }
 958     if(src == NULL)
 959     {
 960         return(size_t)-1;   //是一个特别大的值
 961     }
 962 }
 963 
 964 ComplexNode* Step(ComplexNode* head,size_t offset)
 965 {
 966     ComplexNode* cur=head;
 967     size_t i=0;
 968     while(1)
 969     {
 970         if(head == NULL)
 971         {
 972             return NULL;
 973         }                                                                                                
 974         if(i>=offset)
 975         {
 976             return cur;
 977         }
 978         ++i;
 979         cur=cur->next;
 980     }
 981     return NULL;
 982 }
 983 
 984 ComplexNode* CopyComplexList(ComplexNode* head)
 985 {
 986     //先按照简单的链表copy一份
 987     ComplexNode* new_head=NULL;
 988     ComplexNode* new_tail=NULL;
 989     ComplexNode* cur=head;
 990     for(;cur!=NULL;cur=cur->next)                                                                        
 991     {
 992         ComplexNode* new_node=CreateComplexNode(cur->data);
 993         if(new_node == NULL)
 994         {
 995             new_head=new_tail=new_node;
 996         }
 997         else{
 998             new_tail->next=new_node;
 999             new_tail=new_tail->next;
1000         }
1001     }
1002     //遍历链表,找到每个链表节点random指针相对于链表头指针的偏移量  
1003     //遍历新链表,根据偏移量设新链表的random指针
1004     ComplexNode* new_cur=new_head;
1005     for(cur=head;cur!=NULL;cur=cur->next,new_cur=new_cur->next)
1006     {
1007                                                                                                          
1008     if(cur->random == NULL)
1009     {
1010         new_cur->random = NULL;
1011         continue;
1012     }
1013         //通过Diff函数计算出链表两个节点的偏移量
1014         size_t offset=Diff(head,cur->random);
1015         //通过Step函数,相当于求出从new_head出发,走了offset步到达的位置
1016         new_cur->random;
1017     }
1018     return new_head;
1019 
1020 }
1021 

方法二:遍历链表,在每个链表后插入新节点,维护新节点的random指针,将新节点拆除
1023 ComplexNode* CopyComplexList2(ComplexNode* head)
1024 {
1025     //遍历链表在每个链表后插入新节点
1026     //维护新节点的random指针
1027     //新节点排除
1028     ComplexNode* cur=head;
1029     for(;cur!=NULL;cur=cur->next->next)
1030     {
1031         ComplexNode* new_node=CreateComplexNode(cur->data);
1032         new_node->next=cur->next;
1033     }
1034     for(cur=head;cur!=NULL;cur=cur->next->next)                                                          
1035     {
1036         ComplexNode* new_cur=cur->next;
1037         if(cur->random==NULL)
1038         {
1039             new_cur->random=NULL;
1040             continue;
1041         }
1042         new_cur->random=cur->random->next;
1043     }
1044     ComplexNode* new_head=NULL;
1045     ComplexNode* new_tail=NULL;
1046     for(cur=head;cur!=NULL;cur=cur->next)
1047     {
1048         ComplexNode* new_cur=cur->next;
1049         cur->next=new_cur->next;
1050         if(new_head == NULL)                                                                             
1051         {
1052             new_head=new_tail=new_cur;
1053         }
1054         else{
1055             new_tail->next=new_cur;
1056             new_tail=new_tail->next;
1057         }
1058         new_tail=NULL;
1059     }
1060     return new_head;
1061 }



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值