Answers:
如果
以下算法将是解决方案。
首先,数字。假设第一个列表的长度为a+c
,第二个列表的长度为b+c
,其中c
是它们共同的“尾巴”的长度(在合并点之后)。让我们将它们表示如下:
x = a+c
y = b+c
由于我们不知道长度,因此我们将进行计算x
,y
而无需进行其他迭代;您将看到如何。
然后,我们迭代每个列表,并在迭代时反转它们!如果两个迭代器同时到达合并点,那么我们仅通过比较就可以找到它。否则,一个指针将在另一指针之前到达合并点。
此后,当另一个迭代器到达合并点时,它将不会进行到通用尾部。相反,它将返回到之前已达到合并点的列表的前一个开始!因此,在到达更改列表的末尾(即另一个列表的前一个开始)之前,他将进行a+b+1
总计迭代。叫它z+1
。
首先到达合并点的指针将继续迭代,直到到达列表的末尾。计算出的迭代次数应等于x
。
然后,该指针进行迭代,并再次反转列表。但是现在它不会回到原来的列表的开头了!相反,它将转到另一个列表的开头!计算得出的迭代次数应等于y
。
因此,我们知道以下数字:
x = a+c
y = b+c
z = a+b
从中我们确定
a = (+x-y+z)/2
b = (-x+y+z)/2
c = (+x+y-z)/2
解决了问题。
以下是迄今为止我所见过的最棒的-O(N),没有计数器。我在VisionMap的候选人SN采访中得到了它。
像这样创建一个中间指针:它每次都向前移动直到结尾,然后跳到对面列表的开头,依此类推。创建其中两个,指向两个头。每次将每个指针前进1,直到它们相遇。一两次通过之后,就会发生这种情况。
我在访谈中仍然使用这个问题-但要了解需要多长时间才能理解该解决方案的有效性。
a-b-c-x-y-z
和p-q-x-y-z
。第一个指针的a,b,c,x,y,z,p,q,x
路径,第二个指针的路径p,q,x,y,z,a,b,c,x
Pavel的答案需要修改列表以及对每个列表进行两次迭代。
这是一个只需要迭代每个列表两次的解决方案(第一次计算它们的长度;如果给出了长度,则只需要迭代一次)。
这个想法是忽略较长列表的开始条目(合并点不能在那里),以便两个指针到列表末尾的距离相等。然后将它们向前移动,直到它们合并。
lenA = count(listA) //iterates list A
lenB = count(listB) //iterates list B
ptrA = listA
ptrB = listB
//now we adjust either ptrA or ptrB so that they are equally far from the end
while(lenA > lenB):
ptrA = ptrA->next
lenA--
while(lenB > lenA):
prtB = ptrB->next
lenB--
while(ptrA != NULL):
if (ptrA == ptrB):
return ptrA //found merge point
ptrA = ptrA->next
ptrB = ptrB->next
与我的其他答案渐近相同(线性时间),但常数较小,因此可能更快。但是我认为我的其他答案比较酷。
好吧,如果您知道它们将合并:
假设您从以下内容开始:
A-->B-->C
|
V
1-->2-->3-->4-->5
1)浏览第一个列表,将每个下一个指针设置为NULL。
现在您有了:
A B C
1-->2-->3 4 5
2)现在浏览第二个列表,等待直到看到一个NULL,这就是您的合并点。
如果不能确定它们是否合并,可以将定点值用作指针值,但这并不那么优雅。
这是一个解决方案,计算速度快(每个列表重复一次),但使用大量内存:
for each item in list a
push pointer to item onto stack_a
for each item in list b
push pointer to item onto stack_b
while (stack_a top == stack_b top) // where top is the item to be popped next
pop stack_a
pop stack_b
// values at the top of each stack are the items prior to the merged item
可以说这违反了“仅分析每个列表一次”的条件,但是实现了乌龟和野兔算法(用于查找循环列表的合并点和循环长度),因此您从列表A开始,并且在到达列表NULL时您假装它是指向列表B开头的指针,从而创建了循环列表的外观。然后,该算法将告诉您确切的合并列表A有多远(根据Wikipedia的描述,变量“ mu”)。
另外,“ lambda”值告诉您列表B的长度,如果需要,您可以在算法期间(重定向NULL链接时)计算出列表A的长度。
也许我已经简化了这个工作,但是只是迭代最小的列表并将最后一个节点Link
用作合并点?
因此,Data->Link->Link == NULL
终点在哪里,Data->Link
作为合并点(在列表的末尾)。
编辑:
好的,从您发布的图片中,您解析了两个列表,最小的列表。使用最小的列表,您可以维护对以下节点的引用。现在,当您解析第二个列表时,您将对引用进行比较,以找到引用[i]在LinkedList [i]-> Link处的引用。这将给出合并点。用图片解释的时间(将值叠加在OP上)。
您有一个链接列表(参考如下所示):
A->B->C->D->E
您还有第二个链接列表:
1->2->
对于合并的列表,引用将如下所示:
1->2->D->E->
因此,您映射了第一个“较小”列表(作为合并列表,这就是我们要计算的长度为4而主列表为5)
遍历第一个列表,维护引用的引用。
该列表将包含以下参考Pointers { 1, 2, D, E }
。
现在,我们浏览第二个列表:
-> A - Contains reference in Pointers? No, move on
-> B - Contains reference in Pointers? No, move on
-> C - Contains reference in Pointers? No, move on
-> D - Contains reference in Pointers? Yes, merge point found, break.
当然,您需要维护一个新的指针列表,但这并不超出规范范围。但是,第一个列表仅被解析一次,而第二个列表将仅在没有合并点的情况下被完全解析。否则,它将更快(在合并点)结束。
我已经在我的FC9 x86_64上测试了合并情况,并打印每个节点地址,如下所示:
Head A 0x7fffb2f3c4b0
0x214f010
0x214f030
0x214f050
0x214f070
0x214f090
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
Head B 0x7fffb2f3c4a0
0x214f0b0
0x214f0d0
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
请注意,如果我已经对齐了节点结构,那么当malloc()一个节点时,地址以16个字节对齐,请参见最低4位。最低位为0,即0x0或000b。因此,如果您也使用相同的特殊情况(对齐的节点地址),则可以使用这至少4位。例如,当两个列表都从头到尾旅行时,设置访问节点地址的4位中的1或2,即设置一个标志;
next_node = node->next;
node = (struct node*)((unsigned long)node | 0x1UL);
请注意,以上标志不会影响实际节点地址,而只会影响您的SAVED节点指针值。
一旦发现有人设置了标志位,则发现的第一个节点应为合并点。完成后,您将通过清除已设置的标志位来还原节点地址。重要的是,在进行迭代(例如,node = node-> next)进行清理时应该小心。记住你已经设置了标志位,所以这样做
real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;
因为此建议将还原已修改的节点地址,所以可以将其视为“未修改”。
可以有一个简单的解决方案,但需要一个辅助空间。这个想法是遍历一个列表并将每个地址存储在一个哈希图中,现在遍历另一个列表并匹配该地址是否在哈希图中。每个列表仅被遍历一次。没有任何列表的修改。长度仍然未知。使用的辅助空间:O(n),其中“ n”是所遍历的第一个列表的长度。
此解决方案仅迭代每个列表一次...也无需修改列表。.尽管您可能会抱怨空间。.1
)基本上,您在list1中进行迭代并将每个节点的地址存储在一个数组中(该数组存储了unsigned int值)
2)然后迭代list2,并为每个节点的地址--->搜索是否找到匹配项的数组...如果您这样做,则这是合并节点
//pseudocode
//for the first list
p1=list1;
unsigned int addr[];//to store addresses
i=0;
while(p1!=null){
addr[i]=&p1;
p1=p1->next;
}
int len=sizeof(addr)/sizeof(int);//calculates length of array addr
//for the second list
p2=list2;
while(p2!=null){
if(search(addr[],len,&p2)==1)//match found
{
//this is the merging node
return (p2);
}
p2=p2->next;
}
int search(addr,len,p2){
i=0;
while(i<len){
if(addr[i]==p2)
return 1;
i++;
}
return 0;
}
希望这是一个有效的解决方案...
int FindMergeNode(Node headA, Node headB) {
Node currentA = headA;
Node currentB = headB;
// Do till the two nodes are the same
while (currentA != currentB) {
// If you reached the end of one list start at the beginning of the other
// one currentA
if (currentA.next == null) {
currentA = headA;
} else {
currentA = currentA.next;
}
// currentB
if (currentB.next == null) {
currentB = headB;
} else {
currentB = currentB.next;
}
}
return currentB.data;
}
这是幼稚的解决方案,无需遍历整个列表。
如果您的结构化节点具有三个字段,例如
struct node {
int data;
int flag; //initially set the flag to zero for all nodes
struct node *next;
};
假设您有两个标头(head1和head2)指向两个列表的标头。
以相同的速度遍历两个列表,并为该节点添加标志= 1(已访问标志),
if (node->next->field==1)//possibly longer list will have this opportunity
//this will be your required node.
我们可以通过引入“ isVisited”字段来有效地解决它。遍历第一个列表,并将所有节点的“ isVisited”值设置为“ true”,直到结束。现在从第二个开始,找到标记为true的第一个节点,然后找到Boom,这是您的合并点。
步骤1:找到两个列表的长度步骤2:找到差异并移动相差最大的列表步骤3:现在两个列表的位置相似。步骤4:遍历列表以找到合并点
//Psuedocode
def findmergepoint(list1, list2):
lendiff = list1.length() > list2.length() : list1.length() - list2.length() ? list2.lenght()-list1.lenght()
biggerlist = list1.length() > list2.length() : list1 ? list2 # list with biggest length
smallerlist = list1.length() < list2.length() : list2 ? list1 # list with smallest length
# move the biggest length to the diff position to level both the list at the same position
for i in range(0,lendiff-1):
biggerlist = biggerlist.next
#Looped only once.
while ( biggerlist is not None and smallerlist is not None ):
if biggerlist == smallerlist :
return biggerlist #point of intersection
return None // No intersection found
int FindMergeNode(Node *headA, Node *headB)
{
Node *tempB=new Node;
tempB=headB;
while(headA->next!=NULL)
{
while(tempB->next!=NULL)
{
if(tempB==headA)
return tempB->data;
tempB=tempB->next;
}
headA=headA->next;
tempB=headB;
}
return headA->data;
}
使用“映射”或“字典”存储节点的地址与值。如果地图/字典中已经存在该地址,则键的值就是答案。我这样做:
int FindMergeNode(Node headA, Node headB) {
Map<Object, Integer> map = new HashMap<Object, Integer>();
while(headA != null || headB != null)
{
if(headA != null && map.containsKey(headA.next))
{
return map.get(headA.next);
}
if(headA != null && headA.next != null)
{
map.put(headA.next, headA.next.data);
headA = headA.next;
}
if(headB != null && map.containsKey(headB.next))
{
return map.get(headB.next);
}
if(headB != null && headB.next != null)
{
map.put(headB.next, headB.next.data);
headB = headB.next;
}
}
return 0;
}
AO(n)复杂度解决方案。但是基于一个假设。
假设是:两个节点都只有正整数。
逻辑:将list1的所有整数都设为负数。然后遍历list2,直到得到一个负整数。一旦找到=>接受它,将符号改回正号并返回。
static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
SinglyLinkedListNode current = head1; //head1 is give to be not null.
//mark all head1 nodes as negative
while(true){
current.data = -current.data;
current = current.next;
if(current==null) break;
}
current=head2; //given as not null
while(true){
if(current.data<0) return -current.data;
current = current.next;
}
}
我们可以使用两个指针并以某种方式移动,如果其中一个指针为null,则将其指向另一个列表的头部,另一个指向相同的指针,这样,如果列表长度不同,它们将在第二遍通过。如果list1的长度为n,list2的长度为m,则它们的差为d = abs(nm)。他们将覆盖此距离并在合并点会合。
码:
int findMergeNode(SinglyLinkedListNode* head1, SinglyLinkedListNode* head2) {
SinglyLinkedListNode* start1=head1;
SinglyLinkedListNode* start2=head2;
while (start1!=start2){
start1=start1->next;
start2=start2->next;
if (!start1)
start1=head2;
if (!start2)
start2=head1;
}
return start1->data;
}
您可以将的节点添加list1
到哈希集中,然后通过第二个循环进行循环,如果list2
集合中已存在的任何节点。如果是,则为合并节点
static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
HashSet<SinglyLinkedListNode> set=new HashSet<SinglyLinkedListNode>();
while(head1!=null)
{
set.add(head1);
head1=head1.next;
}
while(head2!=null){
if(set.contains(head2){
return head2.data;
}
}
return -1;
}
使用JavaScript的解决方案
var getIntersectionNode = function(headA, headB) {
if(headA == null || headB == null) return null;
let countA = listCount(headA);
let countB = listCount(headB);
let diff = 0;
if(countA > countB) {
diff = countA - countB;
for(let i = 0; i < diff; i++) {
headA = headA.next;
}
} else if(countA < countB) {
diff = countB - countA;
for(let i = 0; i < diff; i++) {
headB = headB.next;
}
}
return getIntersectValue(headA, headB);
};
function listCount(head) {
let count = 0;
while(head) {
count++;
head = head.next;
}
return count;
}
function getIntersectValue(headA, headB) {
while(headA && headB) {
if(headA === headB) {
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
如果允许编辑链接列表,