本文的源代码和测试代码:https://github.com/clearoff/learngit/blob/master/ComplexLinklist.c
1、复杂链表的定义:
一般的,我们把只有一个数据于和一个指针的节点所组成的链表叫做单链表,由单链表我们引出了复杂链表的定义,如下图:
【复杂链表在C语言中的定义】:
typedef struct ComplexLinkNode
{
Datatype data;
struct ComplexLinkNode *p_Next; //指向下一个节点的指针
struct ComplexLinkNode *p_sibling; //指向任意一个节点或者空
}ComplexLinkNode;
由图可知,复杂链表不光含有单链表中的指向下一个元素的指针,还含有一个指针可以指向任意一个节点或者NULL!
下来我们就来分析一下如何对这个复杂链表进行复制:
方法1:首先我们可以拷贝复杂链表中的单链表部分,并且让拷贝出来的链表所有节点的p_Sibling都指向NULL,
随后我们就要明确所拷贝链表中每一个p_Sibling指针的指向,我们可以先遍历源链表,用两个变量分别记录从头节点到p_Sibling指针不为NULL的节点和p_Sibling所指向节点的步数,然后从所拷贝的链表中找到相应的节点,并进行连接。
【相应C语言代码】:
ComplexLinkNode* Clone_Nodes(ComplexLinkNode* list) //时间复杂度为O(n*n)的方法
{
assert(list);
ComplexLinkNode* list2 = NULL;
ComplexLinkNode* cur1 = list;
ComplexLinkNode* tmp = NULL;
int count1 = 0; //记录原链表找到指针的步数
int count2 = 0; //记录找到指针所指向节点的步数
while (cur1) //先拷贝复杂链表中的单链表部分
{
ComplexLinkNode* New = NULL;
New = (ComplexLinkNode*)malloc(sizeof(ComplexLinkNode)* 1); //开辟一个新节点
if (New == NULL)
{
printf("Out of memory!\n");
exit(EXIT_FAILURE);
}
New->data = cur1->data;
New->p_Next = NULL;
New->p_sibling = NULL;
if (list2 == NULL)
{
list2 = New;
tmp = list2;
}
else
{
tmp->p_Next = New;
tmp = New;
}
cur1 = cur1->p_Next;
}
cur1 = list;
while (cur1)
{
ComplexLinkNode *pos1 = NULL;
ComplexLinkNode *pos2 = NULL;
count1 = 0;
count2 = 0;
if (cur1->p_sibling != NULL)
{
ComplexLinkNode *pcur1 = list;
ComplexLinkNode *pcur2 = list;
pos1 = cur1;
pos2 = cur1->p_sibling;
while (pcur1 != pos1)
{
count1++;
pcur1 = pcur1->p_Next;
}
while (pcur2 != pos2)
{
count2++;
pcur2 = pcur2->p_Next;
}
pcur1 = list2;
pcur2 = list2;
while ((pcur1 != NULL)&&count1)
{
count1--;
pcur1 = pcur1->p_Next;
}
while ((pcur2 != NULL) && count2)
{
count2--;
pcur2 = pcur2->p_Next;
}
pcur1->p_sibling = pcur2;
}
cur1 = cur1->p_Next;
}
printf("复杂链表复制成功!\n");
return list2;
}
由于这种算法需要两次遍历原链表,所以时间复杂度很高,为了减少时间复杂度,我们来参考一下第二种方法:
方法二:在克隆复杂链表中的单链表时,我们不重新建立头节点,而是直接把N节点的拷贝节点链在N节点的后面,
如下图:
【该步骤的C语言代码】:
ComplexLinkNode* CloneNodes(ComplexLinkNode *list) //复制复杂链表的pNext部分!
{
assert(list);
ComplexLinkNode *pNode = list;
while (pNode)
{
ComplexLinkNode *pClone = (ComplexLinkNode*)malloc(sizeof(ComplexLinkNode)* 1);
if (pClone == NULL)
{
printf("Out of memory!\n");
exit(EXIT_FAILURE);
}
pClone->data = pNode->data;
pClone->p_Next = pNode->p_Next;
pClone->p_sibling = NULL;
pNode->p_Next = pClone;
pNode = pClone->p_Next;
}
return list;
}
随后,我们在明确经过上面一个人步骤后链表中每一个节点的指向,找到了N节点p_Sibling指针不为NULL的节点后,我们只需要把N节点的下一个节点指向N节点p_Sibling所指向的节点的下一个节点就可以了。
【该步骤的C语言代码】:
ComplexLinkNode* ConnectSiblingNodes(ComplexLinkNode* head) //复制出来结点的p_sibling指针
{
assert(head);
ComplexLinkNode* pNode = head;
while (pNode)
{
ComplexLinkNode* pClone = pNode->p_Next;
if (pNode->p_sibling != NULL)
{
pClone->p_sibling = pNode->p_sibling->p_Next;
}
pNode = pClone->p_Next;
}
return head;
}
最后一步:切断原链表和拷贝出来的链表
【C语言代码】:
ComplexLinkNode* CutList(ComplexLinkNode* head)
{
assert(head);
ComplexLinkNode* head2 = NULL;
ComplexLinkNode* pNode = head;
ComplexLinkNode* pClone = NULL;
if (pNode) //将克隆出来的节点连接头节点
{
head2 = pNode->p_Next;
pClone = pNode->p_Next;
pNode->p_Next = pClone->p_Next;
pNode = pNode->p_Next;
}
while (pNode)
{
pClone->p_Next = pNode->p_Next;
pClone = pNode->p_Next;
pNode->p_Next = pClone->p_Next;
pNode = pNode->p_Next;
}
return head2;
}
如果本文有问题,希望大家能提些意见!!!