最近听了左神的算法课,对一些常用数据结构以及算法改进的思路有了更深的理解,特此总结,不定期更新算法题目以及答案总结!笔者使用C++进行算法重现!虽然左神使用的是JAVA,但他自己也说了,算法与语言无关,但C++写出来的复杂度过不了,那么用其他的语言JAVA,Python也一定过不了!所以刷题还是尽量C++吧,算法基本用不了什么库函数,顶多几个数据结构,而C++的STL里面都包含。
单向链表
首先什么是单向链表,其实质也很简单,也就是有一个自定义的节点,其内部有且只有一个指针指向下一节点。但是一定要注意,单向链表分为单向有环链表和单向无环链表,其中单向无环链表的末节点为null。所以在面试时一定要注意题目的单向链表有没有说有环还是无环!接下来让我们看看这个题目~
题目:两个单链表相交的第一个节点
在本题中,单链表可能有环,也可能无环。给定两个 单链表的头节点 head1和head2,这两个链表可能相交,也可能 不相交。请实现一个函数, 如果两个链表相交,请返回相交的 第一个节点;如果不相交,返回null 即可。要求:如果链表1 的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外 空间复杂度请达到O(1)
首先我们先画出链表相交的几种形式,如下图所有,共有四种情况:
我们分析好了之后,开始Coding......
首先建立一个函数,判断一个单向链表是否有环,如果有则返回入环节点,如果没有返回null.在这里使用快慢指针的方法:(也可以使用set,但需要辅助空间)
如果不信,可以自己试试,数学能力好的可以自己证明!
// 如果没有环返回null, 如果有环返回入环的第一个节点(快慢指针的方式)
Node* getLoopNode2(Node* head){
if(head == nullptr || head->next == nullptr || head->next->next == nullptr){
return nullptr; // 三个节点以下不可能成环
}
Node* n1 = head->next;
Node* n2 = head->next->next;
while(n1 != n2){
if(n2->next == nullptr || n2->next->next == nullptr){
return nullptr;
}
n2 = n2->next->next;
n1 = n1->next;
}
n2 = head; // 快指针回到原来的位置,并改步长为1
while(n1 != n2){
n1 = n1->next;
n2 = n2->next;
}
return n1;}
接着,经过上面的函数后,我们会得到两种情况:有环链表相交和无环链表相交!但有人问,可不可以一个有环和一个无环相交?绝对不可能,这是单向链表!!!
并且,经过分析后,发现有环链表相交的情况为两种(情况二和情况三),而无环链表相交的情况只有Y字型一种(情况一)。
两个无环链表相交
这个就很简单了,也就是我画的情况一,Y字型,首先遍历两个链表,得出两个链表的长度差n,然后让长链表先遍历n个节点,接着两个链表同时遍历,直到节点相同,则相同节点为目标节点。一定要注意长度差的计算,并且如何区分长短链表!
那么怎么判断不相交呢?对于无环链表来说,相交的情况只存在Y字型,因此只要比对最后节点是不是相同就可以了,因为无环相交的最后一个节点必定相同!
Node* noLoop(Node* head1, Node* head2){ // 只能是Y字型结构 if (head1 == nullptr || head2 == nullptr){ return nullptr; } Node* cur1 = head1; Node* cur2 = head2; int len1 = 0, len2 = 0; while(cur1->next != nullptr){ len1++; cur1 = cur1->next; } while(cur2->next != nullptr){ len2++; cur2 = cur2->next; } if(cur1 != cur2){ // 如果有公共部分,必定指向统一节点 return nullptr; } int n = len1 - len2; cur1 = (n > 0) ? head1 : head2; // cur1为较长的list cur2 = cur1 == head1 ? head2 : head1; // cur2为较短的list n = n < 0 ? -1*n : n; // 取绝对值 while(n != 0){ n --; cur1 = cur1->next; } while(cur1 != cur2){ cur1 = cur1->next; cur2 = cur2->next; } return cur1;}
两个有环链表相交
有环链表相交分为两种情况,即图中的情况二和情况三,也就是入环节点在不在同一个节点?
Node* bothLoop(Node* head1, Node* loop1, Node* head2, Node* loop2){
Node* cur1 = nullptr;
Node* cur2 = nullptr;
if(loop1 == loop2){ // Y型下面有一个环,和Y型类似
cur1 = head1;
cur2 = head2;
int n = 0;
while(cur1 != loop1){
n++;
cur1 = cur1->next;
}
while(cur2 != loop2){
n--;
cur2 = cur2->next;
}
cur1 = n > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
n = n < 0 ? -1*n : n;
while(n != 0){
n--;
cur1 = cur1->next;
}
while(cur1 != cur2){
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}else{
cur1 = loop1->next;
while(cur1 != loop1){
if (cur1 == loop2){
return loop1; // 天线宝宝型的拓扑结构
}
cur1 = cur1->next;
}
return nullptr; // 两个有环链表不相交
}
}
由于C++中new出来的节点,需要进行delete进行内存释放,但笔者在测试文件中只写了单向无环链表的内存释放函数,而有环的如何释放笔者也有点迷茫,有大佬知道的还请告知一下!