您的位置:首页 > 职场人生

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

2018-04-01 11:32 405 查看
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;cur
d2c9
2=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 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: