单链表的几道经典面试题(2)
2018-04-01 11:32
405 查看
1.判断单链表是否带环2.计算环的长度3.求环的入口4.判断两个链表是否相交,若相交求交点(默认链表不带环)5.判断两个链表是否相交,若相交求交点(链表可能带环)6.找两个已排序链表的相同部分7.复杂链表拷贝
判断单链表是否带环方法一:将每个节点保存到顺序表中,再将每个节点遍历,看是否带环。但是这种方法的时间复杂度为O(n^2),所以,又想出了一种方法,来优化第一种方法,来使时间复杂度变为O(n)
方法二:创建两个指针分别为fast和slow,让这两个指针开始都指向链表头部,让fast指针每次走两步,slow指针每次走一步,若最终两个指针相遇,则链表一定带环。
计算环的长度
定义一个指针cur,和一个长度len,让cur指针刚开始就指向环的相遇点,让cur指针一直往下走,cur每走一步len都加1,当再次回到相遇点,返回len,就是环的长度。
求环的入口结论:从链表开始点到入口点的长度等于入口点到相遇点的长度
两条链表相交有两种可能,一种为“X”型相交,一种为“Y”型相交,但由于链表的每一个节点的下一个节点只能有一个,所以“X”型相交不成立,只有“Y”型相交成立。
(1)判断是否相交对于“Y”型相交,判断是否相交,需要先定义两个指针cur1和cu2分别指向两个链表的头部,然后分别往后移动,直到cur1和cur2都指向链表的尾部,若最终两个指针cur1=cur2,则说明,链表相交。
(2)若相交,求交点
首先要定义两个指针cur1和cur2,使它们指向两个链表的头部,然后计算出两条链表的长度,分别为len1和len2,若两个长度不相等,则设两个长度差为offset,让长的链表的的cur指针先走offset步,然后就相当于两个cur指针在同一起点出发,然后让cur1和cur2每次走一步,直到cur1=cur2,则相等的点就是交点。
判断两个链表是否相交,若相交求交点(链表可能带环)
(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)环内相交:求出两个入口点,就是交点
方法二:遍历链表,在每个链表后插入新节点,维护新节点的random指针,将新节点拆除
判断单链表是否带环方法一:将每个节点保存到顺序表中,再将每个节点遍历,看是否带环。但是这种方法的时间复杂度为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 }
相关文章推荐
- 单链表的几道经典面试题(1)
- 经典算法面试题(二):用递归法把二叉树的叶子结点按从左到右的顺序连成一个单链表
- 几道经典C语言面试题
- 单链表的一些经典面试题
- 重解几道华为经典C语言面试题
- 几道经典C语言面试题(转)
- 几道经典C语言面试题
- 单链表的一些经典面试题
- 重解几道华为经典C语言面试题
- 几道链表操作的经典面试题:链表有环、删除节点
- 经典面试题之单链表找环
- 重解几道华为经典C语言面试题
- c::关于strcpy的几道经典的面试题~
- 几道经典的SQL面试题
- 重解几道华为经典C语言面试题
- 关于内存的几道经典面试题
- 【经典面试题】寻找单链表倒数第n个节点
- 【经典面试题】寻找单链表倒数第n个节点_C/C++
- 单链表经典面试题
- Linux下C语言的几道经典面试题小结(分享)