Algorithm 检查两个链接列表是否合并。如果是,在哪里?
这个问题可能很老,但我想不出答案 比如说,有两个长度不同的列表,在一个点合并;我们如何知道合并点在哪里 条件:Algorithm 检查两个链接列表是否合并。如果是,在哪里?,algorithm,linked-list,data-structures,Algorithm,Linked List,Data Structures,这个问题可能很老,但我想不出答案 比如说,有两个长度不同的列表,在一个点合并;我们如何知道合并点在哪里 条件: 我们不知道长度 我们应该只解析每个列表一次 也许我过于简化了,但只需迭代最小的列表并使用最后的节点链接作为合并点 因此,其中Data->Link->Link==NULL是终点,将Data->Link作为合并点(在列表的末尾) 编辑: 好的,从你发布的图片,你分析两个列表,最小的第一个。使用最小的列表,可以维护对以下节点的引用。现在,当您解析第二个列表时,您将对引用进行比较,以找到引用[
也许我过于简化了,但只需迭代最小的列表并使用最后的节点
链接
作为合并点
因此,其中Data->Link->Link==NULL
是终点,将Data->Link
作为合并点(在列表的末尾)
编辑:
好的,从你发布的图片,你分析两个列表,最小的第一个。使用最小的列表,可以维护对以下节点的引用。现在,当您解析第二个列表时,您将对引用进行比较,以找到引用[i]在LinkedList[i]->Link处的引用位置。这将给出合并点。用图片解释的时间(将数值叠加在OP的图片上)
您有一个链接列表(参考资料如下所示):
A->B->C->D->E
您还有第二个链接列表:
1->2->
对于合并列表,引用将如下所示:
x = a+c
y = b+c
1->2->D->E->
因此,您映射了第一个“较小”列表(因为合并列表的长度为4,主列表的长度为5)
循环浏览第一个列表,维护引用的引用
该列表将包含以下引用指针{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.
当然,您维护了一个新的指针列表,但这并不在规范之外。但是,第一个列表只解析一次,第二个列表只有在没有合并点的情况下才会被完全解析。否则,它将更快地结束(在合并点)。这可能违反了“仅解析每个列表一次”的条件,但实现了(用于查找循环列表的合并点和循环长度),因此从列表a开始,当在末尾达到空值时,假装它是指向列表B开头的指针,从而创建循环列表的外观。然后,该算法将告诉您合并列表到底有多远(根据维基百科的描述,变量“mu”)
另外,“lambda”值告诉您列表B的长度,如果需要,您可以在算法过程中计算出列表A的长度(当您重定向空链接时)。如果您知道它们将合并: 假设您从以下内容开始:
A-->B-->C
|
V
1-->2-->3-->4-->5
1) 浏览第一个列表,将下一个指针设置为NULL
现在你有:
A B C
1-->2-->3 4 5
2) 现在浏览第二个列表,直到看到一个空值,这是您的合并点
如果您不能确定它们是否合并,您可以使用sentinel值作为指针值,但这并没有那么优雅。如果
- “不允许修改”的意思是“您可以更改,但最终应该恢复”,以及
- 我们可以迭代列表两次
a+c
,第二个列表的长度为b+c
,其中c
是它们共同的“尾巴”(在合并点之后)的长度。让我们将它们表示为:
x = a+c
y = b+c
由于我们不知道长度,我们将计算x
和y
,无需额外迭代;你会明白的
然后,我们迭代每个列表,并在迭代时反转它们!如果两个迭代器同时到达合并点,那么我们仅通过比较就可以找到它。否则,一个指针将在另一个指针之前到达合并点
之后,当另一个迭代器到达合并点时,它将不会继续到公共尾部。相反,它将返回到列表的前一个开始处,该列表以前已到达合并点!因此,在它到达更改列表的末尾(即另一个列表的前一个开头)之前,他将进行a+b+1
iterations合计。让我们称之为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
这解决了问题。这里有一个解决方案,计算速度快(每个列表迭代一次),但占用大量内存:
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
我在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);
注意:以上标志不会影响实际节点地址,只会影响保存的节点指针值
一旦发现有人设置了标志位,那么第一个找到的节点应该是合并点。
完成后,通过清除已设置的标志位来恢复节点地址。而重要的一点是,在迭代(例如node=node->next)执行clean时,应该小心。记住你已经设置了标志位,所以这样做
real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;
由于此方案将恢复修改后的节点地址,因此可将其视为“无修改”。Pavel的回答需要修改
//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;
}
struct node {
int data;
int flag; //initially set the flag to zero for all nodes
struct node *next;
};
if (node->next->field==1)//possibly longer list will have this opportunity
//this will be your required node.
//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;
}
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;
}
}
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;
}
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;
}
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;
}