常见的单链表面试题——进阶篇
2017-06-16 20:01
459 查看
=================================================================================================================================================
问题描述:单链表是否带环,若带环问环的入口点在哪里,还有环的长度是的多少?对于链表带环我们都知道链表带环会出现死循环,进环就不可能出来了,但这个想法用代码实现起来好像不可能。但可以利用这个特性定义快慢指针,快指针每次走两步,慢指针走一步,因为若带环它们在环内的距离总是一步一步在逼近,最终相遇。从相遇点开始再走一圈记录节点数量就求出了环的长度。具体思路如图:
如图就由2*slow=fast得:2*(L+X)=L+N*C+X
解得:L=N*C-X
所以设置两个指针让一个从头走,一个从快慢指针相遇点走,这两个指针相遇时就是环的入口点。
问题描述:两个链表都没有带环,也就是说它们都可以找到尾节点。相交就是其中一个链表的尾链到了另一个链表的任意一个节点上。怎么判断是否相交呢?很简单,只需判断它们是否有相同的尾就可以了。那么还有一个问题就是在哪个节点相交呢?可以用快慢指针的思路来解决,具体思路如下图所示:
问题描述:若链表带环该如何解决上述问题?
一共有这几种情况:
问题描述:有链表里有两个指针一个指向下一个节点,另一个随机指针指向其他节点(有可能指向空),现在要复制这个链表。一般思路先创建一个链表,先复制它的next对应的节点,然后在通过遍历的方式去给它的random赋值,这样做太麻烦,而且效率还很低。于是乎就有了这样的方法:
通过三遍遍历的方式就完成了复杂链表的复制问题
单链表面试题——进阶篇
单链表是否带环
问题描述:单链表是否带环,若带环问环的入口点在哪里,还有环的长度是的多少?对于链表带环我们都知道链表带环会出现死循环,进环就不可能出来了,但这个想法用代码实现起来好像不可能。但可以利用这个特性定义快慢指针,快指针每次走两步,慢指针走一步,因为若带环它们在环内的距离总是一步一步在逼近,最终相遇。从相遇点开始再走一圈记录节点数量就求出了环的长度。具体思路如图:如图就由2*slow=fast得:2*(L+X)=L+N*C+X
解得:L=N*C-X
所以设置两个指针让一个从头走,一个从快慢指针相遇点走,这两个指针相遇时就是环的入口点。
实现代码:
int IsHaveRing(ListNode* list)//这里设计一个返回值为int的变量可以直接返回环长度 { int length = 1; ListNode* slow = list; ListNode* fast = list; if (list == NULL) return 0;//传来空链表 while (fast && (fast->next))//找尾 { slow = slow->next; fast = (fast->next)->next; if (slow == fast) break;//说明已经在环里相遇了就直接跳出循环 } if ((fast == NULL) || (fast->next == NULL))//判断有没有找到尾 return -1;//返回-1代表没有环 while (fast->next != slow)//这里的判断条件默认少一个节点所以我将长度初值设为1 { length++; fast = fast->next; } return length; }
判断两个链表是否相交(若不带环)
问题描述:两个链表都没有带环,也就是说它们都可以找到尾节点。相交就是其中一个链表的尾链到了另一个链表的任意一个节点上。怎么判断是否相交呢?很简单,只需判断它们是否有相同的尾就可以了。那么还有一个问题就是在哪个节点相交呢?可以用快慢指针的思路来解决,具体思路如下图所示:
实现代码:
ListNode* IsIntersect(ListNode* list1, ListNode* list2) { ListNode* tmp1 = list1; ListNode* tmp2 = list2; int count1 = 0; int count2 = 0; if (list1 == NULL && list2 == NULL) return NULL; while (tmp1->next)//遍历链表记录其节点数 { count1++; tmp1 = tmp1->next; } while (tmp2->next) { count2++; tmp2 = tmp2->next; } if (tmp1 == tmp2)//判断尾若相等则相交 { if (count1 > count2)//谁长决定谁先走差出来的步数 { int gap = count1 - count2; while (gap--)//先走gap步 list1 = list1->next; while (list1 != list2) { list1 = list1->next; list2 = list2->next; } list1->next = NULL; return list1; } else { int gap = count2 - count1;//与上面那段代码是一个逻辑 while (gap--) list2 = list2->next; while (list1 != list2) { list1 = list1->next; list2 = list2->next; } list1->next = NULL; return list1; } } return NULL; }
链表相交问题的升级版
问题描述:若链表带环该如何解决上述问题? 一共有这几种情况:
实现代码:
int IsHaveRing(ListNode* list)//判断是否带环,带环则返回环长度,不带则返回-1 { int length = 1; ListNode* slow = list; ListNode* fast = list; if (list == NULL) return 0; while (fast && (fast->next)) { slow = slow->next; fast = (fast->next)->next; if (slow == fast) break; } if ((fast == NULL) || (fast->next == NULL)) return -1;//返回-1代表没有环 while (fast->next != slow) { length++; fast = fast->next; } return length; } static ListNode* EnterRingNode(ListNode* list)//找入口点 { ListNode* slow = list; ListNode* fast = list; if (list == NULL) return 0; while (fast) { slow = slow->next; fast = (fast->next)->next; if (slow == fast) break; } while (slow != list) { slow = slow->next; list = list->next; } return list; } static ListNode* IsIntersect(ListNode* list1, ListNode* list2)//不带环链表相交。 复用了不带环的判断代码 { ListNode* tmp1 = list1; ListNode* tmp2 = list2; int count1 = 0; int count2 = 0; if (list1 == NULL && list2 == NULL) return NULL; while (tmp1->next) { count1++; tmp1 = tmp1->next; } while (tmp2->next) { count2++; tmp2 = tmp2->next; } if (tmp1 == tmp2) { if (count1 > count2) { int i = count1 - count2; while (--i) list1 = list1->next; while (list1 != list2) { list1 = list1->next; list2 = list2->next; } list1->next = NULL; return list1; } else { int i = count2 - count1; while (i--) { list2 = list2->next; } while (list1 != list2) { list1 = list1->next; list2 = list2->next; } list1->next = NULL; return list1; } } else return NULL; } //以上函数都是为下面这个函数服务的 ListNode* AdIsIntersect(ListNode* list1, ListNode* list2)//带环或不带环链表相交 { int len1 = 0; int len2 = 0; if (list1 == NULL && list2 == NULL) return NULL; len1 = IsHaveRing(list1); len2 = IsHaveRing(list2); if ((len1 < 0) && (len2 < 0))//都不带环且相交情况处理 return IsIntersect(list1, list2); else if (((len1 > 0) && (len2 < 0))||((len1 < 0) && (len2 > 0)))//一个带环一个不带环情况的处理 return NULL; else { ListNode* tmp1 = EnterRingNode(list1); ListNode* tmp2 = EnterRingNode(list2); //判断一个环内是否有另一个的节点 while (tmp2->next != EnterRingNode(list2)) { if (tmp2 == tmp1->next) { if (EnterRingNode(list1) != EnterRingNode(list2)) {//若入口点相同视为一种情况 ListNode* list3 = list1; while (list3->next != EnterRingNode(list1)) { list3 = list3->next; } list3->next = NULL; return IsIntersect(list1, list2); } return EnterRingNode(list1);//入口点不同就返回其中一个 } tmp2 = tmp2->next; } return NULL;//一个环内没有另一个的节点则返回空 } }
复杂链表的复制问题
问题描述:有链表里有两个指针一个指向下一个节点,另一个随机指针指向其他节点(有可能指向空),现在要复制这个链表。一般思路先创建一个链表,先复制它的next对应的节点,然后在通过遍历的方式去给它的random赋值,这样做太麻烦,而且效率还很低。于是乎就有了这样的方法:
实现代码:
ListNode* CPList(ListNode* list) { ListNode* tmp = list; while (tmp) { ListNode* insert = BuyNode(tmp->data); insert->next = tmp->next; tmp->next = insert; tmp = tmp->next->next; } ListNode* CPNode = list; while (CPNode) { if (CPNode->random != NULL) CPNode->next->random = CPNode->random->next; CPNode = CPNode->next->next; } ListNode* div = list; ListNode* cphead = list->next; ListNode* cptail = cphead; while (cptail->next) { div->next = div->next->next; div = div->next; cptail->next = div->next; cptail = cptail->next; if (div->next->next == NULL)//断开原链表的最后一个节点,以防止俩链表相交 div->next = NULL; } return cphead; }
通过三遍遍历的方式就完成了复杂链表的复制问题